在项目中经常会遇到各种各样的需求和效果,而这样一个随着页面变化的指示器则是比较普遍的需求。
具体需求是:在一个可左右滑动的页面顶部居中放置一个标识当前页面的指示器,在页面进行切换的时候当前页面的指示器从选中颜色渐变成默认颜色,而下个即将显示的指示器从默认颜色渐变成选中颜色。
看下效果:
先创建一个继承至TextView的TextColorChangeView类
public class TextColorChangeView extends TextView {
public TextColorChangeView(Context context) {
super(context);
}
public TextColorChangeView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public TextColorChangeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
定义所需的画笔和进度等属性并初始化
这里默认画笔的颜色使用的是在xml中给TextView设置的字体颜色,可变画笔的颜色使用的是醒目的红色,progress标示的是当前页面滑动的百分比,front和after代表的是前一页还是后一页。
public class TextColorChangeView extends TextView {
public TextColorChangeView(Context context) {
this(context, null);
}
public TextColorChangeView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public TextColorChangeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 初始化默认画笔设置
normalPaint.setAntiAlias( true);
normalPaint.setColor(getTextColors().getDefaultColor());
normalPaint.setTextSize(getTextSize());
// 初始化变化的画笔设置
changePaint.setAntiAlias( true);
changePaint.setColor(Color.RED);
Log.d(TAG, "TextColorChangeView: " + getTextSize());
changePaint.setTextSize(getTextSize());
}
private static final String TAG = "TextColorChangeView";
// 默认画笔
private Paint normalPaint = new Paint();
// 可变化的画笔
private Paint changePaint = new Paint();
// 进度
private float progress = 0;
// 之前
private boolean front = true;
// 之后
private boolean after = false;
添加设置进度和页面的set方法
这个只写了一个设置after的方法,因为front始终是after的相反值
public void setProgress(float progress) {
this.progress = progress;
invalidate();
}
public void setAfter(boolean after) {
this.after = after;
this.front = !after;
}
重写onDraw方法
获取当前的文本内容和非空判断这些都是必要的,下面使用整个view的宽除以progress(外部页面滑动的比例)从而得出真正绘制内容position的位置,再判断是前一页还是后一页进行不同的赋值来绘制。
/**
* 画出默认的文字和进度的覆盖颜色
* -判断方向
*
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
String text = getText().toString();
if (!TextUtils.isEmpty(text)){
int position = (int) (getWidth() * progress);
if (after){
drawText(canvas, text, 0 , position);
}else if (front){
drawText(canvas, text, getWidth() - position , getWidth());
}
}
}
真正的绘制方法
第一步是先使用默认画笔绘制文本,裁剪区域设置为0j即不进行裁剪
第二步是使用可变画笔绘制高亮文本,裁剪区域设置为通过progress计算出的左边值和右边值
/**
* 真正的画出默认文字和高亮部分文字
* 设置裁剪区域
* 画出默认文字
* 根据left 和right值裁剪高亮部分的区域位置
* 画出高亮部分
* @param canvas
* @param text
* @param left
* @param right
*/
private void drawText(Canvas canvas, String text, int left, int right){
canvas.save();
canvas.clipRect(0, 0, getWidth(), getHeight());
int textWidth = (int) normalPaint.measureText(text);
int textHeight = (int) (normalPaint.descent() + normalPaint.descent());
canvas.drawText(text, getWidth() / 2 - textWidth / 2 , getHeight() / 2 + textHeight / 2,normalPaint);
canvas.restore();
canvas.save();
canvas.clipRect(left, 0 , right, getHeight());
canvas.drawText(text, getWidth() / 2 - textWidth / 2 , getHeight() / 2 + textHeight / 2, changePaint);
canvas.restore();
}
最后在activity中使用
欢迎star和review
public class MainActivity4 extends AppCompatActivity {
private ViewPager vpPage;
private LinearLayout llTabContainer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().hide();
setContentView(R.layout.activity_main4);
vpPage = findViewById(R.id.vpPage);
llTabContainer = findViewById(R.id.llTabContainer);
setData();
}
void setData(){
final List<TextView> list = new ArrayList<>();
for (int i = 0; i < 4; i++) {
TextView textView = new TextView(this);
textView.setText("page: " + i);
textView.setTextSize(38);
textView.setTextColor(Color.BLUE);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
params.gravity = Gravity.CENTER;
params.leftMargin = 100;
params.topMargin = 200;
textView.setLayoutParams(params);
list.add(textView);
}
vpPage.setAdapter(new PagerAdapter() {
@Override
public int getCount() {
return list.size();
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
container.addView(list.get(position));
return list.get(position);
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
});
vpPage.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
System.out.println("position: " + position);
System.out.println("positionOffset: " + positionOffset);
System.out.println("positionOffsetPixels: " + positionOffsetPixels);
/**
* 通过下标获取当前的指示器
* 设置after为false表示当前不是下一个要完全显示的指示器
* 设置进度为 1-positionoffset代表着将当前显示的指示器渐渐取消掉高亮
*/
TextColorChangeView cur = (TextColorChangeView) llTabContainer.getChildAt(position);
cur.setAfter(false);
cur.setProgress(1-positionOffset);
/**
* 下一个不等于空的情况下设置为要显示的指示器,并把当前viewpager页面显示的进度传递给指示器用作渐变效果
*/
TextColorChangeView next = (TextColorChangeView) llTabContainer.getChildAt(position + 1);
if (next != null){
next.setAfter(true);
next.setProgress(positionOffset);
}
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
TextColorChangeView childAt = (TextColorChangeView) llTabContainer.getChildAt(0);
childAt.setProgress(1.0f);
}
}