您的位置:首页 > 移动开发 > Android开发

Android自定义控件之滑动解锁

2016-05-21 20:53 465 查看
我的视频系列 http://edu.csdn.net/course/detail/2741,一起来学习Android…

代码参考地址 https://github.com/liuzhiyuan0932/SlideUnLock

代码效果图>





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

public class SlideUnlockView extends View


自定义SlideUnLockView的属性

在values文件夹中定义属性

<?xml version="1.0" encoding="utf-8"?>
<resources>

<declare-styleable name="SlideUnlockButton">

<!-- 背景图片的属性, 引用的是R.drawable.xx. -->
<attr name="slideUnlockBackgroundResource" format="reference" />

<!-- 滑动块图片的属性, 引用的是R.drawable.xx. -->
<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自定义控件–滑动解锁的所有代码逻辑
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息