1. 程式人生 > >Android滑動切換頁面Tab文字顏色發生漸變效果

Android滑動切換頁面Tab文字顏色發生漸變效果

Android專案中用到Tab作為導航條切換頁面的效果,我相信大家都用到過吧,但是在切換的時候,Tab下劃線跟著手指滑動的比例而滑動,相關的兩個Tab的文字的顏色根據手指的滑動,而發生顏色漸變的改變。

下面說一下原理:

整個實現的過程需要用到,viewpager+fragment+tab,另外tab下劃線的滑動以及tab中文字的顏色的漸變,是根據viewpager的頁面的滑動比例計算出來相對應的值,然後進行設定,下劃線是設定margin—left來完成的,顏色的漸變是通過設定畫筆的透明度來實現。

一、自定義TextView,根據頁面滑動的比例,計算出文字的透明度,如果對自定義流程不太清楚,請向這裡看過來:

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<Fragment> mList = new ArrayList<Fragment>();
    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新增的這個監聽,需要做一點解釋:
有什麼不理解的地方,可以參考我的另外一篇部落格:
例如:從1-->2滑動:
params.leftMargin = (int) (width * positionOffset + width * currentIndex);
從這個可以看出來,leftMargin的值等於當前的距左的值(width*currentIndex)加上滑動比例對應的寬度
(width*positionOffset)。
mTextViews[position].setmAlpha(1 - positionOffset);
mTextViews[position + 1].setmAlpha(positionOffset);
設定透明度。

專案下載