1. 程式人生 > >自定義view之----自定義button

自定義view之----自定義button

前言:

唉,學習自定義view已經很長時間了,很早就想寫自定義view這類的部落格,這樣一來不僅能夠對知識的梳理與鞏固還能幫助他人。計劃再好不如即時行動更為有效,所以趁著週末趕緊把這個月的計劃完成先。

ok,廢話少說(貌似好像前面的都是廢話~ 額’),切入正題。

正題:

一般應用中按鈕(Button)都會結合自己應用的主題風格來自定義,如:qq,微博,支付寶的登陸按鈕,這裡就不貼圖了,它們登陸按鈕的共同特點都是圓角,不同的是背景顏色不同而已。如果要實現類似的效果,我想說的是 so easy!那麼,如果你看到了這裡,不管你會還是不會,這bi我還是要裝一下的。haha~

實現方式有兩種:

方法一:

只需要自定義背景屬性就能實現

首先看下效果圖,有邊框的,由於是靜態圖所以無法看到點選的效果

這裡寫圖片描述

好了,開始貼程式碼了

xml檔案佈局程式碼:
style=”?android:attr/borderlessButtonStyle”//這一行程式碼是5.0以上版本去掉Button自帶陰影效果的方法

<Button
                style="?android:attr/borderlessButtonStyle"
                android:layout_marginTop="50dp"
                android:layout_width
="200dp" android:layout_height="wrap_content" android:background="@drawable/button_click_enabled" android:layout_gravity="center" android:text="只設置上面兩個角為圓角"/>
<Button android:layout_margin="20dp" android:layout_width
="200dp" style="?android:attr/borderlessButtonStyle" android:layout_height="wrap_content" android:background="@drawable/button_click_enabled_5" android:layout_gravity="center" android:textColor="#fff" android:textSize="15dp" android:text="圓角"/>

button_click_enabled.xml 檔案:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/button_click_unchecked" android:state_pressed="false"></item>
    <item android:drawable="@drawable/button_click_checked" android:state_pressed="true"></item>
</selector>

selector選擇器的意思,顧名思義就算你沒學過你也已經知道它是幹什麼的了吧。預設顯示背景@drawable/button_click_unchecked,當只有按住按鈕(android:state_pressed=”true”時)才會顯示 背景@drawable/button_click_checked

button_click_unchecked.xml檔案:
(複製程式碼後刪掉註釋)

<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/colorAccent"//控制元件顏色
    />
    <corners
        android:bottomLeftRadius="0dp"//左下角設定圓角半徑為0
        android:bottomRightRadius="0dp"//右下角設定圓角半徑為0
        android:topLeftRadius="20dp"//左上角設定圓角半徑為20
        android:topRightRadius="20dp" //右上角設定圓角半徑為20
        />
    <stroke
        android:width="2px"//邊框寬
        android:color="#2443D0"//邊框顏色
         />
</shape>

button_click_checked.xml檔案:

<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#fe3"></solid>

    <corners
        android:bottomLeftRadius="0dp"
        android:bottomRightRadius="0dp"
        android:topLeftRadius="20dp"
        android:topRightRadius="20dp" />
    <stroke
        android:width="2px"
        android:color="#2443D0" />
</shape>

ok,
就這麼簡單。
偷偷告訴你不只是button,其他控制元件也可以這麼幹,自己去發掘吧!

方法二:

下面我要講的才算是自定義view,這種實現方式就比較高大上了,首先你需要對自定義view的知識有一定的瞭解,不瞭解也沒關係,有看不懂的地方請自行百度。如果方法一能實現需求,但你覺得太low了,那麼不要走開(裝逼模式已開啟~)。

同樣,放張效果圖:
這裡寫圖片描述

實現思路:
系統Button是繼承Textview的,所以自定義的view也繼承Textview。
按照步驟:
1.建立類cButton繼承Textview

2.設定自定義屬性(在res/values/styles.xml中):

 <declare-styleable name="cButton">
        <attr name="internalcolor" format="color" />//內部填充顏色
        <attr name="clickedColor" format="color" />//點選後顏色
        <attr name="bordercolor" format="color" />//邊框顏色
        <attr name="textcolor" format="color" />//文字顏色
        <attr name="textsize" format="dimension" />//文字大小
        <attr name="textstr" format="string" />//文字
        <attr name="enablerd" format="boolean" />//是否可點選
        <attr name="angletype" format="integer">//圓角型別
            <enum name="anglecirarc" value="1" />//圓弧形
            <enum name="anglecir" value="2" />//小圓角
        </attr>
    </declare-styleable>

3.在cButton類中對自定義屬性進行解析,解析時設定預設值

 public cButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.cButton);
        enabler = ta.getBoolean(R.styleable.cButton_enablerd, true);//是否可點選
        AngleType = ta.getInt(R.styleable.cButton_angletype, 1);//圓角型別
        clicked_color = ta.getColor(R.styleable.cButton_clickedColor, Color.parseColor("#6F4A4C58"));//點選後的顏色
        border_color = ta.getColor(R.styleable.cButton_bordercolor, Color.parseColor("#40000000"));//邊框顏色
        interna_Color = ta.getColor(R.styleable.cButton_internalcolor, Color.parseColor("#FF4A4C58"));//背景顏色
        text_Color = ta.getColor(R.styleable.cButton_textcolor, Color.BLACK);//文字顏色
        text_Size = ta.getDimension(R.styleable.cButton_textsize, 40);//文字大小
        text = ta.getString(R.styleable.cButton_textstr);//文字
    }

4.邏輯處理繪製圖形:

重點說一下,文字居中繪製:看這裡http://blog.csdn.net/u014702653/article/details/51985821
寬=控制元件寬度/2 - 文字寬度/2;
高=控制元件高度/2 + 文字高度/2;
tPaint.measureText(text);//文字寬度
Paint.FontMetricsInt fm = tPaint.getFontMetricsInt();
fm.bottom - fm.top//文字高度
canvas.drawText(“字串文字”, 寬, 高, tPaint);// 繪製文字

protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
//此處省略n條程式碼
canvas.drawText(text, startX, startY, tPaint);//繪製文字       
canvas.drawRoundRect(rectF, rx, ry, Paint);//繪製圓角矩形
}

5.(能夠繪製正常影象之後)定義點選回撥介面,和觸發點選的方法

public boolean onTouchEvent(MotionEvent event) {
  //cListener.click(cButton.this);//觸發點選事件
   //此處省略n條程式碼
   return true;
}
 /**
     * 定義介面
     */
    public interface OncButtonListener {

        void click(View v);
    }
    /**
     * 監聽回撥方法
     */
    public void setcButtonListener(OncButtonListener listener) {

        cListener = listener;
    }

6.定義設定屬性方法:
每個方法中都必須有invalidate()方法,不然設定無效

    /**
     * 設定是否可點選
     */
    public void setEnabler(boolean enabler) {

        this.enabler = enabler;
        if (enabler) {
            set_Color = interna_Color;
        } else {//
            set_Color = clicked_color;
        }
         invalidate();//重新繪製
    }

    /**
     * 設定文字
     */
    public void setText(String text) {
        this.text = text;
        invalidate();//重繪
    }

   //省略

完成。

下面貼具體程式碼:

cButton類:

/**
 * Created by xxf on 2017/8/19.
 */

public class cButton extends TextView {
    /* 畫筆 */
    private Paint mPaint;//   填充
    private Paint bPaint;//   邊框
    private Paint tPaint;//繪製文字

    private int mWith, mHeight;//寬、高
    private Rect oRect;//
    private String text;//文字
    private int clicked_color;//點選後的顏色
    private int interna_Color;//填充顏色
    private int set_Color;//設定顏色
    private int text_Color;//文字顏色
    private float text_Size;//字型大小
    private OncButtonListener cListener;//點選監聽
    private Context mContext;
    private int border_color;
    private boolean enabler;//設定是否可點選
    private int AngleType;//圓角樣式

    public cButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.cButton);
        enabler = ta.getBoolean(R.styleable.cButton_enablerd, true);//是否可點選
        AngleType = ta.getInt(R.styleable.cButton_angletype, 1);//圓角型別
        clicked_color = ta.getColor(R.styleable.cButton_clickedColor, Color.parseColor("#6F4A4C58"));//點選後的顏色
        border_color = ta.getColor(R.styleable.cButton_bordercolor, Color.parseColor("#40000000"));//邊框顏色
        interna_Color = ta.getColor(R.styleable.cButton_internalcolor, Color.parseColor("#FF4A4C58"));//背景顏色
        if (enabler) {
            set_Color = interna_Color;
        } else {//
            set_Color = clicked_color;
        }

        text_Color = ta.getColor(R.styleable.cButton_textcolor, Color.BLACK);//文字顏色
        text_Size = ta.getDimension(R.styleable.cButton_textsize, 40);//文字大小
        text = ta.getString(R.styleable.cButton_textstr);//文字
    }

    /*用於測量檢視的大小的*/
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWith = MeasureSpec.getSize(widthMeasureSpec);    //取出寬度的確切數值
        mHeight = MeasureSpec.getSize(heightMeasureSpec);    //取出高度的確切數值

    }

    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        init();
        //  Log.e(TAG, "onTouchEvent: " + interna_Color);
//        RectF rectF = new RectF(0 + 10, 0 + 10, mWith - 10, mHeight - 10);
        if (AngleType == 1) {
            // Log.e(TAG, "onDraw: " + 1);
            RectF rectF = new RectF(0 + 10, 0 + 10, mWith - 10, mHeight - 10);
            canvas.drawRoundRect(rectF, mHeight / 2, mHeight / 2 + 30, mPaint);
            canvas.drawRoundRect(rectF,mHeight / 2, mHeight / 2 + 30, bPaint);//邊框
        } else if (AngleType == 2) {
            // Log.e(TAG, "onDraw: " + 2);
            RectF rectF = new RectF(0 + 20, 10, mWith - 20, mHeight - 10);
            canvas.drawRoundRect(rectF, mHeight / 9, mHeight / 9, mPaint);
            canvas.drawRoundRect(rectF, mHeight / 9, mHeight / 9, bPaint);
//        canvas.drawRoundRect(rectF, mHeight / 4, mHeight / 4, bPaint);//邊框
        }


        // canvas.drawText("123",mWith/2,mHeight/2,tPaint);
        /*
         * 控制元件寬度/2 - 文字寬度/2
         */
        float  v=  tPaint.measureText(text);//文字寬度
        float startX = getWidth() / 2 - v / 2;

        /*
         * 控制元件高度/2 + 文字高度/2,繪製文字從文字左下角開始,因此"+"
         */
       // float startY = getHeight() / 2 + oRect.height() / 2;
        Paint.FontMetricsInt fm = tPaint.getFontMetricsInt();
        //fm.bottom - fm.top//文字高度
        int startY = getHeight() / 2 - fm.descent + (fm.bottom - fm.top) / 2;
        // 繪製文字
        canvas.drawText(text, startX, startY, tPaint);
    }

    private void init() {
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(set_Color);
        mPaint.setStrokeWidth(3);
        bPaint = new Paint();
        bPaint.setStyle(Paint.Style.STROKE);
        bPaint.setAntiAlias(true);
        bPaint.setColor(border_color);
        bPaint.setStrokeWidth(2);
        oRect = new Rect();
        tPaint = new Paint();
        tPaint.setAntiAlias(true);
        tPaint.setStyle(Paint.Style.FILL);
        tPaint.setTextSize(text_Size);
        tPaint.setColor(text_Color);
        tPaint.setAntiAlias(true);
        tPaint.getTextBounds(text, 0, text.length(), oRect);
        tPaint.setStrokeWidth(2);
    }

    /**
     * 呼叫 getParent().requestDisallowInterceptTouchEvent(true);
     * 方法。一旦底層View收到touch的action後呼叫這個方法
     * 那麼父層View就不會再呼叫onInterceptTouchEvent了
     *
     * @param ev
     * @return
     */
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (enabler) {
        getParent().requestDisallowInterceptTouchEvent(true);
    }
        return super.dispatchTouchEvent(ev);
    }

    public boolean onTouchEvent(MotionEvent event) {
        if (enabler) {
            // 手指按下:
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    set_Color = clicked_color;//設定按下顏色

                    invalidate();//重新繪製
                    break;

                //手指擡起:
                case MotionEvent.ACTION_UP:
                    if (cListener != null) {
                        if (event.getY() >= mHeight || event.getY() < 0 || event.getX() >= mWith || event.getX() < 0) {
                        } else {
                            cListener.click(cButton.this);
                        }
                    }

                    Log.e(TAG, "onTouchEvent:  ------------" + event.getY());


                    if (enabler) {
                        set_Color = interna_Color;//設定擡起顏色復原
                    } else {
                        set_Color = clicked_color;
                    }

                    invalidate();
                    break;

                // 手指正在移動:
                case MotionEvent.ACTION_MOVE:
                    Log.e(TAG, "onTouchEvent: event.getX()===" + event.getX() + "   mWith========" + mWith);

                    if (event.getY() >= mHeight || event.getY() < 0 || event.getX() >= mWith || event.getX() < 0) {
                        set_Color = interna_Color;

                    } else {
                        set_Color = clicked_color;//設定按下顏色
                    }
                    invalidate();//重新繪製
                    break;
            }
        }
        return true;
    }



    /**
     * 定義介面
     */
    public interface OncButtonListener {

        void click(View v);
    }
    /**
     * 監聽回撥方法
     */
    public void setcButtonListener(OncButtonListener listener) {

        cListener = listener;
    }

    /**
     * 設定是否可點選
     */
    public void setEnabler(boolean enabler) {

        this.enabler = enabler;
        if (enabler) {
            set_Color = interna_Color;
        } else {//
            set_Color = clicked_color;
        }
        setInvalidate();
    }

    /**
     * 設定文字
     */
    public void setText(String text) {
        this.text = text;

        setInvalidate();
    }

    /**
     * 重繪
     */
    public void setInvalidate() {
        invalidate();
    }
}

layout佈局中使用:
宣告名稱空間:xmlns:cButton=”http://schemas.android.com/apk/res-auto”

<xxf.com.buttontest.cButton
                android:id="@+id/cButton"
                android:layout_width="match_parent"
                android:layout_height="48dp"
                android:layout_gravity="center"
                android:layout_marginTop="50dp"
                cButton:enablerd="true"
                cButton:internalcolor="@color/colorPrimary"
                cButton:textcolor="#fff"
                cButton:textsize="20dp"
                cButton:textstr="圓弧邊" />

            <xxf.com.buttontest.cButton
                android:layout_width="match_parent"
                android:layout_height="48dp"
                android:layout_gravity="center"
                cButton:angletype="anglecir"
                cButton:internalcolor="@color/colorPrimary"
                cButton:textcolor="#fff"
                cButton:textsize="15dp"
                cButton:textstr="圓角" />

結束。

謝謝觀看!