软件编程
位置:首页>> 软件编程>> Android编程>> Android图文居中显示控件使用方法详解

Android图文居中显示控件使用方法详解

作者:折翅鵬  发布时间:2023-04-04 14:07:24 

标签:Android,图文居中

最近项目中用到了文字图标的按钮,需要居中显示,如果用TextView实现的方式,必须同时设置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" />

这种方式需要自己做精确计算。比较麻烦。另外还有一种方式就是用线性布局包裹ImageView和TextView,但这样会增加布局层级。于是自己封装了一个控件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>

对应的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;

//计算文本范围
 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());
   }

//加上左右内边距及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());
   }

//加上上下内边距及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();
  //因为drawText以baseline为基准,因此需要向下移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);
 }
}
}

感谢大家的支持,如有错误请指正。

来源:https://blog.csdn.net/szhupeng/article/details/108392513

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com