自定义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>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章