Android滑動切換頁面Tab文字顏色發生漸變效果
阿新 • • 發佈:2019-02-09
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);
設定透明度。