Android圖文居中顯示控件使用方法詳解
最近項(xiàng)目中用到了文字圖標(biāo)的按鈕,需要居中顯示,如果用TextView實(shí)現(xiàn)的方式,必須同時(shí)設(shè)置padding和drawablePadding。如下:
<androidx.appcompat.widget.AppCompatTextView android:layout_width="200dp" android:layout_height="wrap_content" android:drawableLeft="@drawable/ic_xxx" android:drawablePadding="-60dp" android:minHeight="48dp" android:gravity="center" android:padding="80dp" />
這種方式需要自己做精確計(jì)算。比較麻煩。另外還有一種方式就是用線性布局包裹ImageView和TextView,但這樣會(huì)增加布局層級(jí)。于是自己封裝了一個(gè)控件DrawableCenterTextView。
attrs.xml文件中定義屬性:
<declare-styleable name="DrawableCenterTextView"> <attr name="android:text" /> <attr name="android:textColor" /> <attr name="android:textSize" /> <attr name="android:textStyle" /> <attr name="android:drawablePadding" /> <attr name="android:drawableLeft" /> <attr name="android:drawableTop" /> <attr name="android:drawableRight" /> <attr name="android:drawableBottom" /> </declare-styleable>
對(duì)應(yīng)的Java代碼如下:
public class DrawableCenterTextView extends View { static final int LEFT = 0; static final int TOP = 1; static final int RIGHT = 2; static final int BOTTOM = 3; private CharSequence mText; private ColorStateList mTextColor; private float mTextSize; private int mTextStyle; private int mDrawablePadding; private Drawable[] mCompoundDrawables; private Rect mTextBounds; private Rect mDrawableLeftBounds; private Rect mDrawableTopBounds; private Rect mDrawableRightBounds; private Rect mDrawableBottomBounds; private TextPaint mTextPaint; public DrawableCenterTextView(Context context) { this(context, null); } public DrawableCenterTextView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DrawableCenterTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); Drawable drawableLeft = null, drawableTop = null, drawableRight = null, drawableBottom = null; TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.DrawableCenterTextView, defStyleAttr, 0); mText = ta.getText(R.styleable.DrawableCenterTextView_android_text); mTextColor = ta.getColorStateList(R.styleable.DrawableCenterTextView_android_textColor); mTextSize = ta.getDimensionPixelSize(R.styleable.DrawableCenterTextView_android_textSize, 15); mTextStyle = ta.getInt(R.styleable.DrawableCenterTextView_android_textStyle, 0); drawableLeft = ta.getDrawable(R.styleable.DrawableCenterTextView_android_drawableLeft); drawableTop = ta.getDrawable(R.styleable.DrawableCenterTextView_android_drawableTop); drawableRight = ta.getDrawable(R.styleable.DrawableCenterTextView_android_drawableRight); drawableBottom = ta.getDrawable(R.styleable.DrawableCenterTextView_android_drawableBottom); mDrawablePadding = ta.getDimensionPixelSize(R.styleable.DrawableCenterTextView_android_drawablePadding, 0); ta.recycle(); if (mTextColor == null) { mTextColor = ColorStateList.valueOf(0xFF000000); } mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); mTextPaint.density = getResources().getDisplayMetrics().density; mTextPaint.setTextSize(mTextSize); setTypeface(Typeface.create(Typeface.DEFAULT, mTextStyle)); setCompoundDrawablesWithIntrinsicBounds(drawableLeft, drawableTop, drawableRight, drawableBottom); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width; int height; //計(jì)算文本范圍 calcTextBounds(); if (widthMode == MeasureSpec.EXACTLY) { width = widthSize; } else { width = mTextBounds.width(); if (mCompoundDrawables != null) { if (mCompoundDrawables[TOP] != null) { width = Math.max(width, mDrawableTopBounds.width()); } if (mCompoundDrawables[BOTTOM] != null) { width = Math.max(width, mDrawableBottomBounds.width()); } //加上左右內(nèi)邊距及drawable寬度和drawable間距 width += getCompoundPaddingLeft() + getCompoundPaddingRight(); width = Math.max(width, getSuggestedMinimumWidth()); if (widthMode == MeasureSpec.AT_MOST) { width = Math.min(widthSize, width); } } } if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } else { height = mTextBounds.height(); if (mCompoundDrawables != null) { if (mCompoundDrawables[LEFT] != null) { height = Math.max(height, mDrawableLeftBounds.height()); } if (mCompoundDrawables[RIGHT] != null) { height = Math.max(height, mDrawableRightBounds.height()); } //加上上下內(nèi)邊距及drawable高度和drawable間距 height += getCompoundPaddingTop() + getCompoundPaddingBottom(); height = Math.max(height, getSuggestedMinimumHeight()); if (heightMode == MeasureSpec.AT_MOST) { height = Math.min(heightSize, height); } } } setMeasuredDimension(width, height); } public int getCompoundPaddingTop() { if (mCompoundDrawables == null || mCompoundDrawables[TOP] == null) { return getPaddingTop(); } else { Rect rect = new Rect(); mCompoundDrawables[TOP].copyBounds(rect); return getPaddingTop() + mDrawablePadding + rect.height(); } } public int getCompoundPaddingBottom() { if (mCompoundDrawables == null || mCompoundDrawables[BOTTOM] == null) { return getPaddingBottom(); } else { Rect rect = new Rect(); mCompoundDrawables[BOTTOM].copyBounds(rect); return getPaddingBottom() + mDrawablePadding + rect.height(); } } public int getCompoundPaddingLeft() { if (mCompoundDrawables == null || mCompoundDrawables[LEFT] == null) { return getPaddingLeft(); } else { Rect rect = new Rect(); mCompoundDrawables[LEFT].copyBounds(rect); return getPaddingLeft() + mDrawablePadding + rect.width(); } } public int getCompoundPaddingRight() { if (mCompoundDrawables == null || mCompoundDrawables[RIGHT] == null) { return getPaddingRight(); } else { Rect rect = new Rect(); mCompoundDrawables[RIGHT].copyBounds(rect); return getPaddingRight() + mDrawablePadding + rect.width(); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int vspace = getBottom() - getTop() - getCompoundPaddingBottom() - getCompoundPaddingTop(); //剩余垂直可繪制文本空間大小 int hspace = getRight() - getLeft() - getCompoundPaddingRight() - getCompoundPaddingLeft(); //剩余水平可繪制文本空間大小 if (mCompoundDrawables != null) { if (mCompoundDrawables[LEFT] != null) { canvas.save(); canvas.translate((hspace - mTextBounds.width()) / 2.0f + getPaddingLeft(), getCompoundPaddingTop() + (vspace - mDrawableLeftBounds.height()) / 2.0f); mCompoundDrawables[LEFT].draw(canvas); canvas.restore(); } if (mCompoundDrawables[RIGHT] != null) { canvas.save(); canvas.translate(getRight() - getLeft() - getPaddingRight() - (hspace - mTextBounds.width()) / 2.0f - mDrawableRightBounds.width(), getCompoundPaddingTop() + (vspace - mDrawableRightBounds.height()) / 2.0f); mCompoundDrawables[RIGHT].draw(canvas); canvas.restore(); } if (mCompoundDrawables[TOP] != null) { canvas.save(); canvas.translate(getCompoundPaddingLeft() + (hspace - mDrawableTopBounds.width()) / 2.0f, (vspace - mTextBounds.height()) / 2.0f + getPaddingTop()); mCompoundDrawables[TOP].draw(canvas); canvas.restore(); } if (mCompoundDrawables[BOTTOM] != null) { canvas.save(); canvas.translate(getCompoundPaddingLeft() + (hspace - mDrawableBottomBounds.width()) / 2.0f, getBottom() - getTop() - getPaddingBottom() - (vspace - mTextBounds.height()) / 2.0f - mDrawableBottomBounds.height()); mCompoundDrawables[BOTTOM].draw(canvas); canvas.restore(); } } if (!TextUtils.isEmpty(mText)) { float startX = (hspace - mTextBounds.width()) / 2.0f + getCompoundPaddingLeft(); //因?yàn)閐rawText以baseline為基準(zhǔn),因此需要向下移ascent float startY = (vspace - mTextBounds.height()) / 2.0f + getCompoundPaddingTop() - mTextPaint.getFontMetrics().ascent; mTextPaint.setColor(mTextColor.getColorForState(getDrawableState(), 0)); canvas.drawText(mText, 0, mText.length(), startX, startY, mTextPaint); } } @Override protected void drawableStateChanged() { super.drawableStateChanged(); if (mTextColor != null && mTextColor.isStateful()) { mTextPaint.setColor(mTextColor.getColorForState(getDrawableState(), 0)); } if (mCompoundDrawables != null) { final int[] state = getDrawableState(); for (Drawable dr : mCompoundDrawables) { if (dr != null && dr.isStateful() && dr.setState(state)) { invalidateDrawable(dr); } } } } public void setCompoundDrawablesWithIntrinsicBounds(@Nullable Drawable left, @Nullable Drawable top, @Nullable Drawable right, @Nullable Drawable bottom) { if (left != null) { left.setBounds(0, 0, left.getIntrinsicWidth(), left.getIntrinsicHeight()); } if (right != null) { right.setBounds(0, 0, right.getIntrinsicWidth(), right.getIntrinsicHeight()); } if (top != null) { top.setBounds(0, 0, top.getIntrinsicWidth(), top.getIntrinsicHeight()); } if (bottom != null) { bottom.setBounds(0, 0, bottom.getIntrinsicWidth(), bottom.getIntrinsicHeight()); } setCompoundDrawables(left, top, right, bottom); } public void setCompoundDrawables(@Nullable Drawable left, @Nullable Drawable top, @Nullable Drawable right, @Nullable Drawable bottom) { if (mCompoundDrawables == null) { mCompoundDrawables = new Drawable[4]; } else { if (mCompoundDrawables[LEFT] != null && mCompoundDrawables[LEFT] != left) { mCompoundDrawables[LEFT].setCallback(null); } if (mCompoundDrawables[TOP] != null && mCompoundDrawables[TOP] != top) { mCompoundDrawables[TOP].setCallback(null); } if (mCompoundDrawables[RIGHT] != null && mCompoundDrawables[RIGHT] != right) { mCompoundDrawables[RIGHT].setCallback(null); } if (mCompoundDrawables[BOTTOM] != null && mCompoundDrawables[BOTTOM] != bottom) { mCompoundDrawables[BOTTOM].setCallback(null); } } if (left != null) { mDrawableLeftBounds = new Rect(); left.copyBounds(mDrawableLeftBounds); left.setCallback(this); mCompoundDrawables[LEFT] = left; } else { mCompoundDrawables[LEFT] = null; } if (top != null) { mDrawableTopBounds = new Rect(); top.copyBounds(mDrawableTopBounds); top.setCallback(this); mCompoundDrawables[TOP] = top; } else { mCompoundDrawables[TOP] = null; } if (right != null) { mDrawableRightBounds = new Rect(); right.copyBounds(mDrawableRightBounds); right.setCallback(this); mCompoundDrawables[RIGHT] = right; } else { mCompoundDrawables[RIGHT] = null; } if (bottom != null) { mDrawableBottomBounds = new Rect(); bottom.copyBounds(mDrawableBottomBounds); bottom.setCallback(this); mCompoundDrawables[BOTTOM] = bottom; } else { mCompoundDrawables[BOTTOM] = null; } invalidate(); requestLayout(); } public void setText(CharSequence text) { this.mText = text; invalidate(); requestLayout(); } public void setTextSize(float textSize) { this.mTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, textSize, getResources().getDisplayMetrics()); invalidate(); requestLayout(); } public void setTextColor(@ColorInt int textColor) { this.mTextColor = ColorStateList.valueOf(textColor); invalidate(); } public void setTypeface(@Nullable Typeface tf) { if (mTextPaint.getTypeface() != tf) { mTextPaint.setTypeface(tf); requestLayout(); invalidate(); } } private void calcTextBounds() { mTextBounds = new Rect(); if (TextUtils.isEmpty(mText)) return; if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) { mTextPaint.getTextBounds(mText, 0, mText.length(), mTextBounds); } else { int width = (int) Math.ceil(mTextPaint.measureText(mText.toString())); Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics(); int height = (int) Math.ceil(fontMetrics.descent - fontMetrics.ascent); mTextBounds.set(0, 0, width, height); } } }
感謝大家的支持,如有錯(cuò)誤請(qǐng)指正。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Kotlin?協(xié)程的取消機(jī)制詳細(xì)解讀
這篇文章主要為大家介紹了Kotlin?協(xié)程的取消機(jī)制詳細(xì)解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10深入解讀Android開(kāi)發(fā)中Activity的生命周期
這篇文章主要介紹了Android開(kāi)發(fā)中Activity的生命周期,包括Activity的停止和銷毀等重要內(nèi)容,非常推薦!需要的朋友可以參考下2015-12-12Android平臺(tái)生成二維碼并實(shí)現(xiàn)掃描 & 識(shí)別功能
這篇文章主要介紹了Android平臺(tái)生成二維碼并實(shí)現(xiàn)掃描 & 識(shí)別功能的相關(guān)資料,需要的朋友可以參考下2016-06-06Android開(kāi)發(fā)中簡(jiǎn)單設(shè)置啟動(dòng)界面的方法
這篇文章主要介紹了Android開(kāi)發(fā)中簡(jiǎn)單設(shè)置啟動(dòng)界面的方法,涉及Android界面布局、加載、跳轉(zhuǎn)等相關(guān)操作技巧,需要的朋友可以參考下2018-01-01android ListView內(nèi)數(shù)據(jù)的動(dòng)態(tài)添加與刪除實(shí)例代碼
ListView內(nèi)數(shù)據(jù)的動(dòng)態(tài)添加與刪除2013-03-03