前言
Android 自定义 View 技能是成为高级工程师所必备的,笔者觉得自定义 View 没有什么捷径可走,唯有经常练习才能解决产品需求。笔者也好久没有写自定义 View 了,赶紧写个控件找点感觉回来。
本文实现的是一个 锁屏图案的自定义控件。效果图如下:
Github 地址:https://github.com/xing16/AndroidSample
LockView 介绍
自定义属性:
属性 | 描述 |
---|---|
app:rowCount=”3” | 每行每列圆的个数 |
app:normalColor=”0xee776666” | 圆的默认颜色 |
app:moveColor=”0xee0000ff” | 圆的选中颜色 |
app:errorColor=”0xeeff0000” | 圆的错误颜色 |
引用方式:
(1) 在布局文件中引入
<com.xing.androidsample.view.LockView
android:id="@+id/lock_view"
app:rowCount="4"
app:normalColor=""
app:moveColor=""
app:errorColor=""
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="40dp" />
(2) 在代码中设置正确的图案,用于校验是否匹配成功,并在回调中获取结果
List<Integer> intList = new ArrayList<>();
intList.add(3);
intList.add(7);
intList.add(4);
intList.add(2);
lockView.setStandard(intList);
lockView.setOnDrawCompleteListener(new LockView.OnDrawCompleteListener() {
@Override
public void onComplete(boolean isSuccess) {
Toast.makeText(CustomViewActivity.this, isSuccess ? "success" : "fail", Toast.LENGTH_SHORT).show();
}
});
实现思路
- 以默认状态绘制 rowCount * rowCount 个圆,外圆颜色需要在内圆颜色上加上一定的透明度。
- 在 onTouchEvent() 方法中,判断当前触摸点与各个圆的圆心距离是否小于圆的半径,决定各个圆此时处于哪个状态(normal,move,error),调用 invalidate() 重新绘制,更新颜色。
- 将手指滑动触摸过的圆的坐标添加到一个 ArrayList 中,使用 Path 连接该集合中选中的圆,即可绘制出划过的路径线。
实现步骤
自定义属性
在 res/values 目录下新建 attrs.xml 文件:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="LockView">
<attr name="normalColor" format="color|reference" /> <!--默认圆颜色-->
<attr name="moveColor" format="color|reference" /> <!--手指触摸选中圆颜色-->
<attr name="errorColor" format="color|reference" /> <!--手指抬起错误圆颜色-->
<attr name="rowCount" format="integer" /> <!--每行每列圆的个数-->
</declare-styleable>
</resources>
获取自定义属性
public LockView(Context context) {
this(context, null);
}
public LockView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
readAttrs(context, attrs);
init();
}
/**
* 获取自定义属性
*/
private void readAttrs(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LockView);
normalColor = typedArray.getColor(R.styleable.LockView_normalColor, DEFAULT_NORMAL_COLOR);
moveColor = typedArray.getColor(R.styleable.LockView_moveColor, DEFAULT_MOVE_COLOR);
errorColor = typedArray.getColor(R.styleable.LockView_errorColor, DEFAULT_ERROR_COLOR);
rowCount = typedArray.getInteger(R.styleable.LockView_rowCount, DEFAULT_ROW_COUNT);
typedArray.recycle();
}
/**
* 初始化
*/
private void init() {
stateSparseArray = new SparseIntArray(rowCount * rowCount);
points = new PointF[rowCount * rowCount];
innerCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
innerCirclePaint.setStyle(Paint.Style.FILL);
outerCirclePaint = new Paint(Paint.ANTI_