您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了Android如何使用自定義View中Spannable,內(nèi)容簡(jiǎn)而易懂,希望大家可以學(xué)習(xí)一下,學(xué)習(xí)完之后肯定會(huì)有收獲的,下面讓小編帶大家一起來(lái)看看吧。
我們都知道 Android 中使用 Spannable 可以實(shí)現(xiàn) TextView 富文本的顯示,但是在自定義控件中如何使用 Spannable 繪制不同樣式的文字呢?
例如這種效果,標(biāo)題中的 分?jǐn)?shù)字61
是粗體,分
是常規(guī)字體,并且相對(duì)于 61
更小些。
第一反應(yīng)可能是使用 SpannableString.setSpan()
設(shè)置 RelativeSizeSpan
, 然后在 onDraw()
中進(jìn)行繪制,事實(shí)是這樣實(shí)現(xiàn)是沒(méi)有效果的,因?yàn)?onDraw()
中只能獲取到 SpannableString
中的內(nèi)容,拿不到 Span
.
那如何在自定義View 中使用 Spannable 呢? 答案就是系統(tǒng)提供的 Layout
類,
/** * A base class that manages text layout in visual elements on * the screen. * <p>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
是一個(gè)抽象類,有三個(gè)子類,可以實(shí)現(xiàn)一些自動(dòng)換行的顯示效果。
實(shí)現(xiàn)代碼
1. 定義自定義屬性
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="ArcProgressView"> <attr name="arcBackgroundColor" format="color" /> <attr name="arcProgressColor" format="color" /> <attr name="arcSubTitleColor" format="color" /> <attr name="arcStrokeWidth" format="dimension" /> <attr name="arcTitleTextSize" format="dimension" /> <attr name="arcSubTitleTextSize" format="dimension" /> <attr name="arcProgress" format="float" /> <attr name="arcTitleNumber" format="integer" /> </declare-styleable> </resources>
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 繪制文字時(shí),默認(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(); } /** * 對(duì)外提供方法,設(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. 在布局中引用
<com.xing.bottomsheetsample.ArcProgressView android:layout_width="match_parent" android:layout_height="100dp" android:layout_marginTop="20dp" app:arcProgress="0.6" app:arcSubTitleTextSize="14sp" app:arcTitleNumber="61" app:arcTitleTextSize="28sp" />
以上就是關(guān)于Android如何使用自定義View中Spannable的內(nèi)容,如果你們有學(xué)習(xí)到知識(shí)或者技能,可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。