标题带"精""热"。ReplacementSpan实践

今天看博客看到一篇TextView里画世界——ReplacementSpan实践   

        标题带就是标题带小图片的这种

以前讨论群里也有人问过几次,群里的大神总是点到为止用SpannableString。

确实SpannableString能用图片替代字符实现该效果。

而这篇文章使用了ReplacementSpan自定义span达到实现效果  兴趣使然还没用过ReplacementSpan就开始试试

结果我水平不够看的云里雾里,在原基础上修改字体大小高度还会出现整个span失效

然后我就自己搞一个


首先自定义span要继承ReplacementSpan重写两个方法

第一个

public int getSize(Paint paint, CharSequence charSequence, intstart, intend, Paint.FontMetricsInt fontMetricsInt){

return 整个自定义长度;

}

也就是返回值就是自定义span的长度(这个返回长度不够会显示不全)

参数有 paint画笔 文字信息charSequence 开始和结束位置start,end  Paint.FontMetricsInt包含文字的属性值 top,ascent等

第二个

public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint){

//绘制

}

参数有 画布,文字,span开始结束位置,上下左位置,y是文字基准线位置,画笔


感觉过去和自定义view有点像  好像比自定义view简单。


要实现效果就是背景绘制方块然后往其中写字 .java文件名是IconTextSpan.java

在构造函数中传入context和文本text

public IconTextSpan ( Context context,String text){
        this.mContext = context;
        this.mText = text;
        initPaint();
    }
initPaint()初始化背景画笔和文字画笔

public void initPaint(){
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(mContext.getResources().getColor(android.R.color.holo_blue_light));
        mPaint.setAntiAlias(true);
        textPaint = new TextPaint();
        textPaint.setColor(mContext.getApplicationContext().getResources().getColor(android.R.color.white));
        mTextSize = changeintTopx(20);
        textPaint.setTextSize(mTextSize);
        textPaint.setAntiAlias(true);
    }

changeintTopx()是提出来的函数 吧字体大小sp转成px

public float changeintTopx( int i){
        float pxsize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, i, mContext.getResources().getDisplayMetrics());
        return pxsize;
    }

准备工作做完来重写getSize()

这里通过Paint.getTextBounds(字符串文本,诠释开始,诠释结束,矩形边界)和Rect.width

得到字体大小在mTextSize时mText的长度px

public int getSize(@NonNull Paint paint, CharSequence charSequence, int i, int i1, @Nullable Paint.FontMetricsInt fontMetricsInt) {
        Rect textRect = new Rect();
        Paint apaint = new Paint();
        apaint.setTextSize(mTextSize);
        apaint.getTextBounds(mText, 0, mText.length(), textRect);
        float padding = changeintTopx(4);
        return mWidth = (int)(textRect.width() + padding * 2);
    }
如此一来span影响的文字必定会占据mwidth像素长度的位置,后续的没被影响的文字将会被往后推

然后来画draw()

public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
        Paint.FontMetrics metrics = paint.getFontMetrics();
        float textHeight = metrics.descent - metrics.ascent;
        RectF bgRect = new RectF(x, top, x + mWidth, bottom);
        canvas.drawRoundRect(bgRect, changeintTopx(3), changeintTopx(3), mPaint);//圆角背景图
        canvas.drawText(mText, x+changeintTopx(4), (y-(bottom-top-textHeight)/2), textPaint);//文字
    }
这里做了一个小处理 本来draw()只要写出

RectF bgRect = new RectF(x, top, x + mWidth, bottom);
canvas.drawRoundRect(bgRect, changeintTopx(3), changeintTopx(3), mPaint);//圆角背景图
canvas.drawText(mText, x, y, textPaint);//文字

就已经能看出效果了

标题带
恩就是文字靠在最角落 所以使用

Paint.FontMetrics metrics = paint.getFontMetrics();
float textHeight 

来计算drawText()的绘制位置 为了让他能居中

FontMetrics 是文字测量属性集合,文字的绘制居然有这么多小内容
标题带
这里主要要看

基准点是baseline
Ascent是baseline之上至字符最高处的距离
Descent是baseline之下至字符最低处的距离

FontMetrics 文字测量集合以baseline的位置是y=0 

metrics.descent得到正数 metrics.ascent等于负数

 由图可知textHeight  = metrics.descent - metrics.ascent;

然后推出canvas.drawText(mText, x+changeintTopx(4), (y-(bottom-top-textHeight)/2), textPaint);//文字

 x轴位置设为x+changeintTopx(4)是因为

前面getsize()时多返回了2*changeintTopx(4)做圆角缓冲

这样自定义span基本完成



然后就是在文件中使用

  String content = "Android是一种基于Linux的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由Google公司和开放手机联盟领导及开发。尚未有统一中文名称,中国大陆地区较多人使用“安卓”或“安致”。";
        StringBuilder stringBuilder = new StringBuilder();
IconTextSpan iconTextSpan = new IconTextSpan(activity.getApplication(),"热");
        iconTextSpan.setbackgroudcolor(R.color.colorPrimaryDark);//主动更改span背景颜色
stringBuilder.append(" ");
stringBuilder.append(content);
        SpannableString spannableString = new SpannableString(stringBuilder.toString());
spannableString.setSpan(iconTextSpan,0,1,  Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

最后分粗糙的长这样


另外可以自己补充方法函数进去如设置字体大小背景颜色等(我就添加setbackgroudcolor,settextcolor,settextsize)

 在setSpan()调用之前自定义span不会调用getsize()和draw() 所以在此之前都能更改

附点另一个大神展示的好几种自定义span样式

和我的整个代码

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextPaint;
import android.text.style.ReplacementSpan;
import android.util.TypedValue;

/**
 * Created by ssh on 2017/11/27.
 */

public class IconTextSpan extends ReplacementSpan {
    private Context mContext;
    private String mText;  //Icon内文字
    private float mTextSize; //文字大小
    private  Paint mPaint;
    private int mWidth;
    private float mRightMargin;
    private TextPaint textPaint;
    public IconTextSpan ( Context context,String text){
        this.mContext = context;
        this.mText = text;
        initPaint();
    }
    public void initPaint(){
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(mContext.getResources().getColor(android.R.color.holo_blue_light));
        mPaint.setAntiAlias(true);
        textPaint = new TextPaint();
        textPaint.setColor(mContext.getApplicationContext().getResources().getColor(android.R.color.white));
        mTextSize = changeintTopx(20);
        textPaint.setTextSize(mTextSize);
        textPaint.setAntiAlias(true);
    }

    /**
     * 设置背景颜色
     * @param backgroudcolor
     */
    public void setbackgroudcolor(int backgroudcolor){
        mPaint.setColor(mContext.getResources().getColor(backgroudcolor));
    }

    /**
     * 设置字体颜色
     * @param textcolor
     */
    public void settextcolor(int textcolor){
        textPaint.setColor(mContext.getResources().getColor(textcolor));
    }

    /**
     * 设置右边距
     *
     * @param rightMarginDpValue
     */
    public void setRightMarginDpValue(int rightMarginDpValue) {
        this.mRightMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, rightMarginDpValue, mContext.getResources().getDisplayMetrics());
    }

    /**
     * 设置字体大小
     * @param textSize
     */
    public void setTextSize(int textSize){
        this.mTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, textSize, mContext.getResources().getDisplayMetrics());
        textPaint.setTextSize(mTextSize);
    }

    @Override
    public int getSize(@NonNull Paint paint, CharSequence charSequence, int i, int i1, @Nullable Paint.FontMetricsInt fontMetricsInt) {
        Rect textRect = new Rect();
        Paint apaint = new Paint();
        apaint.setTextSize(mTextSize);
        apaint.getTextBounds(mText, 0, mText.length(), textRect);
        float padding = changeintTopx(4);
        return mWidth = (int)(textRect.width() + padding * 2);
    }

    @Override
    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
        Paint.FontMetrics metrics = paint.getFontMetrics();
        float textHeight = metrics.descent - metrics.ascent;
        RectF bgRect = new RectF(x, top, x + mWidth, bottom);
        canvas.drawRoundRect(bgRect, changeintTopx(3), changeintTopx(3), mPaint);
        canvas.drawText(mText, x+changeintTopx(4), (y-(bottom-top-textHeight)/2), textPaint);
    }

    public float changeintTopx( int i){
        float pxsize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, i, mContext.getResources().getDisplayMetrics());
        return pxsize;
    }
}


你可能感兴趣的:(标题带"精""热"。ReplacementSpan实践)