剛才我使用銀行APP時,看見了這個效果,趁着下班時間,就寫了。
先看下效果圖:

我先說下我的思路:
1: 先計算每個點 XY坐标點,然後存儲在integerList中,然後畫九個點,此時我把它叫為:畫闆的原始狀态!
2:複寫onTouchEvent事件, 然後每次監聽ACTION_DOWN,ACTION_MOVE,ACTION_UP狀态;
- ACTION_DOWN:我們需要将畫闆至于原始狀态。
- ACTION_MOVE:我們需要計算周遊integerList的點,然後跟move點做比較,如果距離distance < radius , 就表明有選中了該點,此時我們把改點的下标放進indexList中;然後每一次都重新整理一次view。
- ACTION_UP:離開動作,就涉及到自己的業務邏輯了,在這我寫了一個回調,可以根據回調做相應的業務邏輯。對了,我寫的點不能小于4個,也就是indexList.size()>=4;
對了,integerList裡點的存儲的順序是:從左上角開始為第一個,然後,第二個……..第九個;
具體的看下圖:
具體的看下代碼:
package com.app.test.testlineproject;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
/**
* Created by ${liumegnqiang} on 2017/6/28.
*/
public class LockLineView extends View {
private float height;// view的高度
private float wight;
private float radius ;//圓的半徑
private float companyWidth;//因為是三個點,是以将width分為三分 也就是 companyWidth = width / 3;
private float companyHeight;
private List<PaintBean> integerList = new ArrayList<>();//九個點的坐标
private List<Integer> indexList = new ArrayList<>();//連接配接的點(不重複)
private float moveX;//每次移動的X坐标
private float moveY;//每次移動的Y坐标
private OnLessPaintListener onLessPaintListener;
private boolean isUP = false;//判斷是否是UP狀态,是的話,就去除多餘的連接配接 , 隻連接配接點與點
private int widthLine = ;//線的寬度
//R.color.line 可以自定義color
private int selectColor = ContextCompat.getColor(getContext(),R.color.line);//圓選中的顔色
private int unSelectColor = Color.GRAY;//圓未選中的顔色
private int lineColor = ContextCompat.getColor(getContext(),R.color.line);//線的顔色
public LockLineView(Context context) {
super(context);
}
public LockLineView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LockLineView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
height = getMeasuredHeight();
wight = getMeasuredWidth();
/**
* 這三個數值的計算比例,可根據自己的需求, 更改!!!
*/
companyWidth = wight / ;
companyHeight = height / ;
radius = wight / ;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
/**
* 畫九宮格
*/
paint.setColor(unSelectColor);//原點的顔色
for(int i = ;i < ;i++){
for(int j = ;j < ;j++ ){
RectF rectF = new RectF();
rectF.left = j * companyWidth + companyWidth / - radius;
rectF.right = j * companyWidth + companyWidth / + radius;
rectF.top = i * companyHeight + companyHeight / - radius;
rectF.bottom = i * companyHeight + companyHeight / + radius;
integerList.add(new PaintBean(j * companyWidth + companyWidth / , i * companyHeight + companyHeight / ));
canvas.drawOval(rectF, paint);
}
}
/**
* 畫連接配接的線
*/
if(indexList.size() == ){
return;
}
paint.setColor(lineColor);//線的顔色
paint.setStrokeWidth(widthLine);//線的寬度
for(int i = ;i < indexList.size();i++){
canvas.drawLine(integerList.get(indexList.get(i - )).getPaintX(), integerList.get(indexList.get(i - )).getPaintY(),
integerList.get(indexList.get(i)).getPaintX(),integerList.get(indexList.get(i)).getPaintY(), paint);
}
if(!isUP){
canvas.drawLine(integerList.get(indexList.get(indexList.size() - )).getPaintX(), integerList.get(indexList.get(indexList.size() - )).getPaintY(),
moveX, moveY, paint);
}
/**
* 畫選中的點
*/
paint.setColor(selectColor);//選中圓的顔色
for(int i = ;i < indexList.size();i++){
PaintBean paintBean = integerList.get(indexList.get(i));
RectF rectF = new RectF();
rectF.left = paintBean.getPaintX() - radius;
rectF.right = paintBean.getPaintX() + radius;
rectF.top = paintBean.getPaintY() - radius;
rectF.bottom = paintBean.getPaintY() + radius;
canvas.drawOval(rectF, paint);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:{
Message message = new Message();
message.what = ;
handler.sendMessage(message);
break;
}
case MotionEvent.ACTION_MOVE:{
moveX = event.getX();
moveY = event.getY();
for(int i = ;i < integerList.size();i++){
PaintBean paintBean = integerList.get(i);
float distance = (float)Math.sqrt((paintBean.getPaintX()-moveX) * (paintBean.getPaintX()-moveX)
+ (paintBean.getPaintY() - moveY) * (paintBean.getPaintY() - moveY));
/**
* 有一個問題:
* 就是當你測試時,可以中間隔一個原點,
* 也就是該原點沒有被選中,這時你可以修改該下面的判斷,進行修改;
* 當然 你感覺沒問題的話 就不用改了。。。
*/
if(distance < radius){
if(!indexList.contains(i)){
indexList.add(i);
}
}
}
Message message = new Message();
message.what = ;
handler.sendMessage(message);
break;
}
case MotionEvent.ACTION_UP:{
Message message = new Message();
message.what = ;
handler.sendMessage(message);
break;
}
}
return true;
}
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case :{
isUP = false;
integerList.clear();
invalidate();
break;
}
case :{
isUP = true;
if(indexList.size() < ){
/**
* TODO 連點數少于四個時,業務邏輯處理
*/
Toast.makeText(getContext(),"連點數不能少于4個", Toast.LENGTH_SHORT).show();
if(onLessPaintListener != null){
onLessPaintListener.onLessPaintListener();//少于四個時,回調給view層
}
integerList.clear();
indexList.clear();
}else{
/**
* TODO 連點數大于四個時,業務邏輯處理
*/
}
invalidate();
break;
}
case :{
isUP = false;
integerList.clear();
indexList.clear();
invalidate();
break;
}
}
}
};
/**
* 在view層設定參數之後(比如:setWidthLine(4)),可調用此方法,重新整理LockLineView;
*/
public void setPaint(){
postInvalidate();
}
/**
* 少于四個點數時,回調
*/
interface OnLessPaintListener{
void onLessPaintListener();
}
public OnLessPaintListener getOnLessPaintListener() {
return onLessPaintListener;
}
public void setOnLessPaintListener(OnLessPaintListener onLessPaintListener) {
this.onLessPaintListener = onLessPaintListener;
}
public float getRadius() {
return radius;
}
public void setRadius(float radius) {
this.radius = radius;
}
public int getWidthLine() {
return widthLine;
}
public void setWidthLine(int widthLine) {
this.widthLine = widthLine;
}
public int getLineColor() {
return lineColor;
}
public void setLineColor(int lineColor) {
this.lineColor = lineColor;
}
public int getUnSelectColor() {
return unSelectColor;
}
public void setUnSelectColor(int unSelectColor) {
this.unSelectColor = unSelectColor;
}
public int getSelectColor() {
return selectColor;
}
public void setSelectColor(int selectColor) {
this.selectColor = selectColor;
}
}
有的小夥伴可能會問:為什麼不用postInvalidate()呢?
其實我剛開始用的就是這個方法,但是我發現滑動的move和重新整理view不同步,是以就用了invalidate(),自己控制重新整理view。
至于顔色的,可以通過相應的get方法進行設定。對了 這個view的寬高,需要自己去調試!
最後附上源碼:
http://download.csdn.net/detail/lmq121210/9884371