自定義開關View的酷炫效果(ButtonView)

不多說先看圖

這裏寫圖片描述

寫在前頭

最近在看到的一個按鈕動畫,覺得以後在項目中遇到的可能性挺大的,閒的無聊實現了下,代碼貼在下面,還請各位大神多多指正,互相學習!

代碼實現

做了比較多的註釋,是自己的學習,也是提供給看的人更清楚明瞭。

public class ButtonView extends View {
    //    private OnClickListener mListener;
    //默認初始高度
    private int rect_height = 0;
    //灰色
    private Paint paint_gray;
    //橙色
    private Paint paint_yellow;
    //圓形的左、上、右、下
    private int circle_left, circle_top, circle_right, circle_bottom;
    //圓形半徑
    private int circle_radius;
    //默認padding
    private int DEFAULT_PADDING = 20;
    //上下文
    private Context context;

    //打開標誌
    private boolean openFlag = false;
    //第一次標誌(第一次進入時是灰色的按鈕)
    private boolean firstFlag = true;
    //秒數,默認爲5 單位是毫秒哦
    private int sleepTime = 5;

    //繼承的構造函數
    public ButtonView(Context context) {
        this(context, null);
    }

    public ButtonView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ButtonView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //保存上下文
        this.context = context;
        //初始化數據
        initData();
    }

    /**
     * 初始化數據
     */
    private void initData() {
        //初始化灰色畫筆
        paint_gray = new Paint();
        //抗鋸齒
        paint_gray.setAntiAlias(true);
        //設置畫筆顏色
        paint_gray.setColor(Color.GRAY);
        //Paint.Style.FILL:填充內部Paint.Style.FILL_AND_STROKE  :填充內部和描邊Paint.Style.STROKE  :描邊
        paint_gray.setStyle(Paint.Style.STROKE);
        //線條的末端爲圓弧
        paint_gray.setStrokeCap(Paint.Cap.ROUND);
        //畫筆的寬度
        paint_gray.setStrokeWidth(11f);
        //初始化橙色畫筆
        paint_orange= new Paint();
        paint_orange.setAntiAlias(true);
        paint_orange.setColor(context.getResources().getColor(R.color.orange));
        paint_orange.setStyle(Paint.Style.STROKE);
        paint_orange.setStrokeCap(Paint.Cap.ROUND);
        paint_orange.setStrokeWidth(12f);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //得到尺寸
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        //得到模式
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        // 一般來說,自定義控件都會去重寫View的onMeasure方法,因爲該方法指定該控件在屏幕上的大小。
        // protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)
        // onMeasure傳入的兩個參數是由上一層控件傳入的大小,有多種情況,重寫該方法時需要對計算控件的實際大小,然後調用setMeasuredDimension(int, int)設置實際大小。
        // onMeasure傳入的widthMeasureSpec和heightMeasureSpec不是一般的尺寸數值,而是將模式和尺寸組合在一起的數值。我們需要通過int mode = MeasureSpec.getMode(widthMeasureSpec)得到模式,
        //   用int size = MeasureSpec.getSize(widthMeasureSpec)得到尺寸。
        // mode共有三種情況,取值分別爲MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY, MeasureSpec.AT_MOST。
        // MeasureSpec.EXACTLY是精確尺寸,當我們將控件的layout_width或layout_height指定爲具體數值時如andorid:layout_width="50dip",或者爲FILL_PARENT是,都是控件大小已經確定的情況,都是精確尺寸。
        // MeasureSpec.AT_MOST是最大尺寸,當控件的layout_width或layout_height指定爲WRAP_CONTENT時,控件大小一般隨着控件的子空間或內容進行變化,此時控件尺寸只要不超過父控件允許的最大尺寸即可。因此,此時的mode是AT_MOST,size給出了父控件允許的最大尺寸。
        // MeasureSpec.UNSPECIFIED是未指定尺寸,這種情況不多,一般都是父控件是AdapterView,通過measure方法傳入的模式。
        switch (widthSpecMode) {
            case MeasureSpec.EXACTLY:
                //按照父類尺寸小的確定寬和高
                if (widthSpecSize <= heightSpecSize) {
                    heightSpecSize = widthSpecSize;
                } else {
                    widthSpecSize = heightSpecSize;
                }
                circle_radius = (widthSpecSize - 2 * DEFAULT_PADDING) / 2;
                circle_left = DEFAULT_PADDING;
                circle_top = DEFAULT_PADDING;
                circle_right = circle_left + widthSpecSize - 2 * DEFAULT_PADDING;
                circle_bottom = circle_top + heightSpecSize - 2 * DEFAULT_PADDING;

                setMeasuredDimension(widthSpecSize, heightSpecSize);
                break;
            case MeasureSpec.AT_MOST:
                break;
            case MeasureSpec.UNSPECIFIED:
                break;
        }

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        RectF rectf = new RectF(circle_left, circle_top, circle_right, circle_bottom);

        if (firstFlag && openFlag) {
            //畫圓和線 rectf圓形的外部矩形區域 -60起點 300圓的大小 paint_yellow畫筆
            canvas.drawArc(rectf, -60, 300, false, paint_orange);
            canvas.drawLine(circle_left + circle_radius, circle_top, circle_left + circle_radius, circle_top + circle_radius, paint_orange);
        } else {
            //第一次進入,畫一個灰色的按鈕(就是每次進入界面的時候)
            if (firstFlag) {
                canvas.drawArc(rectf, -60, 300, false, paint_gray);
                canvas.drawLine(circle_left + circle_radius, circle_top, circle_left + circle_radius, circle_top + circle_radius, paint_gray);
            } else {
                //如果打開,畫橙色的圓
                if (openFlag) {
                    if (rect_height < circle_radius) {
                        rect_height += 5;
                        canvas.drawLine(circle_left + circle_radius, circle_top + circle_radius, circle_left + circle_radius, circle_top + circle_radius - rect_height, paint_orange);
                        canvas.drawArc(rectf, -60, (float) rect_height * 300 / circle_radius, false, paint_orange);
                        //每次繪製都間隔的時間
                        SystemClock.sleep(sleepTime);
                        //更新界面
                        invalidate();
                    }

                    canvas.drawLine(circle_left + circle_radius, circle_top + circle_radius, circle_left + circle_radius, circle_top + circle_radius - rect_height, paint_orange);
                    canvas.drawArc(rectf, -60, (float) rect_height * 300 / circle_radius, false, paint_orange);
                } else {  //如果關閉,畫灰色的圓
                    if (rect_height < circle_radius) {
                        rect_height += 5;
                        canvas.drawLine(circle_left + circle_radius, circle_top + circle_radius, circle_left + circle_radius, circle_top + circle_radius - rect_height, paint_gray);
                        //根據比例繪製圓形
                        canvas.drawArc(rectf, -60, (float) rect_height * 300 / circle_radius, false, paint_gray);


                        //每次繪製都間隔的時間
                        SystemClock.sleep(sleepTime);
                        //更新界面
                        invalidate();
                    }
                    canvas.drawLine(circle_left + circle_radius, circle_top + circle_radius, circle_left + circle_radius, circle_top + circle_radius - rect_height, paint_gray);
                    canvas.drawArc(rectf, -60, (float) rect_height * 300 / circle_radius, false, paint_gray);
                }
            }
        }
    }

    /**
     * 開關標誌
     */
    public void click() {
        firstFlag = false;
        //高度爲0
        rect_height = 0;
        openFlag = !openFlag;
        //繪製成初始狀態
        invalidate();
    }

    /**
     * 設置秒數
     */
    public void setSleepTime(int sleepTime) {
        this.sleepTime = sleepTime;
    }

    /**
     * 設置view打開(就是主動設置爲橙色)
     */
    public void setOpened() {

        openFlag = true;
        invalidate();
    }
}

使用

    public class MainActivity extends AppCompatActivity {

    private boolean isFirst = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final ButtonView viewById = (ButtonView) findViewById(R.id.ButtonView);
        Button button = (Button) findViewById(R.id.button);
        final TextView text = (TextView) findViewById(R.id.text);
        //毫秒
        viewById.setSleepTime(20);
        //設置開始顏色爲打開狀態
//        viewById.setOpened();

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!isFirst){
                    viewById.click();
                    isFirst = true;
                    text.setText("狀態:打開");
                }else {
                    //無動畫效果
//                    viewById.setOpened();
                    //有動畫效果
                    viewById.click();
                    isFirst = false;
                    text.setText("狀態:關閉");
                }
            }
        });
    }
}

xml文件

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">



    <com.rmyh.customview.ButtonView
        android:id="@+id/ButtonView"
        android:layout_centerInParent="true"
        android:layout_width="200dp"
        android:layout_height="2000dp" />

    <TextView
        android:id="@+id/text"
        android:text="狀態:關閉"
        android:layout_below="@+id/ButtonView"
        android:layout_centerHorizontal="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />


    <Button
        android:id="@+id/button"
        android:text="打開/關閉"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>

寫在後面

先寫到這裏了,上面的代碼如果您有更好的建議或者思路,請評論告知,希望和大家共同進步,多多交流!如果您有比較好的學習資料,也請多多分享!

發佈了39 篇原創文章 · 獲贊 33 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章