第一步 自定义控件属性
首先在res/values/attrs
<declare-styleable name="GradientTextView">
<attr name="text_size" format="dimension"/>
<attr name="text_style" format="reference|integer"/>
<attr name="is_can_grandient" format="boolean"/>
<attr name="text_color" format="color|reference"/>
<attr name="gravity" format="integer"/>
<attr name="max_width" format="dimension"/>
<attr name="start_color" format="color|reference"/>
<attr name="end_color" format="color|reference"/>
<attr name="angle" format="integer"/>
<attr name="text" format="string"/>
</declare-styleable>
第二步 从构造方法里面去获取自定义的属性值
public GradientTextView(Context context) {
super(context);
init(context, null);
}
public GradientTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public GradientTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, @Nullable AttributeSet attrs) {
if(attrs == null) {
return;
}
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.GradientTextView);
mIsCanGrandient = typedArray.getBoolean(R.styleable.GradientTextView_is_can_grandient,false);
mText = typedArray.getString(R.styleable.GradientTextView_text);
mTextSize = typedArray.getDimensionPixelSize(R.styleable.GradientTextView_text_size, mTextSize);
mAngle = typedArray.getInt(R.styleable.SGradientTextView_angle, -1);
mTextColor = typedArray.getColor(R.styleable.GradientTextView_text_color,Color.BLACK);
color[0] = typedArray.getColor(R.styleable.GradientTextView_start_color,Color.BLACK);
color[1] = typedArray.getColor(R.styleable.GradientTextView_end_color,Color.BLACK);
mGravity = typedArray.getInt(R.styleable.GradientTextView_gravity, Gravity.LEFT);
mMaxWidth = typedArray.getDimensionPixelSize(R.styleable.GradientTextView_max_width, Integer.MAX_VALUE);
textStyle = typedArray.getInt(R.styleable.GradientTextView_text_style, Typeface.NORMAL);
setPaintAttrs();
typedArray.recycle();
}
第三步 重写方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int measureWidth = measureWidth(widthMeasureSpec);
int measureHeight = measureHeight(heightMeasureSpec);
setMeasuredDimension(measureWidth, measureHeight);
mMeasuredWidth = getMeasuredWidth();
mMeasuredHeight = getMeasuredHeight();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
final float desiredTextWidth = mPaint.measureText(mText);
final float maxTextWidth = mMeasuredWidth - getPaddingLeft() - getPaddingRight();
if (mIsCanGrandient) {
if (mAngle >= 0) {
Rect rect = new Rect();
mPaint.getTextBounds(mText,0,mText.length(), rect);
changeGradientColor(mAngle, rect.width(), rect.height());
LinearGradient gradient = new LinearGradient(startX, startY, endX, endY, color[0], color[1], Shader.TileMode.CLAMP);
mPaint.setShader(gradient);
}else {
setPaintAttrs();
}
}
if (desiredTextWidth > maxTextWidth) {
float scaleX = maxTextWidth / desiredTextWidth;
if (scaleX > RATIO_THRESHOLD) {
scaleX *= 0.98f;
mPaint.setTextScaleX(scaleX);
} else {
mPaint.setTextScaleX(scaleX);
float ellipsisLength = mPaint.measureText("...");
int drawCharsCount = mPaint.breakText(mText, true, maxTextWidth - ellipsisLength, new float[0]);
mText = mText.substring(0, drawCharsCount) + "...";
}
}
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
final float fontBaseLine = (getPaddingTop() + mMeasuredHeight - getPaddingBottom()) / 2 + (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
if (mGravity == Gravity.RIGHT) {
final float targetTextWidth = mPaint.measureText(mText);
canvas.drawText(mText, mMeasuredWidth - getPaddingRight() - targetTextWidth, fontBaseLine, mPaint);
} else if (mGravity == Gravity.CENTER) {
final float targetTextWidth = mPaint.measureText(mText);
canvas.drawText(mText, (getPaddingLeft() + mMeasuredWidth - getPaddingRight()) / 2 - targetTextWidth / 2, fontBaseLine, mPaint);
} else {
canvas.drawText(mText, getPaddingLeft(), fontBaseLine, mPaint);
}
}
以下是全部的代码
class GradientTextView extends View {
private static final float RATIO_THRESHOLD = 1.0f / 2;
private int mTextSize = dpToPx(12);
private int mTextColor;
private int mMeasuredWidth, mMeasuredHeight, mMaxWidth = Integer.MAX_VALUE;
private TextPaint mPaint = new TextPaint();
private String mText = "";
private int mGravity;
private int[] color = new int[]{Color.BLACK,Color.BLACK};
private float startX,startY,endX,endY;
//渐变色的角度值
private int mAngle;
//是否开启渐变效果
private boolean mIsCanGrandient;
private int textStyle;
public GradientTextView(Context context) {
super(context);
init(context, null);
}
public GradientTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public GradientTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, @Nullable AttributeSet attrs) {
if(attrs == null) {
return;
}
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.GradientTextView);
mIsCanGrandient = typedArray.getBoolean(R.styleable.GradientTextView_is_can_grandient,false);
mText = typedArray.getString(R.styleable.GradientTextView_text);
mTextSize = typedArray.getDimensionPixelSize(R.styleable.GradientTextView_text_size, mTextSize);
mAngle = typedArray.getInt(R.styleable.SGradientTextView_angle, -1);
mTextColor = typedArray.getColor(R.styleable.GradientTextView_text_color,Color.BLACK);
color[0] = typedArray.getColor(R.styleable.GradientTextView_start_color,Color.BLACK);
color[1] = typedArray.getColor(R.styleable.GradientTextView_end_color,Color.BLACK);
mGravity = typedArray.getInt(R.styleable.GradientTextView_gravity, Gravity.LEFT);
mMaxWidth = typedArray.getDimensionPixelSize(R.styleable.GradientTextView_max_width, Integer.MAX_VALUE);
textStyle = typedArray.getInt(R.styleable.GradientTextView_text_style, Typeface.NORMAL);
setPaintAttrs();
typedArray.recycle();
}
/**
* 设置paint的属性样式
*/
private void setPaintAttrs(){
mPaint.reset();
mPaint.setColor(mTextColor);
mPaint.setTextSize(mTextSize);
mPaint.setAntiAlias(true);
mPaint.setTypeface(getTypeface(textStyle));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int measureWidth = measureWidth(widthMeasureSpec);
int measureHeight = measureHeight(heightMeasureSpec);
setMeasuredDimension(measureWidth, measureHeight);
mMeasuredWidth = getMeasuredWidth();
mMeasuredHeight = getMeasuredHeight();
}
private int measureHeight(int heightMeasureSpec) {
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int result = heightSize;
switch (heightMode) {
case MeasureSpec.UNSPECIFIED:
case MeasureSpec.AT_MOST:
Paint.FontMetricsInt fontMetricsInt = mPaint.getFontMetricsInt();
result = fontMetricsInt.bottom - fontMetricsInt.top + getPaddingTop() + getPaddingBottom();
break;
case MeasureSpec.EXACTLY:
result = heightSize;
break;
}
return result;
}
private int measureWidth(int widthMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int result = widthSize;
switch (widthMode) {
case MeasureSpec.UNSPECIFIED:
case MeasureSpec.AT_MOST:
result = (int) (mPaint.measureText(mText) + getPaddingLeft() + getPaddingRight());
if (result > mMaxWidth) {
result = mMaxWidth;
}
break;
case MeasureSpec.EXACTLY:
result = widthSize;
break;
}
return result;
}
public void setText(String text) {
if (text == null) {
text = "";
}
if (text.equals(mText)) {
return;
}
mText = text;
mPaint.setTextScaleX(1.0f);
requestLayout();
invalidate();
}
public void setTextColor(int color) {
mTextColor = color;
mPaint.setColor(mTextColor);
invalidate();
}
/**
* 是否开启渐变效果
* @param isCanGradient
*/
public void setIsCanGradient(boolean isCanGradient){
mIsCanGrandient = isCanGradient;
invalidate();
}
/**
* 渐变色值
* @param startColor
* @param endColor
*/
public void setGradientColor(int startColor,int endColor){
color[0] = startColor;
color[1] = endColor;
invalidate();
}
/**
* 颜色渐变
* @param angle
* 0从左到右
* 90 从下到上
* 180 从右到左
* 270 从上到下
*/
public void setGradient(int angle){
mAngle = angle;
invalidate();
}
/**
* 通过不同的角度改变渐变色的起始方向
* @param angle
* @param width
* @param height
*/
private void changeGradientColor(int angle,float width,float height){
if ((angle % 90) != 0){
throw new IllegalArgumentException("The parameter must be a multiple of 90");
}
switch (angle % 360){
case 0:
startX = 0;
startY = (float) (height *0.5);
endX = width;
endY = (float) (height *0.5);
break;
case 90:
startX = (float) (width *0.5);
startY = height;
endX = (float) (width *0.5);
endY = 0;
break;
case 180:
startX = width;
startY = (float) (height *0.5);
endX = 0;
endY = (float) (height *0.5);
break;
case 270:
startX = (float) (width *0.5);
startY = 0;
endX = (float) (width *0.5);
endY = height;
break;
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
final float desiredTextWidth = mPaint.measureText(mText);
final float maxTextWidth = mMeasuredWidth - getPaddingLeft() - getPaddingRight();
if (mIsCanGrandient) {
if (mAngle >= 0) {
Rect rect = new Rect();
mPaint.getTextBounds(mText,0,mText.length(), rect);
changeGradientColor(mAngle, rect.width(), rect.height());
LinearGradient gradient = new LinearGradient(startX, startY, endX, endY, color[0], color[1], Shader.TileMode.CLAMP);
mPaint.setShader(gradient);
}else {
setPaintAttrs();
}
}
if (desiredTextWidth > maxTextWidth) {
float scaleX = maxTextWidth / desiredTextWidth;
if (scaleX > RATIO_THRESHOLD) {
scaleX *= 0.98f;
mPaint.setTextScaleX(scaleX);
} else {
mPaint.setTextScaleX(scaleX);
float ellipsisLength = mPaint.measureText("...");
int drawCharsCount = mPaint.breakText(mText, true, maxTextWidth - ellipsisLength, new float[0]);
mText = mText.substring(0, drawCharsCount) + "...";
}
}
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
final float fontBaseLine = (getPaddingTop() + mMeasuredHeight - getPaddingBottom()) / 2 + (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
if (mGravity == Gravity.RIGHT) {
final float targetTextWidth = mPaint.measureText(mText);
canvas.drawText(mText, mMeasuredWidth - getPaddingRight() - targetTextWidth, fontBaseLine, mPaint);
} else if (mGravity == Gravity.CENTER) {
final float targetTextWidth = mPaint.measureText(mText);
canvas.drawText(mText, (getPaddingLeft() + mMeasuredWidth - getPaddingRight()) / 2 - targetTextWidth / 2, fontBaseLine, mPaint);
} else {
canvas.drawText(mText, getPaddingLeft(), fontBaseLine, mPaint);
}
}
private static Typeface getTypeface(int textStyle) {
Typeface typeface;
switch (textStyle) {
case Typeface.NORMAL:// 正常字体.
default:
typeface = Typeface.DEFAULT;
break;
case Typeface.BOLD:// 加粗字体.
typeface = Typeface.DEFAULT_BOLD;
break;
case Typeface.ITALIC:// 倾斜字体.
typeface = Typeface.create(Typeface.DEFAULT, Typeface.ITALIC);
break;
case Typeface.BOLD_ITALIC:// 加粗倾斜字体.
typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC);
}
return typeface;
}
private int dpToPx(int dps) {
return Math.round(getResources().getDisplayMetrics().density * dps);
}
}
支持属性设置,方法更改相应的要求,里面只要是用到了LinearGradient 这个方法来设置,只支持90的倍数,具体的角度更改设值在setGradient()方法里面有解释