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

来源:互联网 发布:免费电子期刊制作软件 编辑:程序博客网 时间:2024/06/03 14:53

今天看博客看到一篇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;    }}