相信大家都见到了微信图标颜色渐变的过程,是不是感觉很牛逼?不得不说微信团队确实是很厉害的团队,不管是从设计还是开发人员。
今天我带大家来看看,微信 tab 栏图标和字体颜色渐变的过程。先上图吧!今天学了一招制作 gif 动态图的快捷方法。刚好用的上,以前一直想写点什么东西,
苦于一直不知道怎么生成动态图,现在终于学会了,哈哈,让我偷偷的乐一会。额,还是上图吧。。。
好了,效果图也看到了,那么我也就不多啰嗦了,直接进入主题,看代码
- 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 ;
- /*字体未选择的时候的颜色*/
- private int mTextColorNormal;
- /*绘制未选中时字体的画笔*/
- private Paint mTextPaintNormal;
- /*绘制已选中时字体的画笔*/
- private Paint mTextPaintSelect;
- /*每个 item 的宽和高,包括字体和图标一起*/
- private int mViewHeight, mViewWidth;
- /*字体的内容*/
- private String mTextValue ;
- /*已选中时的图标*/
- private Bitmap mIconNormal;
- /*未选中时的图标*/
- private Bitmap mIconSelect;
- /*用于记录字体大小*/
- private Rect mBoundText;
- /*已选中是图标的画笔*/
- private Paint mIconPaintSelect;
- /*为选中时图标的画笔*/
- 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();
- }
- /*初始化画笔,并设置出是内容*/
- 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);
- }
- /*测量字体的大小*/
- private void measureText() {
- mTextPaintNormal.getTextBounds(mTextValue, 0, mTextValue.length(), mBoundText);
- }
- /*测量字体和图标的大小,并设置自身的宽和高*/
- @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) ;
- }
- /*话图标,先画为选中的图标,在画已选中的图标*/
- 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);
- }
- /*画字体*/
- 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;
- }
- /*通过 alpha 来设置 每个画笔的透明度,从而实现现实的效果*/
- 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();
- }
- }
分析: 以上代码,功能实现 tab 的每个 item 的内容,在微信的项目中也就是,一个图标加一个字体,
关键代码就在public void setTabAlpha(float alpha) 这个方法,此方法是 viewPager 切换 item 时,不断改变偏移量来调用,从而不断改变
每个画笔的透明度,实现图标和颜色的渐变;是不是很简单?到这里其实我们颜色渐变的代码就已经实现了。接下来的内容可以忽略
这样我们只需要在 MainActivity 的 xml 中定义一个线性布局,然后放如四个我们自定义的 View 进去,就可以了。但是这样你就满足了吗?
我先来给你们看看我的MainActivity 的 代码;
- 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 = {"微信", "通讯录", "发现", "我"};
- 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;
- }
- }
- }
是不是很简单,而 xml 更简单
- <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>
可以看到没有定义我们刚刚自定义的 TabItem,而是使用的 TabView,那 TabView 到底是啥东西?相信大家都想到了,TabView 其实就是我们自定义的线性布局,
- 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("亲,在您设置TabView的ViewPager时,请先设置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("请让你的pageAdapter实现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);
- }
- }
注释有点少,额,不是少,是压根就没有,其实,这个类的代码不需要注释,我相信大家都能看懂,我就讲下他的作用吧,
1、添加 item
2、监听 ViewPager 的滚动事件,从而设置相应 item 之间的颜色渐变,
3、设置相应 ViewPage 的透明度
4、为 TabItem 设置监听事件,
其实上面很多功能本来是在 MainActivity 中实现的,为了减少 Activity 内部的代码量,抽取出来,到达低耦合,高内聚的效果。
Ok,以上就是 微信6.1 tab 栏颜色渐变效果的实现全过程。