现在很多Android项目上都使用到类似IOS上的风格,比如滑动开关、毛玻璃效果等等,在Android4.0上我们可以使用Switch,但是在4.0以下是没有这样的控件的,所以我们可以自定义这样的控件的风格来进行适配。

先来看下图片的效果吧:

Android实现苹果的开关按钮 android开关控件_ide

自定义该控件大致需要注意一下几个方面:

1、动态的控制mSlideLeft的值

2、将开点击事件和滑动事件进行隔离

3、滑动的时候注意slideButton不要离开屏幕

下面就直接看自定义View的代码吧:

package com.jun.slidebutton;

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

/**
 * Created by zengy on 2015/10/26.
 */
public class MySlideButton extends View implements View.OnClickListener {
    /**
     * 按钮图片
     */
    private Bitmap mSlideButton;
    /**
     * 背景图片
     */
    private Bitmap mBackgound;
    /**
     * 画笔
     */
    private Paint mPaint;
    /**
     * 按钮图片距离左边界的距离
     */
    private int mSlideLeft = 0;

    public MySlideButton(Context context, AttributeSet attrs) {
        super(context, attrs);

        /**
         * 获得自定义属性的值
         */
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MySlideButton);
        //总多少个自定义属性
        int count = typedArray.getIndexCount();
        for (int i = 0; i < count; i++) {
            //获得每个属性的ID
            int itemId = typedArray.getIndex(i);
            switch (itemId){
                case R.styleable.MySlideButton_my_background:
                    int backgroundId = typedArray.getResourceId(itemId,-1);
                    if(backgroundId == -1){
                        throw new RuntimeException("请设置背景图片");
                    }
                    mBackgound = BitmapFactory.decodeResource(getResources(), backgroundId);
                    break;
                case R.styleable.MySlideButton_my_slide_button:
                    int slideButtonId = typedArray.getResourceId(itemId,-1);
                    if(slideButtonId == -1){
                        throw new RuntimeException("请设置遮挡图片");
                    }
                    mSlideButton = BitmapFactory.decodeResource(getResources(), slideButtonId);
                    break;
                case R.styleable.MySlideButton_curr_state:
                    isOpen = typedArray.getBoolean(itemId,false);
                    break;
            }
        }

//        mSlideButton = BitmapFactory.decodeResource(getResources(), R.mipmap.slidebutton);
//        mBackgound = BitmapFactory.decodeResource(getResources(), R.mipmap.background);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        //设置点击事件的监听
        setOnClickListener(this);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        /**
         * 设置当前view的大小 width :view的宽度 height :view的高度 (单位:像素)
         */
        setMeasuredDimension(mBackgound.getWidth(), mBackgound.getHeight());
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(mBackgound, 0, 0, mPaint);
        canvas.drawBitmap(mSlideButton, mSlideLeft, 0, mPaint);
    }

    /**
     * 判断是否发生拖动, 如果拖动了,就不再响应 onclick 事件
     */
    private boolean isDrag = false;
    /**
     * down 事件时的x值
     */
    private int startX;
    /**
     * touch 事件的上一个x值
     */
    private int endX;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: //手指按下屏幕
                startX = endX = (int) event.getRawX();
                isDrag = false;
                break;
            case MotionEvent.ACTION_MOVE: //手指在屏幕上移动
                int nowX = (int) event.getRawX();
                //判断是否已经拖动
                if (Math.abs(nowX - startX) > 5) {
                    isDrag = true;//是拖动状态
                }
                //手指在屏幕上移动的距离
                int dis = nowX - endX;
                //将本次位置设置给endX
                endX = nowX;
                //根据手指一定的距离来设置mSlideLeft的值
                mSlideLeft = mSlideLeft + dis;
                break;
            case MotionEvent.ACTION_UP: //手指离开屏幕
                //如果发生了滑动,就执行移动事件,否则就执行点击事件
                if (isDrag) {
                    int maxLeft = mBackgound.getWidth() - mSlideButton.getWidth();
                    if (mSlideLeft > maxLeft / 2) {
                        isOpen = true;//如果mSlideLeft比距离最左边的最大值还大,说明是开状态
                    } else {
                        isOpen = false;
                    }
                    //刷新当前状态
                    flushState();
                }
                break;
        }
        //需要一直刷新当前View的显示效果
        flushView();
        return true;
    }

    /**
     * 当前的状态,false表示关,ture表示开
     */
    private boolean isOpen = false;

    @Override
    public void onClick(View v) {
        // 如果没有拖动,才执行改变状态的动作
        if (!isDrag) {
            isOpen = !isOpen;
            //刷新当前的状态
            flushState();
        }
    }

    /**
     * 刷新当前的状态
     */
    private void flushState() {
        if (isOpen) {
            //是开的状态,则mSlideLeft为背景图片的宽度减去按钮的宽度
            mSlideLeft = mBackgound.getWidth() - mSlideButton.getWidth();
        } else {
            //关的状态,则mSlideLeft依然是0
            mSlideLeft = 0;
        }
        /*
         * 刷新当前视图 导致 执行onDraw执行
		 */
        invalidate();
    }

    /**
     * 刷新当前视图(View的显示效果)
     */
    public void flushView() {
		/*
		 * 对 slideBtn_left 的值进行判断 ,确保其在合理的位置 即 0<=slideBtn_left <= maxLeft
		 * 如果不进行该值的判断,那么mSlideButton将会划出屏幕,感兴趣的可以将这几行注释看效果
		 */
        int maxLeft = mBackgound.getWidth() - mSlideButton.getWidth();
        // 确保 slideBtn_left >= 0
        mSlideLeft = mSlideLeft <= 0 ? 0 : mSlideLeft;
        // 确保 slideBtn_left <=maxLeft
        mSlideLeft = mSlideLeft >= maxLeft ? maxLeft : mSlideLeft;
		/*
		 * 刷新当前视图 导致 执行onDraw执行
		 */
        invalidate();
    }
}



上面的代码基本上每行都有注释,这里就不过多说了,自定义好了控件之后,我们就可以再布局文件中使用了:

activity_main.xml文件是:

<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:feng="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <com.jun.slidebutton.MySlideButton
        android:id="@+id/mySlideButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        feng:my_background="@mipmap/background"
        feng:my_slide_button="@mipmap/slidebutton"
        feng:curr_state="true"/>

</LinearLayout >

使用自定义布局文件的时候需要指定全名哦。

这里使用了自定义属性来灵活的设置图片,如果不熟悉自定义属性的朋友请自行脑补哦。