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);
}
}
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) {
}
}
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);
设置透明度。
项目下载