自定义一个可部分变色的TextView,类似歌词效果:
最终效果如上图,可以从左往右变色也可以从右往左变色。
实现的思路是将文字分成两部分来绘制,
如这个helloworld,定好一个位置将竖线左边的画笔颜色改成红色,后面的画笔颜色为默认颜色分成两部分来绘制。因为最终使用的是canvas.drawText来绘制,但是文字“HelloWorld”是一个整体,必须进行裁剪。具体方法是使用canvas.clipRect,绘制左边文字时裁剪文字开始到红色位置的部份,右边文字裁剪红线位置到文字末端部份。
动态变色效果则是通过改变红线位置,不断重绘即可。
关键代码
@Override
protected void onDraw(Canvas canvas) {
int position = (int) (getWidth() * progress);
if (currentOrientation == LEFT_TO_RIGHT) {
drawText(canvas, changedPaint, 0, position);//绘制改变颜色的部份
drawText(canvas, defaultPaint, position, getWidth()); //绘制默认颜色的部份
} else {
drawText(canvas, changedPaint, getWidth() - position, getWidth());
drawText(canvas, defaultPaint, 0, getWidth() - position);
}
}
重写了onDraw方法,根据progress得到分割的位置position,然后调用了两次drawText,分别绘制0~position和position到末端的文字。
private void drawText(Canvas canvas, Paint paint, int left, int right) {
canvas.save();
clipRect.set(left, 0, right, getBottom());
canvas.clipRect(clipRect); //裁剪一部份绘制不同颜色
// 给boundsRect赋值
paint.getTextBounds(getText().toString(), 0, getText().toString().length(), boundsRect);
int dx = (getWidth() - boundsRect.width()) / 2; //让文字居中
Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();
//fontMetricsInt.bottom为基线到底部的距离为正值,fontMetricsInt.top为基线到顶部的距离,为负值
int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;//文字y中心到基线的距离
int baseLine = getHeight() / 2 + dy; //基线的位置
//drawText的第三个参数传的是baseLine的位置
canvas.drawText(getText().toString(), dx, baseLine, paint);
canvas.restore();
}
drawText方法的关键是根据position进行了clipRect裁剪。因为我们对画布进行了操作所以一进来需要保存画布的状态,在绘制完成后使用restore进行恢复,否则右边部分的绘制将会失效(因为画布裁剪只剩下了左边部分)。
最后提供一个改变分割位置的方法:
public void setProgress(float progress) {//改变进度
this.progress = progress;
invalidate();
}
外部使用的时候直接输入百分比就可以,在onDraw中根据比例计算实际分割位置
int position = (int) (getWidth() * progress);
下面是完整代码---------------------------------------------------------------------------------------------------------------------------->>>>>>
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.TextView;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatTextView;
//自定义可改变颜色的textView,跟歌词效果类似
public class ChangeColorTextView extends AppCompatTextView {
private Paint defaultPaint;
private Paint changedPaint;
private float progress = 0f;
private int currentOrientation = LEFT_TO_RIGHT;
public static final int LEFT_TO_RIGHT = 0;
public static final int RIGHT_TO_LEFT = 1;
//限制输入参数
@IntDef(value = {LEFT_TO_RIGHT, RIGHT_TO_LEFT})
public @interface Orientation{
}
public ChangeColorTextView(Context context) {
this(context, null);
}
public ChangeColorTextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ChangeColorTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint(context, attrs);
}
//设置从左到右还是右到左
public void setCurrentOrientation(@Orientation int currentOrientation) {
this.currentOrientation = currentOrientation;
}
//初始化画笔
private void initPaint(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ChangeColorTextView);
//读取自定义属性
int defaultColor = typedArray.getColor(R.styleable.ChangeColorTextView_defaultColor, getTextColors().getDefaultColor());
int changeColor = typedArray.getColor(R.styleable.ChangeColorTextView_changeColor, getTextColors().getDefaultColor());
typedArray.recycle();
defaultPaint = getPaintByColor(defaultColor);
changedPaint = getPaintByColor(changeColor);
}
private Paint getPaintByColor(int Color) {
Paint paint = new Paint();
paint.setAntiAlias(true); //抗锯齿
paint.setDither(true);//防抖
paint.setColor(Color);
paint.setTextSize(getTextSize());
return paint;
}
@Override
protected void onDraw(Canvas canvas) {
int position = (int) (getWidth() * progress);
if (currentOrientation == LEFT_TO_RIGHT) {
drawText(canvas, changedPaint, 0, position);//绘制改变颜色的部份
drawText(canvas, defaultPaint, position, getWidth()); //绘制默认颜色的部份
} else {
drawText(canvas, changedPaint, getWidth() - position, getWidth());
drawText(canvas, defaultPaint, 0, getWidth() - position);
}
}
Rect clipRect = new Rect();
Rect boundsRect = new Rect();
private void drawText(Canvas canvas, Paint paint, int left, int right) {
canvas.save();
clipRect.set(left, 0, right, getBottom());
canvas.clipRect(clipRect); //裁剪一部份绘制不同颜色
// 给boundsRect赋值
paint.getTextBounds(getText().toString(), 0, getText().toString().length(), boundsRect);
int dx = (getWidth() - boundsRect.width()) / 2; //让文字居中
Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();
//fontMetricsInt.bottom为基线到底部的距离为正值,fontMetricsInt.top为基线到顶部的距离,为负值
int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;//文字y中心到基线的距离
int baseLine = getHeight() / 2 + dy; //基线的位置
//drawText的第三个参数传的是baseLine的位置
canvas.drawText(getText().toString(), dx, baseLine, paint);
canvas.restore();
}
public void setProgress(float progress) {//改变进度
this.progress = progress;
invalidate();
}
}
attrs.xml自定义属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ChangeColorTextView">
<attr name="defaultColor" format="color"/>
<attr name="changeColor" format="color"/>
</declare-styleable>
</resources>
简单使用:
activity.xml
.................
<com.sample.changecolortext.ChangeColorTextView
android:id="@+id/changeColorTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="This is L2R ChangeColorTextView"
android:textSize="20sp"
app:changeColor="#ff0000"
app:defaultColor="#ccc" />
<SeekBar
android:id="@+id/seekBar"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="150dp" />
.................
Activity中使用SeekBar改变进度
.....................
changeColorTextView=findViewById(R.id.changeColorTextView2);
//改变方向
//changeColorTextView.setCurrentOrientation(ChangeColorTextView.RIGHT_TO_LEFT);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
changeColorTextView.setProgress(progress/100f);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
.....................
完。