自定义控件-自定义开关

布局:

<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:yzx="http://schemas.android.com/apk/res/com.yzx.mytogglebutton"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" 
    >

    <com.yzx.mytogglebutton.MyToggleButton
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        heima:slide_button="@drawable/slide_button"
        heima:switch_background="@drawable/switch_background"
        />

</RelativeLayout>

com.yzx.togglebutton.ToggleButton类的实现和自定义属性的使用。

 

业务逻辑实现

 

1.添加自定义属性。在values目录下创建attrs.xml文件

attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyToggleBtn">  
        <attr name="slide_button" format="reference"/>  
        <attr name="switch_background" format="reference"/>  
    </declare-styleable> 
</resources>

 

2.在布局文件中用到自定义属性时需要引入自定义的命名空间。

在activity_main.xml布局中引入如下命名空间:

xmlns:yzx="http://schemas.android.com/apk/res/com.example.togglebutton"

com.example.togglebutton是包名,其实名字可以自定义,并不是非得用类名,但是我们习惯用类型作为自定义属性的命名空间。

 

 

3.自定义类MyToggleButton继承View类。

MyToggleButton.java

package com.itheima.mytogglebutton;

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.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;

public class MyToggleButton extends View implements OnClickListener {
    // 该按钮其实是在一个背景图片上叠加一个图片
    private Bitmap slide_button;
    private Bitmap switch_background;
    private Paint paint;
    int slideLeft = 0;
    int slideLeftMax;
    // 当前是开或者关,开关的状态
    boolean toggleState = false;
    int firstX;
    int lastX;
    // 记录用户当前是点击还是滑动
    boolean isDrop;

    // 自定义控件必须覆写父类的三个构造函数
    public MyToggleButton(Context context, AttributeSet attrs) {
        super(context, attrs);

        // int count = attrs.getAttributeCount();
        // for (int i = 0; i < count; i++) {
        // System.out.println(attrs.getAttributeName(i) + ":" +
        // attrs.getAttributeValue(i));
        // }

        // arg0:要解析的属性在哪个AttributeSet中
        // arg1:把要解析的两个自定义属性的id封装在这个int数组中
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyToggleBtn);
        BitmapDrawable bd1 = (BitmapDrawable) array.getDrawable(R.styleable.MyToggleBtn_slide_button);
        BitmapDrawable bd2 = (BitmapDrawable) array.getDrawable(R.styleable.MyToggleBtn_switch_background);

        // 加载两张背景图
        slide_button = bd1.getBitmap();
        switch_background = bd2.getBitmap();
        // slide_button = BitmapFactory.decodeResource(getResources(),
        // R.drawable.slide_button);
        // switch_background = BitmapFactory.decodeResource(getResources(),
        // R.drawable.switch_background);

        slideLeftMax = switch_background.getWidth() - slide_button.getWidth();

        paint = new Paint();

        // 给自己设置点击侦听
        setOnClickListener(this);
    }

    // 测量
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 指定组件宽高
        setMeasuredDimension(switch_background.getWidth(), switch_background.getHeight());
    }

    // 绘制
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 绘制两张图片
        canvas.drawBitmap(switch_background, 0, 0, paint);
        canvas.drawBitmap(slide_button, slideLeft, 0, paint);
    }

    @Override
    public void onClick(View v) {
        // 先判断用户到底是想点击还是想滑动
        if (!isDrop) {
            if (toggleState) {
                slideLeft = 0;
            } else {
                slideLeft = slideLeftMax;
            }
            toggleState = !toggleState;
        }
        invalidate();
    }

    // 触摸事件
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            firstX = lastX = (int) event.getX();
            isDrop = false;
            break;
        case MotionEvent.ACTION_MOVE:
            int newX = (int) event.getX();
            // 计算出本次move事件和上一次move事件之间位移的像素
            int offsetX = newX - lastX;
            slideLeft += offsetX;
            lastX = newX;

            // 判断当前用户是点击按钮还是在滑动按钮
            // 如果滑动距离小于6,就算是点击
            if (Math.abs(newX - firstX) <= 6) {
                isDrop = false;
            } else {
                // 如果滑动距离大于6,就算是用户在滑动
                isDrop = true;
            }
            break;
        case MotionEvent.ACTION_UP:
            if (isDrop) {
                // 检测手指抬起时,滑块的位移像素是否大于slideLeftMax的一半
                int upX = (int) event.getX();
                // 往右滑动,是否大于一半
                if (upX - firstX > slideLeftMax / 2) {
                    toggleState = true;
                }
                // 往左滑动,是否大于一半
                else if (firstX - upX > slideLeftMax / 2) {
                    toggleState = false;
                }
                flushViewState();
            }
            break;

        }
        flushViewBound();
        return true;
    }

    private void flushViewBound() {
        if (slideLeft < 0) {
            slideLeft = 0;
        } else if (slideLeft > slideLeftMax) {
            slideLeft = slideLeftMax;
        }
        invalidate();
    }

    private void flushViewState() {
        if (toggleState) {
            slideLeft = slideLeftMax;
        } else {
            slideLeft = 0;
        }
    }
}

MyView.java

package com.itheima.mytogglebutton;

import android.R.color;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

public class MyView extends View {

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }

    // 测量宽高(确定宽高)
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),
        // widthMeasureSpec),
        // getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));

        // 传入这个方法的参数就是组件的最终宽高
        setMeasuredDimension(300, 200);
    }

    // 分配位置
    // left、top、right、bottom是父空间传入的参数,这四个参数表明了组件的宽高和位置
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        // TODO Auto-generated method stub
        super.onLayout(changed, left, top, right, bottom);
        // System.out.println(left + " ; " + top + " ; " + right + " ; " +
        // bottom);
    }

    // 在组件中绘制内容
    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);

        Paint paint = new Paint();

        canvas.drawColor(Color.RED);

        paint.setColor(Color.YELLOW);
        // 设置抗锯齿
        paint.setAntiAlias(true);
        // 设置圆心坐标和圆的半径
        canvas.drawCircle(100, 100, 60, paint);
    }
}

 

posted @ 2016-05-31 15:36  沉默的羊癫疯  阅读(135)  评论(0编辑  收藏  举报