900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > 自定义九宫格解锁布局

自定义九宫格解锁布局

时间:2020-01-14 23:47:11

相关推荐

自定义九宫格解锁布局

自定义九宫格解锁是一个特别常见的布局,在一些金融类App里面见到的比较多,下面,我们就通过代码来实现一下这个布局.

第一步,我们先创建一个LockPatternView继承自View.

/*** 自定义九宫格*/public class LockPatternView extends View {public LockPatternView(Context context) {this(context,null);}public LockPatternView(Context context, @Nullable AttributeSet attrs) {this(context, attrs,0);}public LockPatternView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}}

第二步,绘制九个格子,每排三个,每个格子占屏幕宽的1/3(注意,竖屏就是占高的1/3)

先我们定义一个内部类Point.

class Point{//默认状态public static final int STATUS_NORMAL = 1;//选择状态public static final int STATUS_PRESSED = 2;//错误状态public static final int STATUS_ERROR = 3;//通过状态public static final int STATUS_PASS = 4;//中心点xint centerX;//中心点yint centerY;//下标,就是我们定义的密码1-9int index;//当前点的状态int status = STATUS_NORMAL;public Point(int centerX, int centerY, int index) {this.centerX = centerX;this.centerY = centerY;this.index = index;}

接下来我们定义一个3*3的数组来保存这9个点

private Point[][] mPoints = new Point[3][3];

下一步我们来获取这9个点的中心点的坐标

// 外圆的半径private int mDotRadius = 0;//内圆半径private int mInnerDotRadius = 0;private void initDot(){int mWidth = getWidth() - getPaddingLeft() - getPaddingRight();int mHeight = getHeight() - getPaddingTop() - getPaddingBottom();int offsetX = 0;int offsetY = 0;//这里就是我们支持横屏或者竖屏的代码if (mWidth > mHeight) {offsetX = (mWidth - mHeight) / 2 + getPaddingLeft();offsetY = getPaddingTop();mWidth = mHeight;} else {offsetX = getPaddingLeft();offsetY = (mHeight - mWidth) / 2 + getPaddingTop();mHeight = mWidth;}//每个点占1/3int squareWidth = mWidth/3;//外圆的半径为点的宽度的1/3,这样不会显得挤mDotRadius = squareWidth/3;//内圆半径为外圆半径的1/6mInnerDotRadius = mDotRadius/6;for (int i = 0;i<mPoints.length;i++) {for (int j = 0;j<mPoints[i].length;j++) {// 循环获取九个点mPoints[i][j] = new Point(offsetX + squareWidth * (j * 2 + 1) / 2,offsetY + squareWidth * (i * 2 + 1) / 2, i * mPoints.length + j+1);}}}

这时候我们已经完成了9个点的中心点坐标获取,下一步我们来绘制这九个点.

这个点我们只需要初始化一次,所以我们先定义一个变量来保存初始化状态

private boolean mIsInit = false;

下一步,我们需要初始化我们的画笔

// 默认的外圆画笔private Paint mOuterNormalPaint;// 默认的内圆画笔private Paint mInnerNormalPaint;/*** 初始化画笔**/private void initPaint() {// 默认的外圆画笔mOuterNormalPaint = new Paint();//颜色可以自定义mOuterNormalPaint.setColor(Color.BLACK);//外圆为空心的mOuterNormalPaint.setStyle(Paint.Style.STROKE);mOuterNormalPaint.setAntiAlias(true);mOuterNormalPaint.setStrokeWidth(mDotRadius / 9);// 默认的内圆画笔mInnerNormalPaint = new Paint();//颜色可以自定义mInnerNormalPaint.setColor(Color.BLACK);//内圆为实心的mInnerNormalPaint.setStyle(Paint.Style.FILL);mInnerNormalPaint.setAntiAlias(true);mInnerNormalPaint.setStrokeWidth(mDotRadius / 6);}

接下来我们在onDraw方法里面去初始化我们的点

@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if (!mIsInit){initDot();initPaint();mIsInit = true;}}

下一步,我们来绘制这9个点

private void drawDot(Canvas canvas) {for (int i = 0;i<mPoints.length;i++) {for (int j = 0;j<mPoints[i].length;j++) {// 循环绘制九个点Point point = mPoints[i][j];if (Point.STATUS_NORMAL == point.status) {canvas.drawCircle(point.centerX, point.centerY, mInnerDotRadius, mInnerNormalPaint);canvas.drawCircle(point.centerX, point.centerY, mDotRadius, mOuterNormalPaint);}}}}

然后,我们在onDraw调用drawDot(canvas)方法.

我们来运行一遍

现在我们的运行效果是这样的,你会发现触摸是没用的,下一步我们开始处理触摸事件.

首先我们重写onTouchEvent方法

学过事件分发的人应该知道,如果ACTION_DOWN事件不返回true,后面的事件我们是收不到的,所以我们的onTouchEvent方法我们直接返回true.

我们先来处理一下ACTION_DOWN事件.我们先来分析一下.

我们先获取一下按下的点的坐标,然后我们去查找一下这个点有没有在这9个点内部,如果在,就将这个点的status设置为STATUS_PRESSED.

我们看下代码.

@Overridepublic boolean onTouchEvent(MotionEvent event) {switch(event.getAction()){case MotionEvent.ACTION_DOWN:Point pressPoint = getTouchPoint(event.getX(),event.getY());if (null != pressPoint){pressPoint.status = Point.STATUS_PRESSED;}break;case MotionEvent.ACTION_MOVE:break;case MotionEvent.ACTION_UP:break;}invalidate();return true;}//获取当前触摸位置的点public Point getTouchPoint(float x,float y){for (int i = 0;i<mPoints.length;i++) {for (int j = 0;j<mPoints[i].length;j++) {// 循环遍历九个点Point point = mPoints[i][j];if (x>point.centerX-mDotRadius && x < point.centerX+mDotRadius && y>point.centerY-mDotRadius && y<point.centerY+mDotRadius){return point;}}}return null;}

下一步我们需要在绘制点的方法里面去加一个判断,绘制按下的点.

先第一步初始化按下的点绘制的画笔.

// 按下的外圆画笔private Paint mOuterPressedPaint;// 按下的内圆画笔private Paint mInnerPressedPaint;// 按下的外圆画笔mOuterPressedPaint = new Paint();mOuterPressedPaint.setColor(Color.BLUE);mOuterPressedPaint.setStyle(Paint.Style.STROKE);mOuterPressedPaint.setAntiAlias(true);mOuterPressedPaint.setStrokeWidth(mDotRadius / 9);// 按下的内圆画笔mInnerPressedPaint = new Paint();mInnerPressedPaint.setColor(Color.BLUE);mInnerPressedPaint.setStyle(Paint.Style.FILL);mInnerPressedPaint.setAntiAlias(true);mInnerPressedPaint.setStrokeWidth(mDotRadius / 6);

Paint的初始化一样放到initPaint方法里面

接着我们在drawDot方法里面增加status==STATUS_PRESSED的判断

else if(Point.STATUS_PRESSED == point.status){canvas.drawCircle(point.centerX, point.centerY, mInnerDotRadius, mInnerPressedPaint);canvas.drawCircle(point.centerX, point.centerY, mDotRadius, mOuterPressedPaint);}

这时候我们再运行一下,效果如下

我们可以看到,现在我们按下时有效果的,但是滑动还是没反应,下一步我们处理滑动事件.

还在跟之前的思想一样,我们拿到划过的坐标,然后去查找这个位置是不是在点内部,是的话就将点的status设置为STATUS_PRESSED ,代码如下;

case MotionEvent.ACTION_MOVE:Point movePoint = getTouchPoint(event.getX(),event.getY());if (null != movePoint){movePoint.status = Point.STATUS_PRESSED;}

效果如下

我们可以看到,这时侯我们已经滑动有效果了.

下一步我们绘制一下连线.

首先我们定义一个List来保存我们触摸过的点,

private List<Point> mSelectPoints = new ArrayList<Point>();

然后在onTouch事件中把按下的点和触摸过的点保存起来

@Overridepublic boolean onTouchEvent(MotionEvent event) {switch(event.getAction()){case MotionEvent.ACTION_DOWN:Point pressPoint = getTouchPoint(event.getX(),event.getY());if (null != pressPoint){mSelectPoints.add(pressPoint);pressPoint.status = Point.STATUS_PRESSED;}break;case MotionEvent.ACTION_MOVE:Point movePoint = getTouchPoint(event.getX(),event.getY());if (null != movePoint){if (!mSelectPoints.contains(movePoint)) {mSelectPoints.add(movePoint);}movePoint.status = Point.STATUS_PRESSED;}break;case MotionEvent.ACTION_UP:break;}invalidate();return true;}

还有个问题,我们一个点但没有到下一个点的时候我们也需要画最后一个点到当前位置的线,所以我们需要将当前的位置保存起来,在这里我们直接用全局变量保存就好了.

case MotionEvent.ACTION_MOVE:moveX = event.getX();moveY = event.getY();

直接赋值就好,接下来我们来绘制连线.

因为前面我们已经将当前触摸过的点保存起来了,所以我们只需要绘制点与点之间的连线和最后一个点到当前位置的连线.

// 初始化线的画笔mLinePaint = new Paint();mLinePaint.setColor(Color.BLUE);mLinePaint.setStyle(Paint.Style.STROKE);mLinePaint.setAntiAlias(true);mLinePaint.setStrokeWidth(mDotRadius / 9);//在onDraw方法里面调用drawLine@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if (!mIsInit){initDot();initPaint();mIsInit = true;}drawDot(canvas);if (mSelectPoints.size()>0){drawLine(canvas);}}private void drawLine(Canvas canvas){//绘制最后一点到当前位置的线if (moveX != 0 && moveY != 0) {Point lastPoint = mSelectPoints.get(mSelectPoints.size() - 1);canvas.drawLine(lastPoint.centerX, lastPoint.centerY, moveX, moveY, mLinePaint);}if (mSelectPoints.size()>1) {for (int i = 0; i < mSelectPoints.size()-1; i++) {Point point = mSelectPoints.get(i);Point nextPoint = mSelectPoints.get(i+1);if(Point.STATUS_PRESSED == point.status){canvas.drawLine(point.centerX, point.centerY, nextPoint.centerX, nextPoint.centerY, mLinePaint);}}}}

我们可以看下效果.

这个效果还是ok的,但我们发现有个问题,就是我们按下时如果没有按中点,按道理后面 的触摸也应该是没有效果的,所以我们需要先判断一下按下时是不是按中了点,再去处理MOVE事件,我们定义一个变量

private boolean mIsTouchPoint = false;

接下来在ACTION_DOWN事件中当按下的点是有效的,我们将再去处理ACTION_MOVE事件,代码如下.

@Overridepublic boolean onTouchEvent(MotionEvent event) {switch(event.getAction()){case MotionEvent.ACTION_DOWN:mIsTouchPoint = false;Point pressPoint = getTouchPoint(event.getX(),event.getY());if (null != pressPoint){mSelectPoints.add(pressPoint);pressPoint.status = Point.STATUS_PRESSED;mIsTouchPoint = true;}break;case MotionEvent.ACTION_MOVE:if (mIsTouchPoint) {moveX = event.getX();moveY = event.getY();Point movePoint = getTouchPoint(moveX, moveY);if (null != movePoint) {if (!mSelectPoints.contains(movePoint)) {mSelectPoints.add(movePoint);}movePoint.status = Point.STATUS_PRESSED;}}break;case MotionEvent.ACTION_UP:break;}invalidate();return true;}

这时候大家再跑一遍,会发现上面的问题解决了.

接下来我们来处理ACTION_UP事件.

在开始前我们需要提前获取到密码保存起来,以备touch完毕后检验.

接下来就是判断touch的值和设置进来的值是否相等来绘制成功和失败的效果,并回调给调用者.代码先不贴,最后来贴一个完整的.

先来看看效果

这个是错误的效果

这个是成功的.

下面我把代码全部贴出来给大家看看,需要的可以拷贝到自己的项目中去,谢谢大家.

package cn.npe1348.lib_lockpatternview;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.os.Handler;import android.os.Message;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import androidx.annotation.NonNull;import androidx.annotation.Nullable;import java.util.ArrayList;import java.util.List;/*** 自定义九宫格* 1.绘制九个格子,每排三个,每个格子占屏幕宽的1/3(注意,竖屏就是占高的1/3)*/public class LockPatternView extends View {private String mPassword = "123456";private Point[][] mPoints = new Point[3][3];// 外圆的半径private int mDotRadius = 0;//内圆半径private int mInnerDotRadius = 0;private boolean mIsInit = false;private boolean mIsTouchPoint = false;// 默认的外圆画笔private Paint mOuterNormalPaint;// 默认的内圆画笔private Paint mInnerNormalPaint;// 按下的外圆画笔private Paint mOuterPressedPaint;// 按下的内圆画笔private Paint mInnerPressedPaint;//默认线画笔private Paint mLinePaint;private Paint mErrorLinePaint;private Paint mPassLinePaint;private Paint mOuterErrorPaint;private Paint mOuterPassPaint;private Paint mInnerErrorPaint;private Paint mInnerPassPaint;private List<Point> mSelectPoints = new ArrayList<Point>();private float moveX;private float moveY;private OnUnLockListener mOnUnLockListener;public void setUnLockListener(OnUnLockListener onUnLockListener){this.mOnUnLockListener = onUnLockListener;}public interface OnUnLockListener{public void isUnLockSuccess(boolean success,String selectIndexStr);}public LockPatternView(Context context) {this(context,null);}public LockPatternView(Context context, @Nullable AttributeSet attrs) {this(context, attrs,0);}public LockPatternView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}public void setPassword(String password) {this.mPassword = mPassword;}private void initDot(){int mWidth = getWidth() - getPaddingLeft() - getPaddingRight();int mHeight = getHeight() - getPaddingTop() - getPaddingBottom();int offsetX = 0;int offsetY = 0;//这里就是我们支持横屏或者竖屏的代码if (mWidth > mHeight) {offsetX = (mWidth - mHeight) / 2 + getPaddingLeft();offsetY = getPaddingTop();mWidth = mHeight;} else {offsetX = getPaddingLeft();offsetY = (mHeight - mWidth) / 2 + getPaddingTop();mHeight = mWidth;}//每个点占1/3int squareWidth = mWidth/3;//外圆的半径为点的宽度的1/3,这样不会显得挤mDotRadius = squareWidth/3;//内圆半径为外圆半径的1/6mInnerDotRadius = mDotRadius/6;for (int i = 0;i<mPoints.length;i++) {for (int j = 0;j<mPoints[i].length;j++) {// 循环获取九个点mPoints[i][j] = new Point(offsetX + squareWidth * (j * 2 + 1) / 2,offsetY + squareWidth * (i * 2 + 1) / 2, i * mPoints.length + j+1);}}}/*** 初始化画笔**/private void initPaint() {// 默认的外圆画笔mOuterNormalPaint = new Paint();//颜色可以自定义mOuterNormalPaint.setColor(Color.BLACK);//外圆为空心的mOuterNormalPaint.setStyle(Paint.Style.STROKE);mOuterNormalPaint.setAntiAlias(true);mOuterNormalPaint.setStrokeWidth(mDotRadius / 9);// 默认的内圆画笔mInnerNormalPaint = new Paint();//颜色可以自定义mInnerNormalPaint.setColor(Color.BLACK);//内圆为实心的mInnerNormalPaint.setStyle(Paint.Style.FILL);mInnerNormalPaint.setAntiAlias(true);mInnerNormalPaint.setStrokeWidth(mDotRadius / 6);// 按下的外圆画笔mOuterPressedPaint = new Paint();mOuterPressedPaint.setColor(Color.BLUE);mOuterPressedPaint.setStyle(Paint.Style.STROKE);mOuterPressedPaint.setAntiAlias(true);mOuterPressedPaint.setStrokeWidth(mDotRadius / 9);// 按下的内圆画笔mInnerPressedPaint = new Paint();mInnerPressedPaint.setColor(Color.BLUE);mInnerPressedPaint.setStyle(Paint.Style.FILL);mInnerPressedPaint.setAntiAlias(true);mInnerPressedPaint.setStrokeWidth(mDotRadius / 6);// 线的画笔mLinePaint = new Paint();mLinePaint.setColor(Color.BLUE);mLinePaint.setStyle(Paint.Style.STROKE);mLinePaint.setAntiAlias(true);mLinePaint.setStrokeWidth(mDotRadius / 9);// 错误的线的画笔mErrorLinePaint = new Paint();mErrorLinePaint.setColor(Color.RED);mErrorLinePaint.setStyle(Paint.Style.STROKE);mErrorLinePaint.setAntiAlias(true);mErrorLinePaint.setStrokeWidth(mDotRadius / 9);// 通过的线的画笔mPassLinePaint = new Paint();mPassLinePaint.setColor(Color.GREEN);mPassLinePaint.setStyle(Paint.Style.STROKE);mPassLinePaint.setAntiAlias(true);mPassLinePaint.setStrokeWidth(mDotRadius / 9);//错误的外圆画笔mOuterErrorPaint = new Paint();mOuterErrorPaint.setColor(Color.RED);mOuterErrorPaint.setStyle(Paint.Style.STROKE);mOuterErrorPaint.setAntiAlias(true);mOuterErrorPaint.setStrokeWidth(mDotRadius / 9);//通过的外圆画笔mOuterPassPaint = new Paint();mOuterPassPaint.setColor(Color.GREEN);mOuterPassPaint.setStyle(Paint.Style.STROKE);mOuterPassPaint.setAntiAlias(true);mOuterPassPaint.setStrokeWidth(mDotRadius / 9);//错误的内圆画笔mInnerErrorPaint = new Paint();mInnerErrorPaint.setColor(Color.RED);mInnerErrorPaint.setStyle(Paint.Style.FILL);mInnerErrorPaint.setAntiAlias(true);mInnerErrorPaint.setStrokeWidth(mDotRadius / 6);//通过的内圆画笔mInnerPassPaint = new Paint();mInnerPassPaint.setColor(Color.GREEN);mInnerPassPaint.setStyle(Paint.Style.FILL);mInnerPassPaint.setAntiAlias(true);mInnerPassPaint.setStrokeWidth(mDotRadius / 6);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if (!mIsInit){initDot();initPaint();mIsInit = true;}drawDot(canvas);if (mSelectPoints.size()>0){drawLine(canvas);}}private void drawLine(Canvas canvas){//绘制最后一点到当前位置的线if (moveX != 0 && moveY != 0) {Point lastPoint = mSelectPoints.get(mSelectPoints.size() - 1);canvas.drawLine(lastPoint.centerX, lastPoint.centerY, moveX, moveY, mLinePaint);}if (mSelectPoints.size()>1) {for (int i = 0; i < mSelectPoints.size()-1; i++) {Point point = mSelectPoints.get(i);Point nextPoint = mSelectPoints.get(i+1);if(Point.STATUS_PRESSED == point.status){canvas.drawLine(point.centerX, point.centerY, nextPoint.centerX, nextPoint.centerY, mLinePaint);}else if(Point.STATUS_ERROR == point.status){canvas.drawLine(point.centerX, point.centerY, nextPoint.centerX, nextPoint.centerY, mErrorLinePaint);}else if(Point.STATUS_PASS == point.status){canvas.drawLine(point.centerX, point.centerY, nextPoint.centerX, nextPoint.centerY, mPassLinePaint);}}}}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch(event.getAction()){case MotionEvent.ACTION_DOWN:mIsTouchPoint = false;Point pressPoint = getTouchPoint(event.getX(),event.getY());if (null != pressPoint){mSelectPoints.add(pressPoint);pressPoint.status = Point.STATUS_PRESSED;mIsTouchPoint = true;}break;case MotionEvent.ACTION_MOVE:if (mIsTouchPoint) {moveX = event.getX();moveY = event.getY();Point movePoint = getTouchPoint(moveX, moveY);if (null != movePoint) {if (!mSelectPoints.contains(movePoint)) {mSelectPoints.add(movePoint);}movePoint.status = Point.STATUS_PRESSED;}}break;case MotionEvent.ACTION_UP:if (mIsTouchPoint) {moveX = 0;moveY = 0;String selectIndexStr = getSelectPointIndexStr();if (null != mPassword && mPassword.equals(selectIndexStr)) {if (null != mOnUnLockListener) {mOnUnLockListener.isUnLockSuccess(true,selectIndexStr);}setAllSelectPointPass();}else{if (null != mOnUnLockListener) {mOnUnLockListener.isUnLockSuccess(false,selectIndexStr);}setAllSelectPointError();}}break;}invalidate();return true;}/*** 获取选中的下标字符串* @return*/private String getSelectPointIndexStr(){String indexStr = "";for (int i = 0; i < mSelectPoints.size(); i++){indexStr+=mSelectPoints.get(i).index;}return indexStr;}private Handler mHandler = new Handler(){@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);clearSelectPointStatus();}};/*** 设置错误状态*/private void setAllSelectPointError(){for (int i = 0; i < mSelectPoints.size(); i++){mSelectPoints.get(i).status = Point.STATUS_ERROR;}invalidate();mHandler.postDelayed(new Runnable() {@Overridepublic void run() {mHandler.sendEmptyMessage(Point.STATUS_ERROR);}},1000);}/*** 设置通过状态*/private void setAllSelectPointPass(){for (int i = 0; i < mSelectPoints.size(); i++){mSelectPoints.get(i).status = Point.STATUS_PASS;}invalidate();mHandler.postDelayed(new Runnable() {@Overridepublic void run() {mHandler.sendEmptyMessage(Point.STATUS_PASS);}},1000);}/*** 清除选中点的状态*/private void clearSelectPointStatus(){for (int i = 0;i<mPoints.length;i++) {for (int j = 0;j<mPoints[i].length;j++) {// 循环绘制九个点mPoints[i][j].status = Point.STATUS_NORMAL;}}mSelectPoints.clear();invalidate();}//获取当前触摸位置的点private Point getTouchPoint(float x,float y){for (int i = 0;i<mPoints.length;i++) {for (int j = 0;j<mPoints[i].length;j++) {// 循环遍历九个点Point point = mPoints[i][j];if (x>point.centerX-mDotRadius && x < point.centerX+mDotRadius && y>point.centerY-mDotRadius && y<point.centerY+mDotRadius){return point;}}}return null;}private void drawDot(Canvas canvas) {for (int i = 0;i<mPoints.length;i++) {for (int j = 0;j<mPoints[i].length;j++) {// 循环绘制九个点Point point = mPoints[i][j];if (Point.STATUS_NORMAL == point.status) {canvas.drawCircle(point.centerX, point.centerY, mInnerDotRadius, mInnerNormalPaint);canvas.drawCircle(point.centerX, point.centerY, mDotRadius, mOuterNormalPaint);}else if(Point.STATUS_PRESSED == point.status){canvas.drawCircle(point.centerX, point.centerY, mInnerDotRadius, mInnerPressedPaint);canvas.drawCircle(point.centerX, point.centerY, mDotRadius, mOuterPressedPaint);}else if(Point.STATUS_ERROR == point.status){canvas.drawCircle(point.centerX, point.centerY, mInnerDotRadius, mInnerErrorPaint);canvas.drawCircle(point.centerX, point.centerY, mDotRadius, mOuterErrorPaint);}else if(Point.STATUS_PASS == point.status){canvas.drawCircle(point.centerX, point.centerY, mInnerDotRadius, mInnerPassPaint);canvas.drawCircle(point.centerX, point.centerY, mDotRadius, mOuterPassPaint);}}}}class Point{//默认状态public static final int STATUS_NORMAL = 1;//选择状态public static final int STATUS_PRESSED = 2;//错误状态public static final int STATUS_ERROR = 3;//通过状态public static final int STATUS_PASS = 4;//中心点xint centerX;//中心点yint centerY;//下标,就是我们定义的密码1-9int index;//当前点的状态int status = STATUS_NORMAL;public Point(int centerX, int centerY, int index) {this.centerX = centerX;this.centerY = centerY;this.index = index;}}}

关于这里面颜色和宽度的自定义大家可以自己去修改下,我会在后面的时间修改后上传到github上,链接下次上传后会写在这里.

就说这么多了,谢谢.

Github源码地址 源码

自定义属性已经添加了,大家可以去github查看源代码

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。