這篇文章主要為大家展示了Android如何使用自定義View中Spannable,內(nèi)容簡而易懂,希望大家可以學(xué)習(xí)一下,學(xué)習(xí)完之后肯定會有收獲的,下面讓小編帶大家一起來看看吧。
為鐵東等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計制作服務(wù),及鐵東網(wǎng)站建設(shè)行業(yè)解決方案。主營業(yè)務(wù)為網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計、鐵東網(wǎng)站設(shè)計,以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會得到認(rèn)可,從而選擇與我們長期合作。這樣,我們也可以走得更遠(yuǎn)!
我們都知道 Android 中使用 Spannable 可以實現(xiàn) TextView 富文本的顯示,但是在自定義控件中如何使用 Spannable 繪制不同樣式的文字呢?
例如這種效果,標(biāo)題中的 分?jǐn)?shù)字61
是粗體,分
是常規(guī)字體,并且相對于 61
更小些。
第一反應(yīng)可能是使用 SpannableString.setSpan()
設(shè)置 RelativeSizeSpan
, 然后在 onDraw()
中進(jìn)行繪制,事實是這樣實現(xiàn)是沒有效果的,因為 onDraw()
中只能獲取到 SpannableString
中的內(nèi)容,拿不到 Span
.
那如何在自定義View 中使用 Spannable 呢? 答案就是系統(tǒng)提供的 Layout
類,
/** * A base class that manages text layout in visual elements on * the screen. *For text that will be edited, use a {@link DynamicLayout}, * which will be updated as the text changes. * For text that will not change, use a {@link StaticLayout}. */ public abstract class Layout { }
可以看到 Layout
是一個抽象類,有三個子類,可以實現(xiàn)一些自動換行的顯示效果。
實現(xiàn)代碼
1. 定義自定義屬性
<?xml version="1.0" encoding="utf-8"?>
2. 繼承 View, 在 onDraw()
中繪制
public class ArcProgressView extends View { private int arcBackgroundColor; // 圓弧背景顏色 private int arcProgressColor; // 圓弧進(jìn)度顏色 private int arcSubTitleColor; // 副標(biāo)題顏色 private float arcStrokeWidth; // 圓弧線的厚度 private float arcTitleTextSize; // 標(biāo)題文字大小 private float arcSubTitleTextSize; // 副標(biāo)題文字大小 private float arcProgress; // 進(jìn)度 private int arcTitleNumber; // 值 private Paint paint; private float centerX; private float centerY; private float radius; // 半徑 private RectF rectF; private int startAngle = 135; private int sweepAngle = 270; private String subTitle = "1月份"; private SpannableString spannableString; private TextPaint textPaint; private RelativeSizeSpan relativeSizeSpan; private DynamicLayout dynamicLayout; private String text = "11分"; private StyleSpan styleSpan; private float curProgress; // 當(dāng)前進(jìn)度 private int curNumber; public ArcProgressView(Context context) { this(context, null); } public ArcProgressView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public ArcProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); readAttrs(context, attrs); init(context); } private void readAttrs(Context context, AttributeSet attributeSet) { TypedArray typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.ArcProgressView); arcBackgroundColor = typedArray.getColor(R.styleable.ArcProgressView_arcBackgroundColor, 0x1c979797); arcProgressColor = typedArray.getColor(R.styleable.ArcProgressView_arcProgressColor, 0xff3372FF); arcSubTitleColor = typedArray.getColor(R.styleable.ArcProgressView_arcSubTitleColor, 0x66000000); arcStrokeWidth = typedArray.getDimensionPixelSize(R.styleable.ArcProgressView_arcStrokeWidth, dp2px(5)); arcTitleTextSize = typedArray.getDimensionPixelSize(R.styleable.ArcProgressView_arcTitleTextSize, dp2px(30)); arcSubTitleTextSize = typedArray.getDimensionPixelSize(R.styleable.ArcProgressView_arcSubTitleTextSize, dp2px(14)); arcProgress = typedArray.getFloat(R.styleable.ArcProgressView_arcProgress, 1.0f); arcTitleNumber = typedArray.getInt(R.styleable.ArcProgressView_arcTitleNumber, 100); typedArray.recycle(); } private void init(Context context) { paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setStrokeCap(Paint.Cap.ROUND); relativeSizeSpan = new RelativeSizeSpan(0.6f); styleSpan = new StyleSpan(android.graphics.Typeface.BOLD); textPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); textPaint.setColor(arcProgressColor); // textPaint.setTextAlign(Paint.Align.CENTER); // 設(shè)置該屬性導(dǎo)致文字間有間隔 textPaint.setTextSize(sp2px(22)); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); centerX = w / 2f; centerY = h / 2f; radius = (Math.min(w, h) - arcStrokeWidth) / 2f; rectF = new RectF(-radius, -radius, radius, radius); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = getMeasuredSize(widthMeasureSpec, dp2px(100)); int height = getMeasuredSize(heightMeasureSpec, dp2px(100)); setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 繪制圓弧和進(jìn)度 drawArc(canvas); // 繪制文字 title drawTitleText(canvas); // 繪制文字副標(biāo)題 drawSubTitle(canvas); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); startAnimation(); } private void startAnimation() { ValueAnimator progressAnimator = ValueAnimator.ofFloat(0f, arcProgress); ValueAnimator numberAnimator = ValueAnimator.ofInt(0, arcTitleNumber); progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { curProgress = (float) animation.getAnimatedValue(); invalidate(); } }); numberAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { curNumber = (int) animation.getAnimatedValue(); text = curNumber + "分"; } }); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether(progressAnimator, numberAnimator); animatorSet.setDuration(700); animatorSet.setInterpolator(new LinearInterpolator()); animatorSet.start(); } private void drawSubTitle(Canvas canvas) { canvas.save(); canvas.translate(centerX, centerY); paint.setTextSize(arcSubTitleTextSize); paint.setTextAlign(Paint.Align.CENTER); paint.setColor(arcSubTitleColor); paint.setStyle(Paint.Style.FILL); paint.setStrokeWidth(0); canvas.drawText(subTitle, 0, 60, paint); canvas.restore(); } private void drawArc(Canvas canvas) { canvas.save(); canvas.translate(centerX, centerY); paint.setColor(arcBackgroundColor); paint.setStrokeWidth(arcStrokeWidth); paint.setStyle(Paint.Style.STROKE); canvas.drawArc(rectF, startAngle, sweepAngle, false, paint); paint.setColor(arcProgressColor); canvas.drawArc(rectF, startAngle, sweepAngle * curProgress, false, paint); canvas.restore(); } private void drawTitleText(Canvas canvas) { canvas.save(); textPaint.setTextSize(arcTitleTextSize); float textWidth = textPaint.measureText(text); // 文字寬度 float textHeight = -textPaint.ascent() + textPaint.descent(); // 文字高度 // 由于 StaticLayout 繪制文字時,默認(rèn)畫在Canvas的(0,0)點(diǎn)位置,所以居中繪制居中位置,需要將畫布 translate到中間位置。 canvas.translate(centerX - textWidth * 2 / 5f, centerY - textHeight * 2 / 3f); spannableString = SpannableString.valueOf(text); spannableString.setSpan(styleSpan, 0, text.length() - 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); spannableString.setSpan(relativeSizeSpan, text.length() - 1, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); dynamicLayout = new DynamicLayout(spannableString, textPaint, getWidth(), Layout.Alignment.ALIGN_NORMAL, 0, 0, false); dynamicLayout.draw(canvas); canvas.restore(); } /** * 對外提供方法,設(shè)置進(jìn)度 * * @param percent */ public void setArcProgress(float percent) { this.curProgress = percent; invalidate(); } private int getMeasuredSize(int measureSpec, int defvalue) { int mode = MeasureSpec.getMode(measureSpec); int size = MeasureSpec.getSize(measureSpec); if (mode == MeasureSpec.EXACTLY) { return size; } return Math.min(size, defvalue); } private int dp2px(int dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics()); } private int sp2px(int sp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics()); } }
3. 在布局中引用
以上就是關(guān)于Android如何使用自定義View中Spannable的內(nèi)容,如果你們有學(xué)習(xí)到知識或者技能,可以把它分享出去讓更多的人看到。