Android滑动切换页面Tab文字颜色发生渐变效果

Android项目中用到Tab作为导航条切换页面的效果,我相信大家都用到过吧,但是在切换的时候,Tab下划线跟着手指滑动的比例而滑动,相关的两个Tab的文字的颜色根据手指的滑动,而发生颜色渐变的改变。

下面说一下原理:

整个实现的过程需要用到,viewpager+fragment+tab,另外tab下划线的滑动以及tab中文本的颜色的渐变,是根据viewpager的页面的滑动比例计算出来相对应的值,然后进行设置,下划线是设置margin—left来完成的,颜色的渐变是通过设置画笔的透明度来实现。

一、自定义TextView,根据页面滑动的比例,计算出文本的透明度,如果对自定义流程不太清楚,请向这里看过来:

Android自定义View流程:

package com.example.tab;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

/**
 * 自定义textview,可以根据透明度改变其颜色
 * Created by xiaoyunfei on 16/4/7.
 */
public class MyTextView extends View {
    /**
     * 文本,默认:微信
     */
    private String mText = "微信";
    /**
     * 文本大小
     */
    private int mTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10f, getResources().getDisplayMetrics());
    /**
     * 文本颜色
     */
    private int mColor = 0xff45C01A;
    /**
     * 透明度
     */
    private float mAlpha = 0f;
    /**
     * 显示文本区域的矩形
     */
    private Rect mTextRect;
    private Paint mPaint;

    public MyTextView(Context context) {
        this(context, null);
    }

    public MyTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.change_color_icon_view, defStyleAttr, 0);
        int count = array.getIndexCount();
        for (int i = 0; i < count; i++) {
            int attr = array.getIndex(i);
            switch (attr) {
                case R.styleable.change_color_icon_view_color_://文本颜色
                    mColor = array.getColor(attr, 0xff45C01A);
                    break;
                case R.styleable.change_color_icon_view_text_://文本
                    mText = array.getString(attr);
                    break;
                case R.styleable.change_color_icon_view_textSize_://文本字号
                    mTextSize = array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10f, getResources().getDisplayMetrics()));
                    break;
            }
        }
        array.recycle();
        mPaint = new Paint();
        mTextRect = new Rect();
        mPaint.setTextSize(mTextSize);
        mPaint.setColor(mColor);
        mPaint.getTextBounds(mText, 0, mText.length(), mTextRect);
    }

    /**
     * 设置透明度
     *
     * @param alpha
     */
    public void setmAlpha(float alpha) {
        this.mAlpha = alpha;
        invalidateView();
    }

    private void invalidateView() {
        if (Looper.getMainLooper() == Looper.myLooper()) {
            //因为Android UI操作是非线程安全的,所以invalidate()不能再子线程中执行,需要配合handler使用
            invalidate();
        } else {
            //可以在子线程中使用,因为其封装了handler
            postInvalidate();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //alpha:255--->0:完全不透明--->完全透明
        int alpha = (int) Math.ceil(255 * mAlpha);
        drawDefaultText(canvas, alpha);
        drawTargetText(canvas, alpha);
    }

    /**
     * 写入目标文本
     *
     * @param canvas
     * @param alpha
     */
    private void drawTargetText(Canvas canvas, int alpha) {
        mPaint.setColor(mColor);
        mPaint.setAlpha(alpha);
        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        int x = width / 2 - mTextRect.width() / 2;
        int y = height / 2 + mTextRect.height() / 2;
        canvas.drawText(mText, x, y, mPaint);
    }

    /**
     * 写入默认文本
     *
     * @param canvas
     * @param alpha
     */
    private void drawDefaultText(Canvas canvas, int alpha) {
        mPaint.setColor(0xff555555);
        mPaint.setAlpha(255 - alpha);
        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        int x = width / 2 - mTextRect.width() / 2;
        int y = height / 2 + mTextRect.height() / 2;
        canvas.drawText(mText, x, y, mPaint);
    }
}


下面我们来看一下,在activity中,如何实现根据viewpager的滑动,来实现tab的下划线的滑动、以及颜色透明度的设置:

package com.example.tab;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
import android.util.DisplayMetrics;
import android.view.View;
import android.widget.LinearLayout;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends FragmentActivity implements View.OnClickListener, ViewPager.OnPageChangeListener {
    private ViewPager mViewPager;
    private List mList = new ArrayList();
    private String[] tag = {"微信", "通讯录", "发现", "我"};
    private int currentIndex = 0;
    //上部tab
    private int[] mTextId = {R.id.text_1, R.id.text_2, R.id.text_3, R.id.text_4};
    private MyTextView[] mTextViews = new MyTextView[4];
    private View tabView;
    private LinearLayout.LayoutParams params;
    /**
     * tab下划线的宽度
     */
    private int width;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
        initTabLine();
        initData();
    }

    /**
     * 初始化tab下划线
     */
    private void initTabLine() {
        params = (LinearLayout.LayoutParams) tabView.getLayoutParams();
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        width = metrics.widthPixels / 4;
        params.width = width;
        tabView.setLayoutParams(params);
    }

    /**
     * 初始化数据
     */
    private void initData() {
        for (int i = 0; i < 4; i++) {
            TabFragment fragment = new TabFragment();
            Bundle bundle = new Bundle();
            bundle.putString("content", tag[i]);
            fragment.setArguments(bundle);
            mList.add(fragment);
        }
        MyPageAdapter adapter = new MyPageAdapter(getSupportFragmentManager(), mList);
        mViewPager.setAdapter(adapter);
    }

    /**
     * 初始化控件
     */
    private void initViews() {
        mViewPager = (ViewPager) findViewById(R.id.viewpager);
        for (int i = 0; i < mTextId.length; i++) {
            mTextViews[i] = (MyTextView) findViewById(mTextId[i]);
            mTextViews[i].setOnClickListener(this);
        }
        tabView = findViewById(R.id.tab_view);

        mViewPager.addOnPageChangeListener(this);
        mTextViews[0].setmAlpha(1.0f);
    }

    @Override
    public void onClick(View v) {
        resetOtherTab();
        setClickTab(v.getId());
//        switch (v.getId()) {
//            case R.id.tab_1:
//                setClickTab(0);
//                break;
//            case R.id.tab_2:
//                setClickTab(1);
//                break;
//            case R.id.tab_3:
//                setClickTab(2);
//                break;
//            case R.id.tab_4:
//                setClickTab(3);
//                break;
//        }
    }

    /**
     * 设置当前的tab
     *
     * @param id
     */
    private void setClickTab(int id) {
        int i = -1;
        //根据id获取到指定的tab的下下标
        for (int j = 0; j < mTextId.length; j++) {
            if (mTextId[j] == id) {
                i = j;
                break;
            }
        }
        if (i == -1)
            return;
        params.leftMargin = width * i;
        tabView.setLayoutParams(params);
        setTab(i);
    }

    /**
     * 设置当前的tab
     *
     * @param i
     */
    private void setTab(int i) {
        mViewPager.setCurrentItem(i, false);
        mTextViews[i].setmAlpha(1.0f);
        currentIndex = i;
    }

    /**
     * 重新设置tab值默认值
     */
    private void resetOtherTab() {
        for (int i = 0; i < mTextId.length; i++) {
            mTextViews[i].setmAlpha(0f);
        }
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        //注:position大部分时间和左边的页面的下标一致;positionOffset以及positionOffsetPixels大部分时间和右边的页面占比保持一致
//        if (position == 0 && currentIndex == 0) {//0-->1
//            mTextViews[0].setmAlpha(1 - positionOffset);
//            mTextViews[1].setmAlpha(positionOffset);
//            params.leftMargin = (int) (width * positionOffset);
//        } else if (position == 0 && currentIndex == 1) {//1-->0
//            mTextViews[0].setmAlpha(1 - positionOffset);
//            mTextViews[1].setmAlpha(positionOffset);
//            params.leftMargin = (int) (-width * (1 - positionOffset) + width * currentIndex);
//        } else if (position == 1 && currentIndex == 1) {//1-->2
//            mTextViews[1].setmAlpha(1 - positionOffset);
//            mTextViews[2].setmAlpha(positionOffset);
//            params.leftMargin = (int) (width * positionOffset + width * currentIndex);
//        } else if (position == 1 && currentIndex == 2) {//2-->1
//            mTextViews[1].setmAlpha(1 - positionOffset);
//            mTextViews[2].setmAlpha(positionOffset);
//            params.leftMargin = (int) (-width * (1 - positionOffset) + width * currentIndex);
//        } else if (position == 2 && currentIndex == 2) {//2-->3
//            mTextViews[2].setmAlpha(1 - positionOffset);
//            mTextViews[3].setmAlpha(positionOffset);
//            params.leftMargin = (int) (width * positionOffset + width * currentIndex);
//        } else if (position == 2 && currentIndex == 3) {//3-->2
//            mTextViews[2].setmAlpha(1 - positionOffset);
//            mTextViews[3].setmAlpha(positionOffset);
//            params.leftMargin = (int) (-width * (1 - positionOffset) + width * currentIndex);
//        }
        //上面的代码可以抽成下面的一个方法
        pageScroll(position, positionOffset);


    }

    /**
     * viewpager滑动的时候设置顶部和底部tab的透明度以及顶部tab下划线的位置
     *
     * @param position
     * @param positionOffset
     */
    private void pageScroll(int position, float positionOffset) {
        if (position + 1 == mTextViews.length)//因为2-->3滑动的时候,在最后滚动结束的时候,position会突变成3(position大部分时候和左边的页面的下标一直),这样会造成下标越界
            return;
        mTextViews[position].setmAlpha(1 - positionOffset);
        mTextViews[position + 1].setmAlpha(positionOffset);
        if (position == currentIndex) {
            params.leftMargin = (int) (width * positionOffset + width * currentIndex);
        } else {
            params.leftMargin = (int) (-width * (1 - positionOffset) + width * currentIndex);
        }
        tabView.setLayoutParams(params);
    }

    @Override
    public void onPageSelected(int position) {
        currentIndex = position;
        setTab(position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
}

其中,实现的核心代码,在viewpager添加的监听的回调方法

onPageScrolled(int position, float positionOffset, int positionOffsetPixels)中,如果对
viewpager添加的这个监听,需要做一点解释:
 
  
 
  
有什么不理解的地方,可以参考我的另外一篇博客:
ViewPager的setOnPageChangeListener方法详解,这里对viewpager滑动的监听做了详细的说明。
例如:从1-->2滑动:
params.leftMargin = (int) (width * positionOffset + width * currentIndex);
从这个可以看出来,leftMargin的值等于当前的距左的值(width*currentIndex)加上滑动比例对应的宽度
(width*positionOffset)。
 
  
 
  
mTextViews[position].setmAlpha(1 - positionOffset);
 
  
mTextViews[position + 1].setmAlpha(positionOffset);
设置透明度。
 
  


项目下载


你可能感兴趣的:(自定义控件)