Android

Android 实现一个文字左右不同颜色

2017-08-11  本文已影响313人  水言

偶然看到一个文字用两种颜色显示,这个效果最初是在魅族的应用市场看到的,效果如下:

效果图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

上一篇 下一篇

猜你喜欢

热点阅读