Android 微信6.1 tab栏图标和字体颜色渐变的实现

本文介绍了如何在Android中模仿微信6.1版本的tab栏,实现图标和字体颜色的平滑渐变效果。通过关键代码`setTabAlpha(float alpha)`方法调整画笔透明度,完成动态变化。文章还分享了如何在MainActivity中设置ViewPager滚动事件,以实现颜色渐变,并提倡代码结构优化,提高代码复用性和可维护性。

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

今天我带大家来看看,微信 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 栏颜色渐变效果的实现全过程。


源码地址





评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值