自定義View實例(三)滑動開關

自定義View的步驟

通過繼承view類自定義view步驟如下

  1. 實現view構造方法
  2. 測量view的大小,即重寫onMeasure方法
  3. 繪製view,即重寫onDraw方法
    繼承自view一般不需要重寫onLayout方法
    這次我們實現一個滑動的開關,效果見圖:
    在這裏插入圖片描述代碼中有詳細的註釋如下:
package com.car.customview.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.car.customview.R;

public class MyToggleButton extends View implements View.OnClickListener {
    /*
        繼承View自定義View步驟:
        1.構造方法
        2.測量view大小
        3.確定view的位置,view有建議權,決定權在父view手中
        4.繪製view
     */
    //背景圖片
    private Bitmap backgroundBitmap;
    //可以滑動的圖片
    private Bitmap slideBtn;
    private Paint paint;
    private float slideBtn_left = 0;
    private boolean currState = false;//當前狀態
    private boolean isDrag = false;//是否拖動了
    public MyToggleButton(Context context) {
        super(context);
        initView();
    }

    public MyToggleButton(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        //獲得自定義屬性
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyToggleButton);
        currState = typedArray.getBoolean(R.styleable.MyToggleButton_curr_state,false);
        int backgroundId = typedArray.getResourceId(R.styleable.MyToggleButton_background, 0);
        backgroundBitmap = BitmapFactory.decodeResource(getResources(), backgroundId);
        int slideBtnId = typedArray.getResourceId(R.styleable.MyToggleButton_toggle_btn, 0);
        slideBtn = BitmapFactory.decodeResource(getResources(), slideBtnId);
        initView();
    }

    public MyToggleButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }
	//view的大小設置爲背景圖片的寬高
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight());

    }
	//onDraw方法中要注意drawBitmap調用的順序,後調用的會覆蓋在前面調用的上面
    @Override
    protected void onDraw(Canvas canvas) {
    	/*
    		這兩句代碼順序如果顛倒,則會導致背景圖片覆蓋在滑動按鈕上層
    	*/
        //繪製背景
        canvas.drawBitmap(backgroundBitmap,0,0,paint);
        //繪製滑動的按鈕
        canvas.drawBitmap(slideBtn, slideBtn_left, 0, paint);

    }

    private void initView() {
        paint = new Paint();
        paint.setAntiAlias(true);//抗鋸齒
        setOnClickListener(this);
        flushState();
    }

    @Override
    public void onClick(View v) {
        //如果沒有拖動才執行click事件
        if (!isDrag){
            currState = !currState;
            flushState();
        }
    }

    private void flushState() {
        if (currState){
            slideBtn_left = backgroundBitmap.getWidth() - slideBtn.getWidth();
        }else {
            slideBtn_left = 0;
        }
        flushView();//刷新View,會調用onDraw方法
    }

    private int firstX;
    private int lastX;
    //該方法中處理view滑動
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                firstX = lastX = (int) event.getX();
                isDrag = false;
                break;
            case MotionEvent.ACTION_MOVE:
                if (Math.abs(event.getX() - firstX) > 5){
                    isDrag = true;
                }
                int distance = (int) (event.getX() - lastX);
                lastX = (int) event.getX();
                slideBtn_left += distance;
                break;
            case MotionEvent.ACTION_UP:
                //擡起時根據button位置確定當前狀態
                if (isDrag){
                    int maxLeft = backgroundBitmap.getWidth() - slideBtn.getWidth();
                    if (slideBtn_left > maxLeft / 2){
                        currState = true;
                    }else {
                        currState = false;
                    }
                    flushState();
                }
                break;
            default:

        }
        //刷新視圖,button就會滑動
        flushView();
        return true;
    }

    private void flushView() {
        //直接刷新,滑動的button會滑到view外部,所以要進行判斷
        int maxLeft = backgroundBitmap.getWidth() - slideBtn.getWidth();//最大的左邊界
        slideBtn_left = slideBtn_left > 0 ? slideBtn_left : 0;
        slideBtn_left = slideBtn_left < maxLeft ? slideBtn_left : maxLeft;
        invalidate();
    }


}

自定義屬性步驟:

  1. 在vlaues文件夾下新建attrs.xml,定義自己的屬性集
  2. 在佈局文件中使用自定義屬性時需要聲明命名空間xmlns:app=“http://schemas.android.com/apk/res-auto
  3. 在view第二個構造方法中解析自定義屬性

滑動開關的自定義屬性如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyToggleButton">
        <!--背景圖-->
        <attr name="background" format="reference" />
        <!--滑動的按鈕圖-->
        <attr name="toggle_btn" format="reference" />
        <!--默認狀態-->
        <attr name="curr_state" format="boolean" />
    </declare-styleable>
</resources>

在佈局文件中直接使用滑動開關:

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

    <com.car.customview.view.MyToggleButton
        android:id="@+id/toggle_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:background="@drawable/switch_background"
        app:toggle_btn="@drawable/slide_button"
        app:curr_state="true"/>

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