Android 微信6.1 tab欄圖標(biāo)和字體顏色漸變的實(shí)現(xiàn)
相信大家都見(jiàn)到了微信圖標(biāo)顏色漸變的過(guò)程,是不是感覺(jué)很牛?不得不說(shuō)微信團(tuán)隊(duì)確實(shí)是很厲害的團(tuán)隊(duì),不管是從設(shè)計(jì)還是開(kāi)發(fā)人員。
今天我?guī)Т蠹襾?lái)看看,微信 tab 欄圖標(biāo)和字體顏色漸變的過(guò)程。先上圖吧!今天學(xué)了一招制作 gif 動(dòng)態(tài)圖的快捷方法。剛好用的上,以前一直想寫(xiě)點(diǎn)什么東西,
苦于一直不知道怎么生成動(dòng)態(tài)圖,現(xiàn)在終于學(xué)會(huì)了,哈哈,讓我偷偷的樂(lè)一會(huì)。額,還是上圖吧。。。

好了,效果圖也看到了,那么我也就不多啰嗦了,直接進(jìn)入主題,看代碼
[java] view plain copy
package moon.wechat.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
/**
* Created by moon.zhong on 2015/2/4.
*/
public class TabItem extends View {
/*字體大小*/
private int mTextSize ;
/*字體選中的顏色*/
private int mTextColorSelect ;
/*字體未選擇的時(shí)候的顏色*/
private int mTextColorNormal;
/*繪制未選中時(shí)字體的畫(huà)筆*/
private Paint mTextPaintNormal;
/*繪制已選中時(shí)字體的畫(huà)筆*/
private Paint mTextPaintSelect;
/*每個(gè) item 的寬和高,包括字體和圖標(biāo)一起*/
private int mViewHeight, mViewWidth;
/*字體的內(nèi)容*/
private String mTextValue ;
/*已選中時(shí)的圖標(biāo)*/
private Bitmap mIconNormal;
/*未選中時(shí)的圖標(biāo)*/
private Bitmap mIconSelect;
/*用于記錄字體大小*/
private Rect mBoundText;
/*已選中是圖標(biāo)的畫(huà)筆*/
private Paint mIconPaintSelect;
/*為選中時(shí)圖標(biāo)的畫(huà)筆*/
private Paint mIconPaintNormal;
public TabItem(Context context) {
this(context, null);
}
public TabItem(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TabItem(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
initText();
}
/*初始化一些東西*/
private void initView() {
mBoundText = new Rect();
}
/*初始化畫(huà)筆,并設(shè)置出是內(nèi)容*/
private void initText() {
mTextPaintNormal = new Paint();
mTextPaintNormal.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mTextSize, getResources().getDisplayMetrics()));
mTextPaintNormal.setColor(mTextColorNormal);
mTextPaintNormal.setAntiAlias(true);
mTextPaintNormal.setAlpha(0xff);
mTextPaintSelect = new Paint();
mTextPaintSelect.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mTextSize, getResources().getDisplayMetrics()));
mTextPaintSelect.setColor(mTextColorSelect);
mTextPaintSelect.setAntiAlias(true);
mTextPaintSelect.setAlpha(0);
mIconPaintSelect = new Paint(Paint.ANTI_ALIAS_FLAG) ;
mIconPaintSelect.setAlpha(0);
mIconPaintNormal = new Paint(Paint.ANTI_ALIAS_FLAG) ;
mIconPaintNormal.setAlpha(0xff);
}
/*測(cè)量字體的大小*/
private void measureText() {
mTextPaintNormal.getTextBounds(mTextValue, 0, mTextValue.length(), mBoundText);
}
/*測(cè)量字體和圖標(biāo)的大小,并設(shè)置自身的寬和高*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width = 0, height = 0;
measureText();
int contentWidth = Math.max(mBoundText.width(), mIconNormal.getWidth());
int desiredWidth = getPaddingLeft() + getPaddingRight() + contentWidth;
switch (widthMode) {
case MeasureSpec.AT_MOST:
width = Math.min(widthSize, desiredWidth);
break;
case MeasureSpec.EXACTLY:
width = widthSize;
break;
case MeasureSpec.UNSPECIFIED:
width = desiredWidth;
break;
}
int contentHeight = mBoundText.height() + mIconNormal.getHeight();
int desiredHeight = getPaddingTop() + getPaddingBottom() + contentHeight;
switch (heightMode) {
case MeasureSpec.AT_MOST:
height = Math.min(heightSize, desiredHeight);
break;
case MeasureSpec.EXACTLY:
height = heightSize;
break;
case MeasureSpec.UNSPECIFIED:
height = contentHeight;
break;
}
setMeasuredDimension(width, height);
mViewWidth = getMeasuredWidth() ;
mViewHeight = getMeasuredHeight() ;
}
@Override
protected void onDraw(Canvas canvas) {
drawBitmap(canvas) ;
drawText(canvas) ;
}
/*話圖標(biāo),先畫(huà)為選中的圖標(biāo),在畫(huà)已選中的圖標(biāo)*/
private void drawBitmap(Canvas canvas) {
int left = (mViewWidth - mIconNormal.getWidth())/2 ;
int top = (mViewHeight - mIconNormal.getHeight() - mBoundText.height()) /2 ;
canvas.drawBitmap(mIconNormal, left, top ,mIconPaintNormal);
canvas.drawBitmap(mIconSelect, left, top , mIconPaintSelect);
}
/*畫(huà)字體*/
private void drawText(Canvas canvas) {
float x = (mViewWidth - mBoundText.width())/2.0f ;
float y = (mViewHeight + mIconNormal.getHeight() + mBoundText.height()) /2.0F ;
canvas.drawText(mTextValue,x,y, mTextPaintNormal);
canvas.drawText(mTextValue,x,y, mTextPaintSelect);
}
public void setTextSize(int textSize) {
this.mTextSize = textSize;
mTextPaintNormal.setTextSize(textSize);
mTextPaintSelect.setTextSize(textSize);
}
public void setTextColorSelect(int mTextColorSelect) {
this.mTextColorSelect = mTextColorSelect;
mTextPaintSelect.setColor(mTextColorSelect);
mTextPaintSelect.setAlpha(0);
}
public void setTextColorNormal(int mTextColorNormal) {
this.mTextColorNormal = mTextColorNormal;
mTextPaintNormal.setColor(mTextColorNormal);
mTextPaintNormal.setAlpha(0xff);
}
public void setTextValue(String TextValue) {
this.mTextValue = TextValue;
}
public void setIconText(int[] iconSelId,String TextValue) {
this.mIconSelect = BitmapFactory.decodeResource(getResources(), iconSelId[0]);
this.mIconNormal = BitmapFactory.decodeResource(getResources(), iconSelId[1]);
this.mTextValue = TextValue;
}
/*通過(guò) alpha 來(lái)設(shè)置 每個(gè)畫(huà)筆的透明度,從而實(shí)現(xiàn)現(xiàn)實(shí)的效果*/
public void setTabAlpha(float alpha){
int paintAlpha = (int)(alpha*255) ;
mIconPaintSelect.setAlpha(paintAlpha);
mIconPaintNormal.setAlpha(255-paintAlpha);
mTextPaintSelect.setAlpha(paintAlpha);
mTextPaintNormal.setAlpha(255-paintAlpha);
invalidate();
}
}
分析: 以上代碼,功能實(shí)現(xiàn) tab 的每個(gè) item 的內(nèi)容,在微信的項(xiàng)目中也就是,一個(gè)圖標(biāo)加一個(gè)字體,
關(guān)鍵代碼就在public void setTabAlpha(float alpha) 這個(gè)方法,此方法是 viewPager 切換 item 時(shí),不斷改變偏移量來(lái)調(diào)用,從而不斷改變
每個(gè)畫(huà)筆的透明度,實(shí)現(xiàn)圖標(biāo)和顏色的漸變;是不是很簡(jiǎn)單?到這里其實(shí)我們顏色漸變的代碼就已經(jīng)實(shí)現(xiàn)了。接下來(lái)的內(nèi)容可以忽略
這樣我們只需要在 MainActivity 的 xml 中定義一個(gè)線性布局,然后放如四個(gè)我們自定義的 View 進(jìn)去,就可以了。但是這樣你就滿足了嗎?
我先來(lái)給你們看看我的MainActivity的代碼;
[java] view plain copy
package moon.wechat;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import java.util.HashMap;
import java.util.Map;
import moon.wechat.view.TabView;
public class MainActivity extends ActionBarActivity {
private String[] mTitle = {"微信", "通訊錄", "發(fā)現(xiàn)", "我"};
private int[] mIconSelect = {R.drawable.al_, R.drawable.al8, R.drawable.alb, R.drawable.ald};
private int[] mIconNormal = {R.drawable.ala, R.drawable.al9, R.drawable.alc, R.drawable.ale};
private ViewPager mViewPager ;
private TabView mTabView ;
private Map<Integer,Fragment> mFragmentMap ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFragmentMap = new HashMap<>() ;
mViewPager = (ViewPager)findViewById(R.id.id_view_pager) ;
mViewPager.setOffscreenPageLimit(4);
mViewPager.setAdapter(new PageAdapter(getSupportFragmentManager()));
mTabView = (TabView)findViewById(R.id.id_tab) ;
mTabView.setViewPager(mViewPager);
}
private Fragment getFragment(int position){
Fragment fragment = mFragmentMap.get(position) ;
if(fragment == null){
switch (position){
case 0:
fragment = new WeChatFragment() ;
break ;
case 1:
fragment = new WeContactFragment();
break ;
case 2:
fragment = new WeDiscoverFragment();
break;
case 3:
fragment = new GameFragment() ;
// fragment = new WeMineFragment();
break;
}
mFragmentMap.put(position,fragment) ;
}
return fragment ;
}
class PageAdapter extends FragmentPagerAdapter implements TabView.OnItemIconTextSelectListener{
public PageAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return getFragment(position);
}
@Override
public int[] onIconSelect(int position) {
int icon[] = new int[2] ;
icon[0] = mIconSelect[position] ;
icon[1] = mIconNormal[position] ;
return icon;
}
@Override
public String onTextSelect(int position) {
return mTitle[position];
}
@Override
public int getCount() {
return mTitle.length;
}
}
}
是不是很簡(jiǎn)單,而 xml 更簡(jiǎn)單
[html] view plain copy
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:zgy="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="@+id/id_view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
>
</android.support.v4.view.ViewPager>
<moon.wechat.view.TabView
android:id="@+id/id_tab"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#eeeeee"
zgy:text_size="12sp"
zgy:text_normal_color="#ff777777"
zgy:text_select_color="#ff45c01a"
zgy:item_padding="7dp">
</moon.wechat.view.TabView>
</LinearLayout>
可以看到?jīng)]有定義我們剛剛自定義的 TabItem,而是使用的 TabView,那 TabView 到底是啥東西?相信大家都想到了,TabView 其實(shí)就是我們自定義的線性布局,
[java] view plain copy
package moon.wechat.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import java.util.ArrayList;
import java.util.List;
import moon.wechat.R;
/**
* Created by moon.zhong on 2015/2/4.
*/
public class TabView extends LinearLayout implements View.OnClickListener {
private ViewPager mViewPager;
private ViewPager.OnPageChangeListener mOnPageChangeListener;
private PagerAdapter mPagerAdapter;
private int mChildSize;
private List<TabItem> mTabItems;
private OnItemIconTextSelectListener mListener;
private int mTextSize = 12;
private int mTextColorSelect = 0xff45c01a;
private int mTextColorNormal = 0xff777777;
private int mPadding = 10;
public TabView(Context context) {
this(context, null);
}
public TabView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TabView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = getResources().obtainAttributes(attrs, R.styleable.TabView);
int N = typedArray.getIndexCount();
for (int i = 0; i < N; i++) {
switch (typedArray.getIndex(i)) {
case R.styleable.TabView_text_size:
mTextSize = (int) typedArray.getDimension(i, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
mTextSize, getResources().getDisplayMetrics()));
break;
case R.styleable.TabView_text_normal_color:
mTextColorNormal = typedArray.getColor(i, mTextColorNormal);
break;
case R.styleable.TabView_text_select_color:
mTextColorSelect = typedArray.getColor(i, mTextColorSelect);
break;
case R.styleable.TabView_item_padding:
mPadding = (int) typedArray.getDimension(i, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
mPadding, getResources().getDisplayMetrics()));
break;
}
}
typedArray.recycle();
mTabItems = new ArrayList<>();
}
public void setViewPager(final ViewPager mViewPager) {
if (mViewPager == null) {
return;
}
this.mViewPager = mViewPager;
this.mPagerAdapter = mViewPager.getAdapter();
if (this.mPagerAdapter == null) {
throw new RuntimeException("親,在您設(shè)置TabView的ViewPager時(shí),請(qǐng)先設(shè)置ViewPager的PagerAdapter");
}
this.mChildSize = this.mPagerAdapter.getCount();
this.mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// Log.v("zgy","=============position="+position+",====positionOffset="+positionOffset) ;
View leftView;
View rightView;
if (positionOffset > 0) {
leftView = mViewPager.getChildAt(position);
rightView = mViewPager.getChildAt(position + 1);
leftView.setAlpha(1 - positionOffset);
rightView.setAlpha(positionOffset);
mTabItems.get(position).setTabAlpha(1 - positionOffset);
mTabItems.get(position + 1).setTabAlpha(positionOffset);
} else {
mViewPager.getChildAt(position).setAlpha(1);
mTabItems.get(position).setTabAlpha(1 - positionOffset);
}
if (mOnPageChangeListener != null) {
mOnPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
}
@Override
public void onPageSelected(int position) {
if (mOnPageChangeListener != null) {
mOnPageChangeListener.onPageSelected(position);
}
}
@Override
public void onPageScrollStateChanged(int state) {
if (mOnPageChangeListener != null) {
mOnPageChangeListener.onPageScrollStateChanged(state);
}
}
});
if (mPagerAdapter instanceof OnItemIconTextSelectListener) {
mListener = (OnItemIconTextSelectListener) mPagerAdapter;
}else {
throw new RuntimeException("請(qǐng)讓你的pageAdapter實(shí)現(xiàn)OnItemIconTextSelectListener接口");
}
initItem();
}
public void setOnPageChangeListener(ViewPager.OnPageChangeListener mOnPageChangeListener) {
this.mOnPageChangeListener = mOnPageChangeListener;
}
private void initItem() {
for (int i = 0; i < mChildSize; i++) {
TabItem tabItem = new TabItem(getContext());
LayoutParams params = new LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1);
tabItem.setPadding(mPadding, mPadding, mPadding, mPadding);
tabItem.setIconText(mListener.onIconSelect(i), mListener.onTextSelect(i));
tabItem.setTextSize(mTextSize);
tabItem.setTextColorNormal(mTextColorNormal);
tabItem.setTextColorSelect(mTextColorSelect);
tabItem.setLayoutParams(params);
tabItem.setTag(i);
tabItem.setOnClickListener(this);
mTabItems.add(tabItem);
addView(tabItem);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
public void onClick(View v) {
int position = (Integer) v.getTag();
if (mViewPager.getCurrentItem() == position) {
return;
}
for (TabItem tabItem : mTabItems) {
tabItem.setTabAlpha(0);
}
mTabItems.get(position).setTabAlpha(1);
mViewPager.setCurrentItem(position, false);
}
public interface OnItemIconTextSelectListener {
int[] onIconSelect(int position);
String onTextSelect(int position);
}
}
注釋有點(diǎn)少,額,不是少,是壓根就沒(méi)有,其實(shí),這個(gè)類(lèi)的代碼不需要注釋,我相信大家都能看懂,我就講下他的作用吧,
- 添加 item
- 監(jiān)聽(tīng) ViewPager 的滾動(dòng)事件,從而設(shè)置相應(yīng) item 之間的顏色漸變,
- 設(shè)置相應(yīng) ViewPage 的透明度
- 為 TabItem 設(shè)置監(jiān)聽(tīng)事件,
其實(shí)上面很多功能本來(lái)是在 MainActivity 中實(shí)現(xiàn)的,為了減少 Activity 內(nèi)部的代碼量,抽取出來(lái),到達(dá)低耦合,高內(nèi)聚的效果。
Ok,以上就是 微信6.1 tab 欄顏色漸變效果的實(shí)現(xiàn)全過(guò)程。
以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持腳本之家!
相關(guān)文章
Android編程Widget創(chuàng)建與使用方法簡(jiǎn)明教程
這篇文章主要介紹了Android編程Widget創(chuàng)建與使用方法,結(jié)合實(shí)例形式分析了Widget的功能、使用方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下2016-10-10
三款A(yù)ndroid炫酷Loading動(dòng)畫(huà)組件推薦
這篇文章主要介紹了三款A(yù)ndroid炫酷Loading動(dòng)畫(huà)組件推薦,本文介紹了CircleProgress、android-shapeLoadingView、WaitingDots等三款Loading組件,并給出了運(yùn)行效果圖,需要的朋友可以參考下2015-05-05
基于TransactionTooLargeException異常分析
下面小編就為大家分享一篇基于TransactionTooLargeException異常分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
Android升級(jí)gradle 后引入aar包報(bào)錯(cuò)解決
這篇文章主要為大家介紹了Android升級(jí)gradle 后引入aar包報(bào)錯(cuò)解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
Android開(kāi)發(fā)Jetpack組件Lifecycle使用篇
這一篇文章來(lái)介紹Android?Jetpack架構(gòu)組件的Lifecycle;?Lifecycle用于幫助開(kāi)發(fā)者管理Activity和Fragment?的生命周期,?由于Lifecycle是LiveData和ViewModel的基礎(chǔ);所以需要先學(xué)習(xí)它2022-08-08
Android自定義帶動(dòng)畫(huà)效果的圓形ProgressBar
這篇文章主要為大家詳細(xì)介紹了Android自定義帶動(dòng)畫(huà)效果的圓形ProgressBar,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05
Android使用Intent傳大數(shù)據(jù)簡(jiǎn)單實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了Android使用Intent傳大數(shù)據(jù)簡(jiǎn)單實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
詳解Android中Handler的實(shí)現(xiàn)原理
這篇文章主要為大家詳細(xì)介紹了Android中Handler的實(shí)現(xiàn)原理,本文深入分析 Android 的消息處理機(jī)制,了解 Handler 的工作原理,感興趣的小伙伴們可以參考一下2016-04-04

