在项目中,经常会遇到文字色值渐变效果,比如在ViewPage
中的页面指示器,这种实现起来也比较简单,无非是计算偏移量、使用Paint
以及Canvas
提供的方法即可。
该文本有两种颜色,一种默认颜色,一种为要改变的颜色,因此需要两只画笔,根据偏移量,计算对应起始点、结束点使用相应画笔即可。
在这里,只简单定义三个属性
<declare-styleable name="ColorTrackTextView">
<attr name="originColor" format="color" />
<attr name="changeColor" format="color" />
<attr name="direction" format="enum">
<enum name="left2Right" value="1" />
<enum name="right2Left" value="2" />
attr>
declare-styleable>
根据分析,继承 TextView
,重写onDraw
方法即可。
详细注释见代码。
public class ColorTrackTextView extends TextView {
private Paint mOriginPaint;
private Paint mChangePaint;
private float mProgress = 0.0f;
private int mStartX = Integer.MIN_VALUE;
private float mBaseLine = Float.MAX_VALUE;
private Direction mDirection = Direction.LEFT2RIGHT;
public ColorTrackTextView(Context context) {
this(context, null);
}
public ColorTrackTextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ColorTrackTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint(context, attrs);
}
private void initPaint(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ColorTrackTextView);
int originColor = typedArray.getColor(R.styleable.ColorTrackTextView_originColor, getTextColors().getDefaultColor());
int changeColor = typedArray.getColor(R.styleable.ColorTrackTextView_changeColor, getTextColors().getDefaultColor());
int index = typedArray.getInt(R.styleable.ColorTrackTextView_direction, -1);
if (index >= 1) {
setDirection(index);
}
mOriginPaint = getPaintByColor(originColor);
mChangePaint = getPaintByColor(changeColor);
typedArray.recycle();
}
private Paint getPaintByColor(int color) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
paint.setDither(true);
paint.setTextSize(getTextSize());
return paint;
}
@Override
protected void onDraw(Canvas canvas) {
int middle = (int) (mProgress * getWidth());
// 从左到右
if (mDirection == Direction.LEFT2RIGHT) {
// 绘制变色区域
drawText(canvas, mChangePaint, 0, middle);
// 绘制不变色区域
drawText(canvas, mOriginPaint, middle, getWidth());
} else {// 从右到左
// 绘制变色区域
drawText(canvas, mChangePaint, getWidth() - middle, getWidth());
// 绘制不变色区域
drawText(canvas, mOriginPaint, 0, getWidth() - middle);
}
}
/**
* 绘制文本
*
* @param canvas
* @param paint
* @param start
* @param end
*/
private void drawText(Canvas canvas, Paint paint, int start, int end) {
canvas.save();
Rect rect = new Rect(start, 0, end, getHeight());
canvas.clipRect(rect);
canvas.drawText(getText().toString(), getStartX(paint), getBaseLine(paint), paint);
canvas.restore();
}
/**
* 计算文字开始X坐标
*
* @param paint
* @return
*/
private int getStartX(Paint paint) {
if (mStartX != Integer.MIN_VALUE) return mStartX;
String text = getText().toString();
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
mStartX = (getWidth() - bounds.width()) / 2;
return mStartX;
}
/**
* 计算baseLine
*
* @param paint
* @return
*/
private float getBaseLine(Paint paint) {
if (mBaseLine != Float.MAX_VALUE) return mBaseLine;
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float dy = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
mBaseLine = getHeight() / 2 + dy;
return mBaseLine;
}
public void setDirection(Direction direction) {
this.mDirection = direction;
}
public synchronized void setProgress(float progress) {
if (progress < 0) {
throw new IllegalArgumentException("进度不能小于0哦");
}
this.mProgress = progress;
invalidate();
}
private void setDirection(int index) {
if (index == 1) {
mDirection = Direction.LEFT2RIGHT;
} else if (index == 2) {
mDirection = Direction.RIGHT2LEFT;
}
}
public enum Direction {
LEFT2RIGHT, RIGHT2LEFT
}
}
验证下效果吧
测试Activity
:
class ColorTrackActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_color_track)
}
fun left2Right(view: View) {
cttv_content.setDirection(ColorTrackTextView.Direction.LEFT2RIGHT)
val anim = ObjectAnimator.ofFloat(0.0f, 1.0f)
anim.duration = 5000
anim.addUpdateListener { animation ->
val progress = animation.animatedValue as Float
cttv_content.setProgress(progress)
}
anim.start()
}
fun right2Left(view: View) {
cttv_content.setDirection(ColorTrackTextView.Direction.RIGHT2LEFT)
val anim = ObjectAnimator.ofFloat(0.0f, 1.0f)
anim.duration = 5000
anim.addUpdateListener { animation ->
val progress = animation.animatedValue as Float
cttv_content.setProgress(progress)
}
anim.start()
}
}
activity_color_track
布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<com.happy.android.study.view.ColorTrackTextView
android:id="@+id/cttv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="小洋人最happy"
android:textSize="28dp"
app:changeColor="@android:color/holo_red_dark"
app:direction="right2Left" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:orientation="horizontal">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="left2Right"
android:text="从左到右渐变" />
<Button
android:id="@+id/right2Left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="right2Left"
android:text="从右到左渐变" />
LinearLayout>
LinearLayout>
实现代码比较简单,主要知识点如下:
Canvas.clipRect
使用