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