Android技巧之drawablePadding設定
1.問題
我想很多小夥伴都和我一樣應該很喜歡TextView中drawableLeft、drawableTop、drawableRight、drawableBottom這幾個屬性,因為我們可以直接用它來畫出來一個圖文排列的標籤或者按鈕,這樣就對於用兩個控制元件組成的相對複雜的佈局來說容易的多,這在移動UI開發中很常用,但是這樣經常會有個問題困擾著我,因為有時候我們想把圖片和文字對應居中,這樣就會出現關於圖片和文字之間的間距不好控制的問題,有時候我們設定drawablePadding這個屬性之後發現也並沒有達到我們想要的效果。
2.原因
大概看了下原始碼實現,得出的結論就是android:drawablePadding這個屬性在 我們給view設定的寬度或者高度足夠小(以至於將兩者擠壓在一起)的時候,這個屬性才會起作用,也即在圖片和文字之間會有間距產生。如果你的view所設定的寬度或者高度大於drawableLeft/drawableRight或者drawableTop/drawableBottom所產生的間距,那麼這個屬性當然也就不會起作用。
3.實踐
一種最簡單方法是我們可以直接去解決,就是通過設定view的內填充,我們從上面原因中可以知道,drawablePadding不起作用是因為view的寬度過寬,導致view內文字和圖片間距過大,那我們可以通過設定paddingLeft、paddingRight、paddingTop、paddingBottom來縮寫這個間距,如下程式碼:
<Button
android:layout_width="wrap_content"
android:layout_height="40dp"
android:text="@string/xian_txt"
android:drawableRight="@mipmap /ic_triangle_down"
android:background="@android:color/transparent"
android:drawablePadding="6dp"
android:gravity="center"
android:paddingRight="24dp"
android:paddingLeft="24dp"
/>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
對應的效果也可以實現
另外,我們也可以通過自定義View來精確的計算:
我們先自定義屬性iconPadding來設定間距,並提供方法給外部呼叫
重寫setCompoundDrawablesWithIntrinsicBounds()方法來獲取我們設定的drawable寬度。
最後重寫onLayout方法,因為這裡面改變了一些位置屬性,需要通過重新佈局才能起作用。
相關程式碼:
public class IconButton extends Button {
private int drawableWidth;
private int iconPadding;
private DrawablePosition position;
Rect bounds;
private enum DrawablePosition{
NONE,
LEFT_AND_RIGHT,
LEFT,
RIGHT
}
public IconButton(Context context) {
this(context,null,0);
}
public IconButton(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public IconButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
bounds = new Rect();
applyAttributes(attrs);
}
protected void applyAttributes(AttributeSet attrs) {
if (null == bounds) {
bounds = new Rect();
}
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.IconButton);
int paddingId = typedArray.getDimensionPixelSize(R.styleable.IconButton_iconPadding, 0);
setIconPadding(paddingId);
typedArray.recycle();
}
public void setIconPadding(int padding) {
iconPadding = padding;
requestLayout();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
Paint textPaint = getPaint();
String text = getText().toString();
textPaint.getTextBounds(text, 0, text.length(), bounds);
int textWidth = bounds.width();
int factor = (position == DrawablePosition.LEFT_AND_RIGHT) ? 2 : 1;
int contentWidth = drawableWidth + iconPadding * factor + textWidth;
int horizontalPadding = (int) ((getWidth() / 2.0) - (contentWidth / 2.0));
setCompoundDrawablePadding(-horizontalPadding + iconPadding);
switch (position) {
case LEFT:
setPadding(horizontalPadding, getPaddingTop(), 0, getPaddingBottom());
break;
case RIGHT:
setPadding(0, getPaddingTop(), horizontalPadding, getPaddingBottom());
break;
case LEFT_AND_RIGHT:
setPadding(horizontalPadding, getPaddingTop(), horizontalPadding, getPaddingBottom());
break;
default:
setPadding(0, getPaddingTop(), 0, getPaddingBottom());
}
}
@Override
public void setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, Drawable right, Drawable bottom) {
super.setCompoundDrawablesWithIntrinsicBounds(left, top, right, bottom);
if (left != null && right != null) {
drawableWidth = left.getIntrinsicWidth() + right.getIntrinsicWidth();
position = DrawablePosition.LEFT_AND_RIGHT;
} else if (left != null) {
drawableWidth = left.getIntrinsicWidth();
position = DrawablePosition.LEFT;
} else if (right != null) {
drawableWidth = right.getIntrinsicWidth();
position = DrawablePosition.RIGHT;
} else {
position = DrawablePosition.NONE;
}
requestLayout();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
這樣同樣可以實現我們想要的效果,並且可以自由設定間距
<com.yuxingxin.iconview.IconButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:gravity="center"
android:drawableRight="@mipmap/ic_triangle_down"
app:iconPadding="6dp"
android:background="@color/colorPrimary"
android:textColor="@android:color/white"
/>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
========== 以下為個人實踐經驗 ================
<1>IconButton 重寫了setCompoundDrawablesWithIntrinsicBounds方法,該方法畫的drawable寬高是按照drawable固定的寬高,有時我們需要在程式碼設定drawable圖片寬高,這時就需要重寫setCompoundDrawables 方法。
如:程式碼中重設drawable寬高
IconTextView itv = (IconTextView)findViewById(R.id.itv_text);
Drawable[] drawables =null;
drawables= itv.getCompoundDrawables();
if (drawables.length==4) {
if (drawables[0]!=null){
drawables[0].setBounds(0, 0, 150, 150);
}
if (drawables[1]!=null){
drawables[1].setBounds(0, 0, 150, 150);
}
if (drawables[2]!=null){
drawables[2].setBounds(0, 0, 150, 150);
}
if (drawables[3]!=null){
drawables[3].setBounds(0, 0, 150, 150);
}
}
itv.setCompoundDrawables(drawables[0], drawables[1], drawables[2], drawables[3]);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
在IconButton 中重寫setCompoundDrawables方法如下:
@Override
public void setCompoundDrawables(Drawable left, Drawable top,
Drawable right, Drawable bottom) {
// TODO Auto-generated method stub
super.setCompoundDrawables(left, top, right, bottom);
if (left != null) {
Rect leftRect = left.getBounds();
leftDrawableWidth = leftRect.right - leftRect.left;
}
if (right != null) {
Rect rightRect = right.getBounds();
rightDrawableWidth = rightRect.right - rightRect.left;
}
if (top != null) {
Rect topRect = top.getBounds();
topDrawableHeight = topRect.bottom - topRect.top;
}
if (bottom != null) {
Rect bottomRect = bottom.getBounds();
bottomDrawableHeight = bottomRect.bottom - bottomRect.top;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
二、設定的drawable足夠小時,IconButton 能夠很好的控制drawablepadding距離,那drawable改多小才行?要小於文字Text的寬/高才行,並且橫向、縱向都設定drawable其實也有問題的,所以IconButton的限制還是有的。
IconButton的onLayout方法如下
Paint textPaint = getPaint();
String text = getText().toString();
textPaint.getTextBounds(text, 0, text.length(), bounds);
int textWidth = bounds.width();
int factor = (position == DrawablePosition.LEFT_AND_RIGHT) ? 2 : 1;
int contentWidth = drawableWidth + iconPadding * factor + textWidth;
int horizontalPadding = (int) ((getWidth() / 2.0) - (contentWidth / 2.0));
setCompoundDrawablePadding(-horizontalPadding + iconPadding);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
值得注意的是,contentWidth是通過圖片寬度+圖片與文字距離+文字寬度功能決定的。
如下圖,如果text都比drawable圖片下,那麼textwidth的距離還是ab線段之間,實際上應該是ac線段之間長度才是正確的,由於topDrawable太大,寬度比text長度要長,自然就加大了text的實際長度。
所以如果需要靈活的設定drawablepadding,還是在容器佈局LinearLayout/RelativeLayout中新增需要的drawable和text是比較合適的,TextView/ButtonView中只適用新增的drawable足夠小,且drawable是同方向的(左右圖片,或是上下圖片),還有值得注意的是,IconButton是通過設定padding的方式確定drawablePadding的,所以使用IconButton時,同方向上的padding不在由使用者決定,IconButton的onLayout方法中重新設定了padding大小。