又是一天过去了,已经坚持写博客三天了,因为之前在研究自定义view,所以也积累了很多demo了,都是借鉴大神的然后自己敲了很多遍的东西,以前敲完都没什么感觉的,所以还是需要静下心总结一下的,于是我开始写博客了。
废话不多说了,进入我们今天的主题(变色TextView及ViewPager指示器),以前都是用github上面的ViewPagerIndicator,只管用了,感觉很高大上的东西,于是自己开始模仿一下啦,,现在还只能说是模仿.^~^!
先看一下运行的效果
思路:
比如绘制一个“helloword!”我们想”hello”为红色,“word!”为黑色,则通过canvas的clipRect方法裁剪画布,裁剪的前部分画笔用红色绘制,后半部分用黑色绘制,不断的改变progress(0~1)的值,来绘制变红的和为变红的地方。
attr文件:
<attr name="change_icon" format="reference"></attr><attr name="change_color" format="color"></attr><attr name="change_text" format="string"></attr><attr name="text_size" format="dimension"></attr><declare-styleable name="changeTextColor"><attr name="change_icon"></attr><attr name="change_color"></attr><attr name="change_text"></attr><attr name="text_size"></attr></declare-styleable>
第一步:老套路了,我们创建一个叫FadeView的extends View,然后覆盖三个构造方法。
/*** Author:Yqy* Date:-07-27* Desc:渐变view* Company:cisetech*/public class FadeView extends View{/*** 需要绘制的文本*/private String mText="测试";/*** 绘制的画笔*/private Paint mPaint;/*** 文本size*/private float mTextSize= TypedValue.applyDimension(PLEX_UNIT_SP,14.5f,getResources().getDisplayMetrics());/*** 未变的text的颜色*/private int originTextColor= Color.BLACK;/*** 变换text的颜色*/private int changeTextColor=Color.RED;/*** text颜色变换的方向,是从左到右还是反之*/private DIRECTION mDirection=DIRECTION.LEFT;/*** 文本绘制的开始位置*/private int mTextStartX;/*** 颜色变换的进度*/private float mProgress;/*** 文字的宽度*/private int mTextWidth;public FadeView(Context context) {this(context, null);}public FadeView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public FadeView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);obtainAttr(context, attrs, defStyleAttr);initView();}/*** 初始化*/private void initView() {mPaint=new Paint(Paint.ANTI_ALIAS_FLAG);mPaint.setStyle(Paint.Style.FILL);mPaint.setDither(true);mPaint.setTextSize(mTextSize);}/*** 枚举类,变换颜色的方向*/public enum DIRECTION{LEFT,RIGHT;}}
第二步:获取参数
private void obtainAttr(Context context, AttributeSet attrs, int defStyleAttr) {TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.fadeView, defStyleAttr, 0);for (int i = 0; i <a.getIndexCount() ; i++) {int attr=a.getIndex(i);switch (attr){case R.styleable.fadeView_fadeText:mText=a.getString(attr);break;case R.styleable.fadeView_changeColor:changeTextColor=a.getColor(attr, changeTextColor);break;case R.styleable.fadeView_originColor:originTextColor=a.getColor(attr, originTextColor);break;case R.styleable.fadeView_textSize:mTextSize=a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(PLEX_UNIT_SP, 14.5f, getResources().getDisplayMetrics()));break;case R.styleable.fadeView_direction:int flag = a.getInt(attr, 1);if(flag==1){mDirection=DIRECTION.LEFT;}else{mDirection=DIRECTION.RIGHT;}break;}}a.recycle();}
第三步:重写onMeasure方法,测量View,告诉父控件自己需要多大位置。
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {mTextWidth= (int) mPaint.measureText(mText,0,mText.length());int width=measureWidth(widthMeasureSpec);int height=measureHeight(heightMeasureSpec);mTextStartX=width/2-mTextWidth/2;setMeasuredDimension(width,height);}/**** @param heightMeasureSpec* @return 测量过后的height*/private int measureHeight(int heightMeasureSpec) {int result;int mode=MeasureSpec.getMode(heightMeasureSpec);int size=MeasureSpec.getSize(heightMeasureSpec);/*** 当为确定值的时候就取确定值,* 当不确定的时候大小就用文本的高度* (int) (mPaint.getFontMetrics().bottom-mPaint.getFontMetrics().top);* 方法可以获取文本宽度*/if(mode==MeasureSpec.EXACTLY){result=size;}else{result= (int) (mPaint.getFontMetrics().bottom-mPaint.getFontMetrics().top);}result=mode==MeasureSpec.AT_MOST?Math.min(result,size):result;return result+getPaddingTop()+getPaddingBottom();}/**** @param widthMeasureSpec* @return 测量过后的width*/private int measureWidth(int widthMeasureSpec) {int result;int mode=MeasureSpec.getMode(widthMeasureSpec);int size=MeasureSpec.getSize(widthMeasureSpec);/*** 当为确定值的时候就取确定值,* 当不确定的时候大小就用文本的大小* mPaint.measureText方法可以获取文本宽度*/if(mode==MeasureSpec.EXACTLY){result=size;}else{result= (int) mPaint.measureText(mText,0,mText.length());}result=mode==MeasureSpec.AT_MOST?Math.min(result,size):result;return result+getPaddingLeft()+getPaddingRight();}
补充一下,关于drawText的一些知识,可以参考我师父的神作,自定义控件之绘图篇( 五):drawText()详解
第四步:关键一步咯,重写onDraw方法,绘制我们的文本
/*** 从startX裁剪到endX范围绘制文本*/private void drawText(Canvas canvas,int color, int startX, int endX) {mPaint.setColor(color);canvas.save(Canvas.CLIP_SAVE_FLAG);canvas.clipRect(startX, 0, endX, getMeasuredHeight());/*** 绘制文本的y坐标,看不懂的话可以看我师傅的神作,博客有给出链接*/int baseY= (int) (getMeasuredHeight()/2+(mPaint.getFontMetrics().bottom-mPaint.getFontMetrics().top)/2-mPaint.getFontMetrics().bottom);canvas.drawText(mText,mTextStartX,baseY,mPaint);canvas.restore();}
第五步:测试一下我们的成果(偷下懒,用属性动画测试了)
public FadeView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);obtainAttr(context, attrs, defStyleAttr);initView();/测试startAnima();}public void startAnima(){ValueAnimator animator=ValueAnimator.ofFloat(0,1.0f);animator.setDuration(1500);animator.setInterpolator(new AccelerateDecelerateInterpolator());animator.setRepeatMode(ValueAnimator.RESTART);animator.setRepeatCount(ValueAnimator.INFINITE);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mProgress = (float) animation.getAnimatedValue();postInvalidate();}});animator.start();}
今天太累了,明天继续写接下来的Viewpager指示器部分,不过到了这里我们也成功了一半了额,因为指示器无非就是在顶部绘制一条线。
附上FadeView全部代码:
package com.cisetech.customer.customer.View;import android.animation.ValueAnimator;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.os.Looper;import android.util.AttributeSet;import android.util.TypedValue;import android.view.View;import android.view.animation.AccelerateDecelerateInterpolator;import com.cisetech.customer.customer.R;/*** Author:Yqy* Date:-07-27* Desc:渐变view* Company:cisetech*/public class FadeView extends View{/*** 需要绘制的文本*/private String mText="测试";/*** 绘制的画笔*/private Paint mPaint;/*** 文本size*/private float mTextSize= TypedValue.applyDimension(PLEX_UNIT_SP,14.5f,getResources().getDisplayMetrics());/*** 未变的text的颜色*/private int originTextColor= Color.BLACK;/*** 变换text的颜色*/private int changeTextColor=Color.RED;/*** text颜色变换的方向,是从左到右还是反之*/private DIRECTION mDirection=DIRECTION.LEFT;/*** 文本绘制的开始位置*/private int mTextStartX;/*** 颜色变换的进度*/private float mProgress;/*** 文字的宽度*/private int mTextWidth;public FadeView(Context context) {this(context, null);}public FadeView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public FadeView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);obtainAttr(context, attrs, defStyleAttr);initView();/测试//startAnima();}public synchronized void setmProgress(float mProgress) {this.mProgress = mProgress;flushView();}public DIRECTION getmDirection() {return mDirection;}public void setmDirection(DIRECTION mDirection) {this.mDirection = mDirection;}private void flushView() {mTextWidth= (int) mPaint.measureText(mText,0,mText.length());if(Looper.getMainLooper()==Looper.myLooper()){invalidate();}else{postInvalidate();}}private void obtainAttr(Context context, AttributeSet attrs, int defStyleAttr) {TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.fadeView, defStyleAttr, 0);for (int i = 0; i <a.getIndexCount() ; i++) {int attr=a.getIndex(i);switch (attr){case R.styleable.fadeView_fadeText:mText=a.getString(attr);break;case R.styleable.fadeView_changeColor:changeTextColor=a.getColor(attr, changeTextColor);break;case R.styleable.fadeView_originColor:originTextColor=a.getColor(attr, originTextColor);break;case R.styleable.fadeView_textSize:mTextSize=a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(PLEX_UNIT_SP, 14.5f, getResources().getDisplayMetrics()));break;case R.styleable.fadeView_direction:int flag = a.getInt(attr, 1);if(flag==1){mDirection=DIRECTION.LEFT;}else{mDirection=DIRECTION.RIGHT;}break;}}a.recycle();}/*** 初始化*/private void initView() {mPaint=new Paint(Paint.ANTI_ALIAS_FLAG);mPaint.setStyle(Paint.Style.FILL);mPaint.setDither(true);mPaint.setTextSize(mTextSize);}public String getmText() {return mText;}public void setmText(String mText) {this.mText = mText;mTextWidth= (int) mPaint.measureText(mText,0,mText.length());postInvalidate();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {mTextWidth= (int) mPaint.measureText(mText,0,mText.length());int width=measureWidth(widthMeasureSpec);int height=measureHeight(heightMeasureSpec);mTextStartX=width/2-mTextWidth/2;setMeasuredDimension(width,height);}/**** @param heightMeasureSpec* @return 测量过后的height*/private int measureHeight(int heightMeasureSpec) {int result;int mode=MeasureSpec.getMode(heightMeasureSpec);int size=MeasureSpec.getSize(heightMeasureSpec);/*** 当为确定值的时候就取确定值,* 当不确定的时候大小就用文本的高度* (int) (mPaint.getFontMetrics().bottom-mPaint.getFontMetrics().top);* 方法可以获取文本宽度*/if(mode==MeasureSpec.EXACTLY){result=size;}else{result= (int) (mPaint.getFontMetrics().bottom-mPaint.getFontMetrics().top);}result=mode==MeasureSpec.AT_MOST?Math.min(result,size):result;return result+getPaddingTop()+getPaddingBottom();}/**** @param widthMeasureSpec* @return 测量过后的width*/private int measureWidth(int widthMeasureSpec) {int result;int mode=MeasureSpec.getMode(widthMeasureSpec);int size=MeasureSpec.getSize(widthMeasureSpec);/*** 当为确定值的时候就取确定值,* 当不确定的时候大小就用文本的大小* mPaint.measureText方法可以获取文本宽度*/if(mode==MeasureSpec.EXACTLY){result=size;}else{result= (int) mPaint.measureText(mText,0,mText.length());}result=mode==MeasureSpec.AT_MOST?Math.min(result,size):result;return result+getPaddingLeft()+getPaddingRight();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);/*** 判断绘制变化的方向,*/if(mDirection==DIRECTION.LEFT){drawChangeLeft(canvas);drawOriginLeft(canvas);}else{drawOriginRight(canvas);drawChangeRight(canvas);}}/*** 当绘制位置为DIRECTION.RIGHT的时候* 绘制变换的部分,也就是红色部分* 裁剪的起始位置为:mTextStartX + ((1-mProgress )* mTextWidth)。* 终止位置为:mTextWidth + mTextStartX*/private void drawChangeRight(Canvas canvas) {drawText(canvas,changeTextColor,(int) (mTextStartX + ((1-mProgress )* mTextWidth)),mTextWidth + mTextStartX);}/*** 当绘制位置为DIRECTION.RIGHT的时候* 绘制未变换的部分,也就是黑色部分* 裁剪的起始位置为:mTextStartX。* 终止位置为:mTextStartX + ((1-mProgress )* mTextWidth)*/private void drawOriginRight(Canvas canvas) {drawText(canvas, originTextColor, mTextStartX, (int) (mTextStartX + ((1 - mProgress) * mTextWidth)));}private void drawOriginLeft(Canvas canvas) {drawText(canvas, originTextColor, (int) (mTextStartX + (mProgress * mTextWidth)), mTextWidth + mTextStartX);}private void drawChangeLeft(Canvas canvas) {drawText(canvas, changeTextColor, mTextStartX, (int) (mTextStartX + (mProgress * mTextWidth)));//drawText(canvas,changeTextColor, mTextStartX, (int) (mTextStartX + (mProgress * mTe)));}/*** 从startX裁剪到endX范围绘制文本*/private void drawText(Canvas canvas,int color, int startX, int endX) {mPaint.setColor(color);canvas.save(Canvas.CLIP_SAVE_FLAG);canvas.clipRect(startX, 0, endX, getMeasuredHeight());/*** 绘制文本的y坐标,看不懂的话可以看我师傅的神作,博客有给出链接*/int baseY= (int) (getMeasuredHeight()/2+(mPaint.getFontMetrics().bottom-mPaint.getFontMetrics().top)/2-mPaint.getFontMetrics().bottom);canvas.drawText(mText,mTextStartX,baseY,mPaint);canvas.restore();}public void startAnima(){ValueAnimator animator=ValueAnimator.ofFloat(0,1.0f);animator.setDuration(1500);animator.setInterpolator(new AccelerateDecelerateInterpolator());animator.setRepeatMode(ValueAnimator.RESTART);animator.setRepeatCount(ValueAnimator.INFINITE);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {mProgress = (float) animation.getAnimatedValue();postInvalidate();}});animator.start();}/*** 枚举类,变换颜色的方向*/public enum DIRECTION{LEFT,RIGHT;}}