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"
    />

對應的效果也可以實現
這裏寫圖片描述

另外,我們也可以通過自定義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();
    }
}

這樣同樣可以實現我們想要的效果,並且可以自由設置間距

<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"
    />

轉載原文鏈接
轉載原文demo下載地址

========== 以下爲個人實踐經驗 ================

<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]);

在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;
        }
    }

二、設置的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);

值得注意的是,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大小。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章