Android自定义控件之滑动解锁

我的视频系列 http://edu.csdn.net/course/detail/2741,一起来学习Android…
代码参考地址 https://github.com/liuzhiyuan0932/SlideUnLock

代码效果图>
Android自定义控件之滑动解锁_第1张图片
Android自定义控件之滑动解锁_第2张图片

自定义滑动解锁的控件继承自View

public class SlideUnlockView extends View 

自定义SlideUnLockView的属性

  • 在values文件夹中定义属性

<resources>

    <declare-styleable name="SlideUnlockButton">

        
        <attr name="slideUnlockBackgroundResource" format="reference" />

        
        <attr name="slideUnlockBlockResource" format="reference" />
    declare-styleable>

resources>
  • 在xml布局中使用相关的属性
    <com.zhiyuan.slideunlockdemo.view.SlideUnlockView
        android:id="@+id/slideUnlockView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        test:slideUnlockBackgroundResource="@drawable/jiesuo_bg"
        test:slideUnlockBlockResource="@drawable/jiesuo_button" />

定义滑块的几种状态

/**
     * 滑块当前的状态
     */
    public int currentState;
    /**
     * 未解锁
     */
    public static final int STATE_LOCK = 1;
    /**
     * 解锁
     */
    public static final int STATE_UNLOCK = 2;
    /**
     * 正在拖拽
     */
    public static final int STATE_MOVING = 3;

获取图片资源,并进行初始绘制

  • 在构造方法中获取属性值
public SlideUnlockView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // 默认滑动解锁为未解锁状态
        currentState = STATE_LOCK;
        // 命名空间
        String namespace = "http://schemas.android.com/apk/res/com.zhiyuan.slideunlockdemo";

        // 取出自定义属性中背景图片
        int slideUnlockBackgroundResource = attrs.getAttributeResourceValue(
                namespace, "slideUnlockBackgroundResource", -1);
        // 取出自定义属性中滑块图片
        int slideUnlockBlockResource = attrs.getAttributeResourceValue(
                namespace, "slideUnlockBlockResource", -1);
        // 取出自定义属性中当前状态
        // 如果解锁状态是true,说明已经解锁
        /**
         * 当取出自定义属性的背景时,设置背景
         */
        setSlideUnlockBackground(slideUnlockBackgroundResource);
        /**
         * 当取出自定义属性的滑块时,设置滑块的图片
         */
        setSlideUnlockBlock(slideUnlockBlockResource);
        /**
         * 执行onDraw方法,进行界面绘制
         */
        postInvalidate();
    }
  • 设置图片的方法,设置好图片之后,进行界面的初始 绘制
    /**
     * 设置背景图
     * @param slideUnlockBackgroundResource
     */
    public void setSlideUnlockBackground(int slideUnlockBackgroundResource) {
        slideUnlockBackground = BitmapFactory.decodeResource(getResources(),
                slideUnlockBackgroundResource);
        // 获取背景图的宽和高
        blockBackgoundWidth = slideUnlockBackground.getWidth();

    }
    /**
     * 设置滑块图
     * @param slideUnlockBlockResource
     */
    public void setSlideUnlockBlock(int slideUnlockBlockResource) {
        slideUnlockBlock = BitmapFactory.decodeResource(getResources(),
                slideUnlockBlockResource);
        // 获取滑块的宽和高
        blockWidth = slideUnlockBlock.getWidth();
        blockHeight = slideUnlockBlock.getHeight();
    }

通过测量背景图的宽高设置SlideUnLockView的宽高

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //设置控件的宽高为滑块背景图的宽高
        setMeasuredDimension(slideUnlockBackground.getWidth(),
                slideUnlockBackground.getHeight());
    }

处理onTouch事件

  • 判断手指是否按在滑块上
    /**
     * 计算手指是否是落在了滑块上(默认是按照滑块在未解锁的初始位置来计算的)
     */
    public boolean isDownOnBlock(float x1, float x2, float y1, float y2) {
        float sqrt = FloatMath.sqrt(Math.abs(x1 - x2) * Math.abs(x1 - x2)
                + Math.abs(y1 - y2) * Math.abs(y1 - y2));
        if (sqrt <= blockWidth / 2) {
            return true;
        }
        return false;
    }
  • onTouch事件处理的主要逻辑
@Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
        // 当手指按下的时候,判断手指按下的位置是否在滑块上边
        case MotionEvent.ACTION_DOWN:

            if (currentState != STATE_MOVING) {
                // 判断一下,如果当前正在移动,则不执行触摸操作
                // 获取相对于背景的左上角的x,y值
                x = event.getX();
                y = event.getY();
                // 先计算出滑块的中心点的x,y坐标
                float blockCenterX = blockWidth * 1.0f / 2;
                float blockCenterY = blockHeight * 1.0f / 2;
                downOnBlock = isDownOnBlock(blockCenterX, x, blockCenterY, y);
                Log.i(TAG, "down......................");
                // 调用onDraw方法
                postInvalidate();

            }
            break;
        case MotionEvent.ACTION_MOVE:
            // 如果手指确定按在滑块上,就视为开始拖拽滑块
            if (downOnBlock) {
                // 获取相对于背景的左上角的x,y值
                x = event.getX();
                y = event.getY();
                currentState = STATE_MOVING;
                Log.i(TAG, "move......................");
                // 调用onDraw方法
                postInvalidate();
            }
            break;
        case MotionEvent.ACTION_UP:
            if (currentState == STATE_MOVING) {
                // 当手指抬起的时候,应该是让滑块归位的
                // 说明未解锁
                if (x < blockBackgoundWidth - blockWidth) {
                    handler.sendEmptyMessageDelayed(0, 10);
                    // 通过回调设置已解锁
                    onUnLockListener.setUnLocked(false);
                } else {
                    currentState = STATE_UNLOCK;
                    // 通过回调设置未解锁
                    onUnLockListener.setUnLocked(true);
                }
                downOnBlock = false;
                // 调用onDraw方法
                postInvalidate();

            }
            break;

        default:
            break;
        }
        return true;
    }

调用OnDraw方法并根据状态进行绘制

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 在一开始的使用将背景图绘制出来
        canvas.drawBitmap(slideUnlockBackground, 0, 0, null);
        /**
         * 判断当前状态
         */
        switch (currentState) {
        // 如果是未解锁,就将滑块绘制到最左端
        case STATE_LOCK:
            canvas.drawBitmap(slideUnlockBlock, 0, 0, null);
            break;
        // 已解锁,计算出
        case STATE_UNLOCK:
            int unlockX = blockBackgoundWidth - blockWidth;
            canvas.drawBitmap(slideUnlockBlock, unlockX, 0, null);
            break;
        case STATE_MOVING:
            if (x < 0) {
                x = 0;
            } else if (x > blockBackgoundWidth - blockWidth) {
                x = blockBackgoundWidth - blockWidth;
            }
            canvas.drawBitmap(slideUnlockBlock, x, 0, null);
            break;
        default:
            break;
        }
    }

设置手指抬起未解锁时滑块缓慢回到初始位置

    /**
     * 通过handler来控制滑块在未解锁的时候,平缓的滑动到左端
     */
    Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            if (msg.what == 0) {
                // 如果x还大于0,就人为的设置缓慢移动到最左端,每次移动距离设置为背景宽的/100
                if (x > 0) {
                    x = x - blockBackgoundWidth * 1.0f / 100;
                    // 刷新界面
                    postInvalidate();
                    // 设置继续移动
                    handler.sendEmptyMessageDelayed(0, 10);
                } else {
                    handler.removeCallbacksAndMessages(null);
                    currentState = STATE_LOCK;
                    Log.i(TAG, "state---lock.....");
                }
            }
        };
    };
case MotionEvent.ACTION_UP:
            if (currentState == STATE_MOVING) {
                // 当手指抬起的时候,应该是让滑块归位的
                // 说明未解锁
                if (x < blockBackgoundWidth - blockWidth) {
                    handler.sendEmptyMessageDelayed(0, 10);
                    // 通过回调设置已解锁
                    onUnLockListener.setUnLocked(false);
                } else {
                    currentState = STATE_UNLOCK;
                    // 通过回调设置未解锁
                    onUnLockListener.setUnLocked(true);
                }
                downOnBlock = false;
                // 调用onDraw方法
                postInvalidate();

            }
            break;

设置滑动解锁的监听

  • 定义一个解锁监听的接口
public interface OnUnLockListener {
        public void setUnLocked(boolean lock);
    }
  • 对外听设置监听的方法
public void setOnUnLockListener(OnUnLockListener onUnLockListener) {
        this.onUnLockListener = onUnLockListener;
    }
  • 在OnTouch中解锁的时候,进行设置
    if (currentState == STATE_MOVING) {
                // 当手指抬起的时候,应该是让滑块归位的
                // 说明未解锁
                if (x < blockBackgoundWidth - blockWidth) {
                    handler.sendEmptyMessageDelayed(0, 10);
                    // 通过回调设置已解锁
                    onUnLockListener.setUnLocked(false);
                } else {
                    currentState = STATE_UNLOCK;
                    // 通过回调设置未解锁
                    onUnLockListener.setUnLocked(true);
                }
                downOnBlock = false;
                // 调用onDraw方法
                postInvalidate();

            }

定义滑动解锁时手机震动的震动器

    // 获取系统振动器服务
        vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
        // 启动震动器 100ms
        vibrator.vibrate(100);

在类中使用SlideUnLockView

slideUnlockView = (SlideUnlockView) findViewById(R.id.slideUnlockView);

        // 设置滑动解锁-解锁的监听
        slideUnlockView.setOnUnLockListener(new OnUnLockListener() {
            @Override
            public void setUnLocked(boolean unLock) {
                // 如果是true,证明解锁
                if (unLock) {
                    // 启动震动器 100ms
                    vibrator.vibrate(100);
                    // 当解锁的时候,执行逻辑操作,在这里仅仅是将图片进行展示
                    imageView.setVisibility(View.VISIBLE);
                    // 重置一下滑动解锁的控件
                    slideUnlockView.reset();
                    // 让滑动解锁控件消失
                    slideUnlockView.setVisibility(View.GONE);
                }
            }
        });

    }

用户屏幕锁屏的监听

  • 注册Android锁屏广播
/**
     * 注册一个屏幕锁屏的广播
     */
    private void registScreenOffReceiver() {
        // TODO Auto-generated method stub
        receiver = new ScreenOnOffReceiver();
        // 创建一个意图过滤器
        IntentFilter filter = new IntentFilter();
        // 添加屏幕锁屏的广播
        filter.addAction("android.intent.action.SCREEN_OFF");
        // 在代码里边来注册广播
        this.registerReceiver(receiver, filter);

    }
  • 定义广播接收器
    class ScreenOnOffReceiver extends BroadcastReceiver {

        private static final String TAG = "ScreenOnOffReceiver";

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            // 关屏的操作
            if ("android.intent.action.SCREEN_OFF".equals(action)) {
                // 当手机关屏时,我们同时也锁屏
                slideUnlockView.setVisibility(View.VISIBLE);
                // 设置图片消失
                imageView.setVisibility(View.GONE);
            }
        }
    }

以上是Android自定义控件–滑动解锁的所有代码逻辑

你可能感兴趣的:(自定义控件)