Tab栏图标和字体颜色渐变的实现

相信大家都见到了微信图标颜色渐变的过程,是不是感觉很牛逼?不得不说微信团队确实是很厉害的团队,不管是从设计还是开发人员。

今天我带大家来看看,微信 tab 栏图标和字体颜色渐变的过程。先上图吧!今天学了一招制作 gif 动态图的快捷方法。刚好用的上,以前一直想写点什么东西,

苦于一直不知道怎么生成动态图,现在终于学会了,哈哈,让我偷偷的乐一会。额,还是上图吧。。。


好了,效果图也看到了,那么我也就不多啰嗦了,直接进入主题,看代码

[java]  view plain copy
  1. package moon.wechat.view;  
  2.   
  3. import android.content.Context;  
  4. import android.graphics.Bitmap;  
  5. import android.graphics.BitmapFactory;  
  6. import android.graphics.Canvas;  
  7. import android.graphics.Paint;  
  8. import android.graphics.Rect;  
  9. import android.util.AttributeSet;  
  10. import android.util.TypedValue;  
  11. import android.view.View;  
  12.   
  13. /** 
  14.  * Created by moon.zhong on 2015/2/4. 
  15.  */  
  16. public class TabItem extends View {  
  17.   
  18.     /*字体大小*/  
  19.       
  20.     private int mTextSize ;  
  21.       
  22.     /*字体选中的颜色*/  
  23.     private int mTextColorSelect ;  
  24.       
  25.     /*字体未选择的时候的颜色*/  
  26.     private int mTextColorNormal;  
  27.       
  28.     /*绘制未选中时字体的画笔*/  
  29.     private Paint mTextPaintNormal;  
  30.       
  31.     /*绘制已选中时字体的画笔*/  
  32.     private Paint mTextPaintSelect;  
  33.       
  34.     /*每个 item 的宽和高,包括字体和图标一起*/  
  35.     private int mViewHeight, mViewWidth;  
  36.       
  37.     /*字体的内容*/  
  38.     private String mTextValue ;  
  39.       
  40.     /*已选中时的图标*/  
  41.     private Bitmap mIconNormal;  
  42.       
  43.     /*未选中时的图标*/  
  44.     private Bitmap mIconSelect;  
  45.       
  46.     /*用于记录字体大小*/  
  47.     private Rect mBoundText;  
  48.       
  49.     /*已选中是图标的画笔*/  
  50.     private Paint mIconPaintSelect;  
  51.       
  52.     /*为选中时图标的画笔*/  
  53.     private Paint mIconPaintNormal;  
  54.   
  55.     public TabItem(Context context) {  
  56.         this(context, null);  
  57.     }  
  58.   
  59.     public TabItem(Context context, AttributeSet attrs) {  
  60.         this(context, attrs, 0);  
  61.     }  
  62.   
  63.     public TabItem(Context context, AttributeSet attrs, int defStyleAttr) {  
  64.         super(context, attrs, defStyleAttr);  
  65.         initView();  
  66.         initText();  
  67.     }  
  68.   
  69.     /*初始化一些东西*/  
  70.     private void initView() {  
  71.         mBoundText = new Rect();  
  72.     }  
  73.   
  74.     /*初始化画笔,并设置出是内容*/  
  75.     private void initText() {  
  76.         mTextPaintNormal = new Paint();  
  77.         mTextPaintNormal.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mTextSize, getResources().getDisplayMetrics()));  
  78.         mTextPaintNormal.setColor(mTextColorNormal);  
  79.         mTextPaintNormal.setAntiAlias(true);  
  80.         mTextPaintNormal.setAlpha(0xff);  
  81.   
  82.         mTextPaintSelect = new Paint();  
  83.         mTextPaintSelect.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mTextSize, getResources().getDisplayMetrics()));  
  84.         mTextPaintSelect.setColor(mTextColorSelect);  
  85.         mTextPaintSelect.setAntiAlias(true);  
  86.         mTextPaintSelect.setAlpha(0);  
  87.   
  88.         mIconPaintSelect = new Paint(Paint.ANTI_ALIAS_FLAG) ;  
  89.         mIconPaintSelect.setAlpha(0);  
  90.   
  91.         mIconPaintNormal = new Paint(Paint.ANTI_ALIAS_FLAG) ;  
  92.         mIconPaintNormal.setAlpha(0xff);  
  93.     }  
  94.   
  95.     /*测量字体的大小*/  
  96.     private void measureText() {  
  97.         mTextPaintNormal.getTextBounds(mTextValue, 0, mTextValue.length(), mBoundText);  
  98.     }  
  99.   
  100.       
  101.     /*测量字体和图标的大小,并设置自身的宽和高*/  
  102.     @Override  
  103.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  104.         int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  105.         int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
  106.         int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
  107.         int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
  108.   
  109.         int width = 0, height = 0;  
  110.   
  111.         measureText();  
  112.         int contentWidth = Math.max(mBoundText.width(), mIconNormal.getWidth());  
  113.         int desiredWidth = getPaddingLeft() + getPaddingRight() + contentWidth;  
  114.         switch (widthMode) {  
  115.             case MeasureSpec.AT_MOST:  
  116.                 width = Math.min(widthSize, desiredWidth);  
  117.                 break;  
  118.             case MeasureSpec.EXACTLY:  
  119.                 width = widthSize;  
  120.                 break;  
  121.             case MeasureSpec.UNSPECIFIED:  
  122.                 width = desiredWidth;  
  123.                 break;  
  124.         }  
  125.         int contentHeight = mBoundText.height() + mIconNormal.getHeight();  
  126.         int desiredHeight = getPaddingTop() + getPaddingBottom() + contentHeight;  
  127.         switch (heightMode) {  
  128.             case MeasureSpec.AT_MOST:  
  129.                 height = Math.min(heightSize, desiredHeight);  
  130.                 break;  
  131.             case MeasureSpec.EXACTLY:  
  132.                 height = heightSize;  
  133.                 break;  
  134.             case MeasureSpec.UNSPECIFIED:  
  135.                 height = contentHeight;  
  136.                 break;  
  137.         }  
  138.         setMeasuredDimension(width, height);  
  139.         mViewWidth = getMeasuredWidth() ;  
  140.         mViewHeight = getMeasuredHeight() ;  
  141.     }  
  142.   
  143.     @Override  
  144.     protected void onDraw(Canvas canvas) {  
  145.         drawBitmap(canvas) ;  
  146.         drawText(canvas) ;  
  147.     }  
  148.   
  149.     /*话图标,先画为选中的图标,在画已选中的图标*/  
  150.     private void drawBitmap(Canvas canvas) {  
  151.         int left = (mViewWidth - mIconNormal.getWidth())/2 ;  
  152.         int top = (mViewHeight - mIconNormal.getHeight() - mBoundText.height()) /2 ;  
  153.         canvas.drawBitmap(mIconNormal, left, top ,mIconPaintNormal);  
  154.         canvas.drawBitmap(mIconSelect, left, top , mIconPaintSelect);  
  155.     }  
  156.       
  157.     /*画字体*/  
  158.     private void drawText(Canvas canvas) {  
  159.         float x = (mViewWidth - mBoundText.width())/2.0f ;  
  160.         float y = (mViewHeight + mIconNormal.getHeight() + mBoundText.height()) /2.0F ;  
  161.         canvas.drawText(mTextValue,x,y, mTextPaintNormal);  
  162.         canvas.drawText(mTextValue,x,y, mTextPaintSelect);  
  163.     }  
  164.   
  165.     public void setTextSize(int textSize) {  
  166.         this.mTextSize = textSize;  
  167.         mTextPaintNormal.setTextSize(textSize);  
  168.         mTextPaintSelect.setTextSize(textSize);  
  169.     }  
  170.   
  171.     public void setTextColorSelect(int mTextColorSelect) {  
  172.         this.mTextColorSelect = mTextColorSelect;  
  173.         mTextPaintSelect.setColor(mTextColorSelect);  
  174.         mTextPaintSelect.setAlpha(0);  
  175.     }  
  176.   
  177.     public void setTextColorNormal(int mTextColorNormal) {  
  178.         this.mTextColorNormal = mTextColorNormal;  
  179.         mTextPaintNormal.setColor(mTextColorNormal);  
  180.         mTextPaintNormal.setAlpha(0xff);  
  181.     }  
  182.   
  183.     public void setTextValue(String TextValue) {  
  184.         this.mTextValue = TextValue;  
  185.     }  
  186.     public void setIconText(int[] iconSelId,String TextValue) {  
  187.         this.mIconSelect = BitmapFactory.decodeResource(getResources(), iconSelId[0]);  
  188.         this.mIconNormal = BitmapFactory.decodeResource(getResources(), iconSelId[1]);  
  189.         this.mTextValue = TextValue;  
  190.     }  
  191.   
  192.       
  193.     /*通过 alpha 来设置 每个画笔的透明度,从而实现现实的效果*/  
  194.     public void setTabAlpha(float alpha){  
  195.         int paintAlpha = (int)(alpha*255) ;  
  196.         mIconPaintSelect.setAlpha(paintAlpha);  
  197.         mIconPaintNormal.setAlpha(255-paintAlpha);  
  198.         mTextPaintSelect.setAlpha(paintAlpha);  
  199.         mTextPaintNormal.setAlpha(255-paintAlpha);  
  200.         invalidate();  
  201.     }  
  202. }  

分析: 以上代码,功能实现 tab 的每个 item 的内容,在微信的项目中也就是,一个图标加一个字体,

关键代码就在public void setTabAlpha(float alpha) 这个方法,此方法是 viewPager 切换 item 时,不断改变偏移量来调用,从而不断改变

每个画笔的透明度,实现图标和颜色的渐变;是不是很简单?到这里其实我们颜色渐变的代码就已经实现了。接下来的内容可以忽略


这样我们只需要在 MainActivity 的 xml 中定义一个线性布局,然后放如四个我们自定义的 View 进去,就可以了。但是这样你就满足了吗?

我先来给你们看看我的MainActivity 的 代码;

[java]  view plain copy
  1. package moon.wechat;  
  2.   
  3. import android.support.v4.app.Fragment;  
  4. import android.support.v4.app.FragmentManager;  
  5. import android.support.v4.app.FragmentPagerAdapter;  
  6. import android.support.v4.view.ViewPager;  
  7. import android.support.v7.app.ActionBarActivity;  
  8. import android.os.Bundle;  
  9.   
  10. import java.util.HashMap;  
  11. import java.util.Map;  
  12.   
  13. import moon.wechat.view.TabView;  
  14.   
  15.   
  16. public class MainActivity extends ActionBarActivity {  
  17.     private String[] mTitle = {"微信""通讯录""发现""我"};  
  18.     private int[] mIconSelect = {R.drawable.al_, R.drawable.al8, R.drawable.alb, R.drawable.ald};  
  19.     private int[] mIconNormal = {R.drawable.ala, R.drawable.al9, R.drawable.alc, R.drawable.ale};  
  20.     private ViewPager mViewPager ;  
  21.     private TabView mTabView ;  
  22.     private Map<Integer,Fragment> mFragmentMap ;  
  23.     @Override  
  24.     protected void onCreate(Bundle savedInstanceState) {  
  25.         super.onCreate(savedInstanceState);  
  26.         setContentView(R.layout.activity_main);  
  27.         mFragmentMap = new HashMap<>() ;  
  28.         mViewPager = (ViewPager)findViewById(R.id.id_view_pager) ;  
  29.         mViewPager.setOffscreenPageLimit(4);  
  30.         mViewPager.setAdapter(new PageAdapter(getSupportFragmentManager()));  
  31.         mTabView = (TabView)findViewById(R.id.id_tab) ;  
  32.         mTabView.setViewPager(mViewPager);  
  33.     }  
  34.   
  35.     private Fragment getFragment(int position){  
  36.         Fragment fragment = mFragmentMap.get(position) ;  
  37.         if(fragment == null){  
  38.             switch (position){  
  39.                 case 0:  
  40.                     fragment = new WeChatFragment() ;  
  41.                     break ;  
  42.                 case 1:  
  43.                     fragment = new WeContactFragment();  
  44.                     break ;  
  45.                 case 2:  
  46.                     fragment = new WeDiscoverFragment();  
  47.                     break;  
  48.                 case 3:  
  49.                     fragment = new GameFragment() ;  
  50. //                    fragment = new WeMineFragment();  
  51.                     break;  
  52.             }  
  53.             mFragmentMap.put(position,fragment) ;  
  54.         }  
  55.         return fragment ;  
  56.     }  
  57.   
  58.     class PageAdapter extends FragmentPagerAdapter implements TabView.OnItemIconTextSelectListener{  
  59.   
  60.         public PageAdapter(FragmentManager fm) {  
  61.             super(fm);  
  62.         }  
  63.         @Override  
  64.         public Fragment getItem(int position) {  
  65.             return getFragment(position);  
  66.         }  
  67.         @Override  
  68.         public int[] onIconSelect(int position) {  
  69.             int icon[] = new int[2] ;  
  70.             icon[0] = mIconSelect[position] ;  
  71.             icon[1] = mIconNormal[position] ;  
  72.             return icon;  
  73.         }  
  74.         @Override  
  75.         public String onTextSelect(int position) {  
  76.             return mTitle[position];  
  77.         }  
  78.   
  79.         @Override  
  80.         public int getCount() {  
  81.             return mTitle.length;  
  82.         }  
  83.     }  
  84. }  

是不是很简单,而 xml 更简单


[html]  view plain copy
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:zgy="http://schemas.android.com/apk/res-auto"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:background="@android:color/white"  
  6.     android:orientation="vertical">  
  7.   
  8.     <android.support.v4.view.ViewPager  
  9.         android:id="@+id/id_view_pager"  
  10.         android:layout_width="match_parent"  
  11.         android:layout_height="wrap_content"  
  12.         android:layout_weight="1"  
  13.         >  
  14.         </android.support.v4.view.ViewPager>  
  15.   
  16.     <moon.wechat.view.TabView  
  17.         android:id="@+id/id_tab"  
  18.         android:layout_width="match_parent"  
  19.         android:layout_height="wrap_content"  
  20.         android:orientation="horizontal"  
  21.         android:background="#eeeeee"  
  22.         zgy:text_size="12sp"  
  23.         zgy:text_normal_color="#ff777777"  
  24.         zgy:text_select_color="#ff45c01a"  
  25.         zgy:item_padding="7dp">  
  26.      </moon.wechat.view.TabView>  
  27. </LinearLayout>  

可以看到没有定义我们刚刚自定义的 TabItem,而是使用的 TabView,那 TabView 到底是啥东西?相信大家都想到了,TabView 其实就是我们自定义的线性布局,

[java]  view plain copy
  1. package moon.wechat.view;  
  2.   
  3. import android.content.Context;  
  4. import android.content.res.TypedArray;  
  5. import android.support.v4.view.PagerAdapter;  
  6. import android.support.v4.view.ViewPager;  
  7. import android.util.AttributeSet;  
  8. import android.util.Log;  
  9. import android.util.TypedValue;  
  10. import android.view.View;  
  11. import android.view.ViewGroup;  
  12. import android.widget.LinearLayout;  
  13.   
  14. import java.util.ArrayList;  
  15. import java.util.List;  
  16.   
  17. import moon.wechat.R;  
  18.   
  19. /** 
  20.  * Created by moon.zhong on 2015/2/4. 
  21.  */  
  22. public class TabView extends LinearLayout implements View.OnClickListener {  
  23.   
  24.   
  25.     private ViewPager mViewPager;  
  26.     private ViewPager.OnPageChangeListener mOnPageChangeListener;  
  27.     private PagerAdapter mPagerAdapter;  
  28.     private int mChildSize;  
  29.     private List<TabItem> mTabItems;  
  30.     private OnItemIconTextSelectListener mListener;  
  31.   
  32.     private int mTextSize = 12;  
  33.     private int mTextColorSelect = 0xff45c01a;  
  34.     private int mTextColorNormal = 0xff777777;  
  35.     private int mPadding = 10;  
  36.   
  37.     public TabView(Context context) {  
  38.         this(context, null);  
  39.     }  
  40.   
  41.     public TabView(Context context, AttributeSet attrs) {  
  42.         this(context, attrs, 0);  
  43.     }  
  44.   
  45.     public TabView(Context context, AttributeSet attrs, int defStyleAttr) {  
  46.         super(context, attrs, defStyleAttr);  
  47.         TypedArray typedArray = getResources().obtainAttributes(attrs, R.styleable.TabView);  
  48.         int N = typedArray.getIndexCount();  
  49.         for (int i = 0; i < N; i++) {  
  50.             switch (typedArray.getIndex(i)) {  
  51.                 case R.styleable.TabView_text_size:  
  52.                     mTextSize = (int) typedArray.getDimension(i, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,  
  53.                             mTextSize, getResources().getDisplayMetrics()));  
  54.                     break;  
  55.                 case R.styleable.TabView_text_normal_color:  
  56.                     mTextColorNormal = typedArray.getColor(i, mTextColorNormal);  
  57.                     break;  
  58.                 case R.styleable.TabView_text_select_color:  
  59.                     mTextColorSelect = typedArray.getColor(i, mTextColorSelect);  
  60.                     break;  
  61.                 case R.styleable.TabView_item_padding:  
  62.                     mPadding = (int) typedArray.getDimension(i, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,  
  63.                             mPadding, getResources().getDisplayMetrics()));  
  64.                     break;  
  65.             }  
  66.         }  
  67.         typedArray.recycle();  
  68.         mTabItems = new ArrayList<>();  
  69.     }  
  70.   
  71.     public void setViewPager(final ViewPager mViewPager) {  
  72.         if (mViewPager == null) {  
  73.             return;  
  74.         }  
  75.         this.mViewPager = mViewPager;  
  76.         this.mPagerAdapter = mViewPager.getAdapter();  
  77.         if (this.mPagerAdapter == null) {  
  78.             throw new RuntimeException("亲,在您设置TabView的ViewPager时,请先设置ViewPager的PagerAdapter");  
  79.         }  
  80.         this.mChildSize = this.mPagerAdapter.getCount();  
  81.         this.mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {  
  82.             @Override  
  83.             public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {  
  84.   
  85. //                Log.v("zgy","=============position="+position+",====positionOffset="+positionOffset) ;  
  86.                 View leftView;  
  87.                 View rightView;  
  88.   
  89.                 if (positionOffset > 0) {  
  90.                     leftView = mViewPager.getChildAt(position);  
  91.                     rightView = mViewPager.getChildAt(position + 1);  
  92.                     leftView.setAlpha(1 - positionOffset);  
  93.                     rightView.setAlpha(positionOffset);  
  94.                     mTabItems.get(position).setTabAlpha(1 - positionOffset);  
  95.                     mTabItems.get(position + 1).setTabAlpha(positionOffset);  
  96.                 } else {  
  97.                     mViewPager.getChildAt(position).setAlpha(1);  
  98.                     mTabItems.get(position).setTabAlpha(1 - positionOffset);  
  99.                 }  
  100.                 if (mOnPageChangeListener != null) {  
  101.                     mOnPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);  
  102.                 }  
  103.             }  
  104.   
  105.             @Override  
  106.             public void onPageSelected(int position) {  
  107.                 if (mOnPageChangeListener != null) {  
  108.                     mOnPageChangeListener.onPageSelected(position);  
  109.                 }  
  110.             }  
  111.   
  112.             @Override  
  113.             public void onPageScrollStateChanged(int state) {  
  114.                 if (mOnPageChangeListener != null) {  
  115.                     mOnPageChangeListener.onPageScrollStateChanged(state);  
  116.                 }  
  117.             }  
  118.         });  
  119.         if (mPagerAdapter instanceof OnItemIconTextSelectListener) {  
  120.             mListener = (OnItemIconTextSelectListener) mPagerAdapter;  
  121.         }else {  
  122.             throw new RuntimeException("请让你的pageAdapter实现OnItemIconTextSelectListener接口");  
  123.         }  
  124.         initItem();  
  125.     }  
  126.   
  127.     public void setOnPageChangeListener(ViewPager.OnPageChangeListener mOnPageChangeListener) {  
  128.         this.mOnPageChangeListener = mOnPageChangeListener;  
  129.     }  
  130.   
  131.     private void initItem() {  
  132.         for (int i = 0; i < mChildSize; i++) {  
  133.             TabItem tabItem = new TabItem(getContext());  
  134.             LayoutParams params = new LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1);  
  135.             tabItem.setPadding(mPadding, mPadding, mPadding, mPadding);  
  136.             tabItem.setIconText(mListener.onIconSelect(i), mListener.onTextSelect(i));  
  137.             tabItem.setTextSize(mTextSize);  
  138.             tabItem.setTextColorNormal(mTextColorNormal);  
  139.             tabItem.setTextColorSelect(mTextColorSelect);  
  140.             tabItem.setLayoutParams(params);  
  141.             tabItem.setTag(i);  
  142.             tabItem.setOnClickListener(this);  
  143.             mTabItems.add(tabItem);  
  144.             addView(tabItem);  
  145.         }  
  146.     }  
  147.   
  148.     @Override  
  149.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  150.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  151.     }  
  152.   
  153.     @Override  
  154.     public void onClick(View v) {  
  155.         int position = (Integer) v.getTag();  
  156.         if (mViewPager.getCurrentItem() == position) {  
  157.             return;  
  158.         }  
  159.         for (TabItem tabItem : mTabItems) {  
  160.             tabItem.setTabAlpha(0);  
  161.         }  
  162.         mTabItems.get(position).setTabAlpha(1);  
  163.         mViewPager.setCurrentItem(position, false);  
  164.     }  
  165.   
  166.     public interface OnItemIconTextSelectListener {  
  167.   
  168.         int[] onIconSelect(int position);  
  169.   
  170.         String onTextSelect(int position);  
  171.     }  
  172. }  

注释有点少,额,不是少,是压根就没有,其实,这个类的代码不需要注释,我相信大家都能看懂,我就讲下他的作用吧,

1、添加 item 

2、监听 ViewPager 的滚动事件,从而设置相应 item 之间的颜色渐变,

3、设置相应 ViewPage 的透明度

4、为 TabItem 设置监听事件,

其实上面很多功能本来是在 MainActivity 中实现的,为了减少 Activity 内部的代码量,抽取出来,到达低耦合,高内聚的效果。


Ok,以上就是 微信6.1 tab 栏颜色渐变效果的实现全过程。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值