26
2017
09

Android之文字描边

结果是最不重要的,重要的是过程
* [问题] 本文主要解决白色背景下,白色的文字看不见的问题
* [解决思路] 给文字加阴影,ios下很好解决,苹果提供了很好 View.layer.shadow**属性,奈何android虽然提供了,但是效果不好, 解决方案是利用TextView在onDraw的时候,获取到画笔,先进行一次比默认大小的文字内容稍微大一点的绘制,然后再进行一次默认大小的文字内容的绘制,这样就产生出了描边效果
* [效果图] 这里写图片描述 这里写图片描述
* [核心代码分析] 这个方案可以使用在自定义view时加阴影,如果你画一个矩形,就可以使用这段代码加一圈阴影

    // 绘制阴影,使用空心画笔,宽度加宽
    mPaint.setColor(mShadowColor);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(3);
    mPaint.setFakeBoldText(true); // 外层text采用粗体
    // todo 此处需要执行绘制操作

    // 正常绘制,恢复画笔
    mPaint.setStyle(Paint.Style.FILL);
    mPaint.setStrokeWidth(0);
    mPaint.setFakeBoldText(false);

比如画阴影矩形

 private void drawInternal(Canvas canvas) {
        drawShadow(canvas, mUpBarRect);

        mPaint.setColor(mColor);
        canvas.drawRoundRect(mUpBarRect, mBarRoundX, mBarRoundY, mPaint);
    }

    private void drawShadow(Canvas canvas, RectF rect) {
        mPaint.setColor(mShadowColor);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(3);
        mPaint.setFakeBoldText(true); // 外层text采用粗体
        canvas.drawRoundRect(rect, mBarRoundX, mBarRoundY, mPaint);

        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(0);
        mPaint.setFakeBoldText(false);
    }

附上完整代码:

/** * TextView 文字描边 * 是利用TextView在onDraw的时候,获取到画笔,先进行一次比默认大小的文字内容稍微大一点的绘制,然后再进行一次默认大小的文字内容的绘制,这样就产生出了描边效果 * Created by slack * on 17/9/6 下午3:43 */

public class WrapTextView extends AppCompatTextView {

    private @ColorInt int mBorderColor = Color.GRAY;
    private @ColorInt int mInnerColor = Color.WHITE;
    private boolean mBorderText = true;
    private TextPaint mTextPaint;

    public WrapTextView(Context context) {
        this(context, null);
    }

    public WrapTextView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, android.R.attr.textViewStyle);
    }

    public WrapTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mTextPaint = this.getPaint();
        mInnerColor = getCurrentTextColor();
    }

    /** * onDraw draw border first */
    @Override
    protected void onDraw(Canvas canvas) {
        if(mBorderText) {
            // 描外层
            setTextColorUseReflection(mBorderColor);
            mTextPaint.setStrokeWidth(2); // 描边宽度
            mTextPaint.setStyle(Paint.Style.STROKE); // 画笔空心
            mTextPaint.setFakeBoldText(true); // 外层text采用粗体
            super.onDraw(canvas);

            // 描内层,恢复原先的画笔
            setTextColorUseReflection(mInnerColor);
            mTextPaint.setStrokeWidth(0);
            mTextPaint.setStyle(Paint.Style.FILL);
            mTextPaint.setFakeBoldText(false);
        }
        super.onDraw(canvas);
    }

    /** * 使用反射的方法进行字体颜色的设置 * 其实就是 this.setTextColor(); 但是这个系统方法里会调用invalidate(); 最终的效果导致这段代码无限循环 * 反射部分: * 1. Class.getDeclaredField(String name); 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段(包括私有成员) * 2. 获取私有属性的时候必须先设置Accessible为true,然后才能获取 * 3. 通过Field.get(Object obj)获取属性的值,通过Field.set(Object obj,value)重新设置新的属性值 */
    private void setTextColorUseReflection(int color) {
        Field textColorField;
        try {
            textColorField = TextView.class.getDeclaredField("mCurTextColor");
            textColorField.setAccessible(true);
            textColorField.set(this, color);
            textColorField.setAccessible(false);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        mTextPaint.setColor(color);
    }
}
上一篇:Toolbar使用详解 下一篇:Android解析ClassLoader(一)Java中的ClassLoader