偶然看到一个文字用两种颜色显示,这个效果最初是在魅族的应用市场看到的,效果如下:
效果图1第一次看到的时候就觉得很好奇,于是就想着怎么实现,首先从原生控件的角度出发想了想好像没有合适的方法,百度找资料也没找到合适的,于是就想着自己实现下。
最后实现的效果图:
效果图2文本绘制
文本绘制这个功能的实现主要是用了系统文本绘制的api:
canvas.drawText(mText, //文本内容
mTextStartX, //文本绘制开始位置X,Y
getMeasuredHeight() / 2 - ((mPaint.descent() + mPaint.ascent()) / 2),
mPaint); //画笔
如何使得同一个文本有两种颜色
实现的原理就是分开两个区域单独用不同的颜色的画笔绘制文本,左边区域绘制颜色A文本,右边区域绘制颜色B文本。
绘制文本的方法:
//参数 画布,画笔颜色、开始和结束的X
private void drawText_h(Canvas canvas, int color, int startX, int endX) {
mPaint.setColor(color);//画笔颜色
canvas.save();
//设置画布的显示区域 ,默认交集显示
canvas.clipRect(startX, 0, endX, getMeasuredHeight());// left, top,right, bottom
//正常绘制文本,但是由于画布clip一个固定的范围,实际绘制的文本就在范围中
canvas.drawText(mText,
mTextStartX,
//mPaint.descent() + mPaint.ascent()文本高度
getMeasuredHeight() / 2 - ((mPaint.descent() + mPaint.ascent()) / 2),
mPaint);
canvas.restore();
}
❑ save:用来保存Canvas的状态。save之后,可以调用Canvas的平移、放缩、旋转、错切、裁剪等操作。
❑ restore:用来恢复Canvas之前保存的状态。防止save后对Canvas执行的操作对后续的绘制有影响。
执行绘制左右两边文本的代码如下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//左边文本绘制
drawText_h(canvas,
mTextChangeColor,
mTextStartX, //文本开始位置
(int) (mTextStartX + mProgress * mTextWidth)); //progress是需要绘制颜色A的范围
//右边文本绘制
drawText_h(canvas,
mTextOriginColor,
(int) (mTextStartX + mProgress * mTextWidth),
mTextStartX + mTextWidth);
}
控件大小、文本开始位置控制
1.首先确定文本区域
//测量文本内容的宽高
private void measureText() {
mTextWidth = (int) mPaint.measureText(mText);
//文本长度超过屏幕长度,截取前部分,(当然可以换行,实现原理一样过程麻烦点)
if(mTextWidth>screenWidth){
ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) getLayoutParams();
int length=(int)(((float)(screenWidth-mlp.bottomMargin-mlp.topMargin)/(float)mTextWidth)*mText.length());
mText=mText.substring(0,length);
mTextWidth = (int) mPaint.measureText(mText);
}
Paint.FontMetrics fm = mPaint.getFontMetrics();
mTextHeight = (int) Math.ceil(fm.descent - fm.top);
mPaint.getTextBounds(mText, 0, mText.length(), mTextBound);
mTextHeight = mTextBound.height();
}
2.确定控件大小
控件的测绘模式
MeasureSpec.EXACTLY是精确尺寸,当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width="50dip",或者为MATCh_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。
MeasureSpec.AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。
MeasureSpec.UNSPECIFIED是未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。
//重新计算所需的高度
private int measureHeight(int measureSpec) {
int mode = MeasureSpec.getMode(measureSpec); //控件的测绘模式
int val = MeasureSpec.getSize(measureSpec); //控件大小
int result = 0;
switch (mode) {
case MeasureSpec.EXACTLY:
result = val;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED:
//
result = mTextHeight;
result += getPaddingTop() + getPaddingBottom();
break;
}
result = mode == MeasureSpec.AT_MOST ? Math.min(result, val) : result;
return result;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureText();
//从新测量控件的大小,确保在没有指定控件大小的情况下正常显示
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec);
setMeasuredDimension(width, height);
//确保文本居中显示,获得文本的开始位置
mTextStartX = getMeasuredWidth() / 2 - mTextWidth / 2;
mTextStartY = getMeasuredHeight() / 2 - mTextHeight / 2;
}
自定义控件状态保存
在横竖屏切换等操作导致Activity重新创建的情况保存数据。
private static final String PROGRESS = "progress";
private static final String STATE = "state";
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putFloat(PROGRESS, mProgress);
bundle.putParcelable(STATE, super.onSaveInstanceState());
return bundle;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
mProgress = bundle.getFloat(PROGRESS);
super.onRestoreInstanceState(bundle.getParcelable(STATE));
return;
}
super.onRestoreInstanceState(state);
}
最后附上多颜色文本和下载进度的demo,供参考:
项目地址:链接:http://pan.baidu.com/s/1geDhtWR 密码:k820
参考:
http://blog.csdn.net/lmj623565791/article/details/44098729
网友评论