最近一直想寫一片視圖的繪製過程,一張圖,從測量佈局到繪製,真的會的人也不用看,不會的人吧看完也不懂..尷尬。
這幾天項目用到手勢密碼,網上下載了改了改。發現晚上很多都是自己從頭畫到底,很多帶的功能不少,但是實際開發中就很尷尬。自己有想到了個實現方法,閒來沒事搞了下,感覺可行分享出來。
這個使用的時候必須注意要給LinGesturePassword設置上背景,否則不會調用的的onDraw,主要是父類組件的一個特性,父類容器是用來發子類的所以沒背景是不會調用的的onDraw。
這裏要使用的時候添加的子類必須實現LinGesturePassword.ZhuangTai接口的三個方法,這樣滑動纔會改變,這裏爲了方便英語學渣的我直接沒用使用有道查詢,有強迫的人可以修改。這裏我還主要標記了一下onLayout方法,這是父類的一個佈局方法,有的時候是很方便的,自定義視圖不自己寫也要理解,不然怎麼魔改別人的代碼。
public class LinGesturePassword extends LinearLayout {
float xy[] = new float[2];
List<float[]> list = new ArrayList<>();
List<Integer> lintp = new ArrayList<>();//保存密碼id
WanChengHuiDiao wanChengHuiDiao;
private int lineColor = 0xFF378FC9;
private int lineColorCuo = 0xFF378FC9;
private int linStrokeWidth=10;
private boolean isTouch = true;
private boolean cuowu=false;
public LinGesturePassword(Context context) {
super(context);
}
public LinGesturePassword(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public LinGesturePassword(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public LinGesturePassword(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public void onDraw(Canvas canvas) {
Log.e("aaa", "運行拉啦啦啦");
super.onDraw(canvas);
drawAllLine(canvas);
}
/**
* 關閉手勢密碼
* @param touch
*/
public void setTouch(boolean touch) {
isTouch = touch;
}
public void setWanChengHuiDiao(WanChengHuiDiao wanChengHuiDiao) {
this.wanChengHuiDiao = wanChengHuiDiao;
}
/**
* 線條顏色
* @param lineColor
*/
public void setLineColor(int lineColor) {
this.lineColor = lineColor;
}
/**
* 選擇錯誤後的線條顏色
* @param lineColorCuo
*/
public void setLineColorCuo(int lineColorCuo) {
this.lineColorCuo = lineColorCuo;
}
/**
* 設置線條粗細
* @param linStrokeWidth
*/
public void setLinStrokeWidth(int linStrokeWidth) {
this.linStrokeWidth = linStrokeWidth;
}
private void drawAllLine(Canvas canvas) {
Paint mPaint = new Paint();
canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
mPaint.setAntiAlias(true);
mPaint.setFilterBitmap(true);
if(cuowu) {
mPaint.setColor(lineColorCuo);
}else {
mPaint.setColor(lineColor);
}
mPaint.setStrokeWidth(linStrokeWidth);
for (int i = 0; i < list.size(); i++) {
if (i == list.size() - 1) {
canvas.drawLine(list.get(i)[0], list.get(i)[1], xy[0], xy[1], mPaint);
} else {
canvas.drawLine(list.get(i)[0], list.get(i)[1], list.get(i + 1)[0], list.get(i + 1)[1], mPaint);
}
}
}
/**
* 控制子控件的換行
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int cellWidth = 0;//寬度偏差
int columns = 0;//保存每排放的第幾個view
int y = 0; //高度偏移量
int maxy = 0; //最大高度
int count = getChildCount();//子view數量
int gw = getMeasuredWidth() / 3;//獲取寬度1/3這裏主要是我一排放三個,看需求可以改成變量
for (int j = 0; j < count; j++) {
final View childView = getChildAt(j);
// 獲取子控件Child的寬高
int w = childView.getMeasuredWidth();
int h = childView.getMeasuredHeight();
// 計算子控件的頂點座標
int left = (gw - w) / 2 + cellWidth;
cellWidth += gw;//下一一個位置
int top = y + (gw - w) / 2;
// 佈局子控件
maxy = Math.max(gw, maxy);
columns++;
childView.layout(left, top, left + w, top + h);
//判斷一排是否到三個了,到三個換行
if (columns >= 3) {
cellWidth = 0;
y += maxy;
maxy = 0;
columns = 0;
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float[] fxy;
if (isTouch) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
chongZhi();
cuowu=false;
fxy = jx(event.getX(), event.getY());
if (fxy != null) {
list.add(fxy);
xy[0] = event.getX();
xy[1] = event.getY();
}
break;
case MotionEvent.ACTION_MOVE:
fxy = jx(event.getX(), event.getY());
if (fxy != null) {
list.add(fxy);
}
xy[0] = event.getX();
xy[1] = event.getY();
break;
case MotionEvent.ACTION_UP:
if (wanChengHuiDiao == null) {
list.clear();
lintp.clear();
} else {
wanChengHuiDiao.wanCheng(lintp);
}
break;
}
postInvalidate();
return true;
} else {
return false;
}
}
/**
* 判斷滑動的點是否和子類交叉
* @param x
* @param y
* @return
*/
public float[] jx(float x, float y) {
int count = getChildCount();
for (int j = 0; j < count; j++) {
View childView = getChildAt(j);
boolean t = true;
for (int i = 0; i < lintp.size(); i++) {
if (lintp.get(i) == j) {
t = false;
break;
}
}
if (t) {
if (childView.getX() < x && childView.getX() + childView.getWidth() > x) {
if (childView.getY() < y && childView.getY() + childView.getHeight() > y) {
lintp.add(j);
if (childView instanceof ZhuangTai) {
((ZhuangTai) childView).xuanZhong();
}
return new float[]{childView.getX() + childView.getWidth() / 2, childView.getY() + childView.getHeight() / 2};
}
}
}
}
return null;
}
/**
* 重置界面
*/
public void chongZhi() {
int count = getChildCount();
for (int j = 0; j < count; j++) {
View childView = getChildAt(j);
if (childView instanceof ZhuangTai) {
((ZhuangTai) childView).weiXuanZhong();
}
}
list.clear();
lintp.clear();
postInvalidate();
}
/**
* 密碼錯誤
*/
public void cuoWuJieMina(){
cuowu=true;
int count = getChildCount();
for (int j = 0; j < count; j++) {
View childView = getChildAt(j);
if (childView instanceof ZhuangTai) {
((ZhuangTai) childView).cuoWu();
}
}
postInvalidate();
}
public interface ZhuangTai {
void xuanZhong();
void cuoWu();
void weiXuanZhong();
}
public interface WanChengHuiDiao {
void wanCheng(List<Integer> list);
}
}
隨便實現的一個子類,正常用的ImageView的也不錯看需求了,現在這個實現很難看,你可以找設計設置幾張圖完事,項目在接口放裏面自己畫也可,感覺還是跟要UI圖方便......
public class Textvvvv extends TextView implements LinGesturePassword.ZhuangTai {
public Textvvvv(Context context) {
super(context);
}
public Textvvvv(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public Textvvvv(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public Textvvvv(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public void xuanZhong() {
setText("選中了");
setBackgroundColor(0xFF378FC9);
}
@Override
public void cuoWu() {
setText("選擇錯誤");
setBackgroundColor(0xFFff0000);
}
@Override
public void weiXuanZhong() {
setText("未選中");
setBackgroundColor(0xFF3ab826);
}
}
佈局文件隨便加就行自動佈局,但是要設置高度DP的話基本不會出現什麼大的佈局問題。這個視圖的是現實根據父視圖大小來實現平均的,可以改變LinGesturePassword的寬度設置居中然後會還看點,fLinGesturePassword的寬度必須大於三個的TextView的總寬度,不然就沒間距了或者重疊。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.ag.text.myapplication.LinGesturePassword
android:id="@+id/lin1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff"
android:layout_margin="10dp"
android:orientation="vertical">
<com.ag.text.myapplication.Textvvvv
android:layout_width="80dp"
android:layout_height="80dp"
android:background="#FF3ab826"
android:text="Hello World!"
android:layout_marginTop="50dp"
/>
<com.ag.text.myapplication.Textvvvv
android:layout_width="80dp"
android:layout_height="80dp"
android:background="#FF3ab826"
android:text="Hello World!"
android:layout_marginTop="50dp"
/>
<com.ag.text.myapplication.Textvvvv
android:layout_width="80dp"
android:layout_height="80dp"
android:background="#FF3ab826"
android:text="Hello World!"
android:layout_marginTop="50dp"
/>
<com.ag.text.myapplication.Textvvvv
android:layout_width="80dp"
android:layout_height="80dp"
android:background="#FF3ab826"
android:text="Hello World!"
android:layout_marginTop="50dp"
/>
<com.ag.text.myapplication.Textvvvv
android:layout_width="80dp"
android:layout_height="80dp"
android:background="#FF3ab826"
android:text="Hello World!"
android:layout_marginTop="50dp"
/>
<com.ag.text.myapplication.Textvvvv
android:layout_width="80dp"
android:layout_height="80dp"
android:background="#FF3ab826"
android:text="Hello World!"
android:layout_marginTop="50dp"
/>
<com.ag.text.myapplication.Textvvvv
android:layout_width="80dp"
android:layout_height="80dp"
android:background="#FF3ab826"
android:text="Hello World!"
android:layout_marginTop="50dp"
/>
<com.ag.text.myapplication.Textvvvv
android:layout_width="80dp"
android:layout_height="80dp"
android:background="#FF3ab826"
android:text="Hello World!"
android:layout_marginTop="50dp"
/>
<com.ag.text.myapplication.Textvvvv
android:layout_width="80dp"
android:layout_height="80dp"
android:background="#FF3ab826"
android:text="Hello World!"
android:layout_marginTop="50dp"
/>
<com.ag.text.myapplication.Textvvvv
android:layout_width="80dp"
android:layout_height="80dp"
android:background="#FF3ab826"
android:text="Hello World!"
android:layout_marginTop="50dp"
/>
</com.ag.text.myapplication.LinGesturePassword>
</LinearLayout>
使用
public class MainActivity extends AppCompatActivity {
LinGesturePassword lin1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lin1=findViewById(R.id.lin1);
// lin1.setLineColor();//線條顏色
// lin1.setLineColorCuo();//錯誤線條顏色
lin1.setLinStrokeWidth(20);//線條粗度
lin1.setWanChengHuiDiao(new LinGesturePassword.WanChengHuiDiao() {
@Override
public void wanCheng(List<Integer> list) {
String pw="";
for (int i = 0; i < list.size(); i++) {
pw+=list.get(i);
}
Log.e("aaa","密碼"+pw);
// lin1.cuoWuJieMina();//密碼錯誤是調用
// lin1.chongZhi();//重置界面
}
});
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("main","main"+event.getAction());
return super.onTouchEvent(event);
}
gitee可能會跟新呵呵~~