1. 程式人生 > >重寫Button實現圖片drawableTop和文字一起居中

重寫Button實現圖片drawableTop和文字一起居中

很多情況下,我們需要這樣的Button。


當然是可以通過建立一個LinearLayout來擺放一個ImageView和TextView來實現。這裡就不說這種方法了。
還有一個實現方法是通過下面標籤實現:
android:drawableLeft,
android:drawableRight,
android:drawableTop,
android:drawableBottom;

可是通過上面的標籤來實現的話,圖片會被貼著邊緣擺放,如下圖,這樣沒辦法跟文字一起居中。


這裡我們可以通過重寫Button的onDraw(Canvas canvas) 方法,把圖片和文字一起居中。

public class DrawableHorizontalButton extends Button {

	public DrawableHorizontalButton(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		canvas = getTopCanvas(canvas);
		super.onDraw(canvas);
	}

	private Canvas getTopCanvas(Canvas canvas) {
		Drawable[] drawables = getCompoundDrawables();
		if (drawables == null) {
			return canvas;
		}
		Drawable drawable = drawables[0];// 左面的drawable
		if (drawable == null) {
			drawable = drawables[2];// 右面的drawable
		}

		// float textSize = getPaint().getTextSize(); // 使用這個會導致文字豎向排下來
		float textSize = getPaint().measureText(getText().toString());
		int drawWidth = drawable.getIntrinsicWidth();
		int drawPadding = getCompoundDrawablePadding();
		float contentWidth = textSize + drawWidth + drawPadding;
		int leftPadding = (int) (getWidth() - contentWidth);
		setPadding(0, 0, leftPadding, 0); // 直接貼到左邊
		float dx = (getWidth() - contentWidth) / 2;
		canvas.translate(dx, 0);// 往右移動
		return canvas;
	}
}

佈局檔案:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <com.example.view.DrawableHorizontalButton
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:drawableLeft="@drawable/ic_android_small"
        android:includeFontPadding="false"
        android:text="@string/hello_world"
        android:textSize="20sp" />
    
    <com.example.view.DrawableHorizontalButton
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:drawableRight="@drawable/ic_android_small"
        android:includeFontPadding="false"
        android:text="@string/hello_world"
        android:textSize="20sp" />

</LinearLayout>

執行上面的程式碼可以看到效果圖:


很好,就是這樣已經居中了。這裡說一下實現原理。就是通過setPadding(int left, int top, int right, int bottom)方法把文字移動到邊緣與圖片挨起來,然後使用canvas.translate(float dx, float dy)把圖片和文字一同移動到控制元件的中間。實現一起居中效果;

同理我們可以在實現上下型的居中:

程式碼如下:

public class DrawableVerticalButton extends Button {

	public DrawableVerticalButton(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		canvas = getTopCanvas(canvas);
		super.onDraw(canvas);
	}

	private Canvas getTopCanvas(Canvas canvas) {
		Drawable[] drawables = getCompoundDrawables();
		if (drawables == null) {
			return canvas;
		}
		Drawable drawable = drawables[1];// 上面的drawable
		if(drawable == null){
			drawable = drawables[3];// 下面的drawable
		}
		
		float textSize = getPaint().getTextSize();
		int drawHeight = drawable.getIntrinsicHeight();
		int drawPadding = getCompoundDrawablePadding();
		float contentHeight = textSize + drawHeight + drawPadding;
		int topPadding = (int) (getHeight() - contentHeight);
		setPadding(0, topPadding , 0, 0);
		float dy = (contentHeight - getHeight())/2;
		canvas.translate(0, dy);
		Log.i("DrawableTopButton", "setPadding(0,"+topPadding+",0,0");
		Log.i("DrawableTopButton", "translate(0,"+dy+")");
		return canvas;
	}

}
佈局檔案:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <com.example.view.DrawableVerticalButton
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:drawableBottom="@drawable/ic_android_small"
        android:includeFontPadding="false"
        android:text="@string/hello_world"
        android:textSize="20sp" />
    
    <com.example.view.DrawableVerticalButton
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:drawableTop="@drawable/ic_android_small"
        android:includeFontPadding="false"
        android:text="@string/hello_world"
        android:textSize="20sp" />

</LinearLayout>
執行或者直接預覽可以看到效果圖如下:

上下和左右方向的實現原理都是一樣的,都是把文字移動到邊緣,然後文字和圖片一同移動到中間,所以上下方向的程式碼是一樣的。左右的方法是一樣的。於是就是分開來寫。大部分情況下我們都是使用上下方向的。

關於自定義控制元件時重寫draw還是重寫onDraw呢,Android官方推薦重寫的話使用onDraw介面。
“When implementing a view, do not override this method; instead, you should implement onDraw”