Android实现九宫格手势密码

本文实例为大家分享了vue + element ui实现锚点定位的具体代码,供大家参考,具体内容如下

介绍下自己编写的九宫格手势密码。先见图

思路:首先是9个格子,接着是格子连线;那么我们的步骤就有了。

1.手势监听,进行连线
2.格子的状态未连接(初始状态)、已连接的(没有结果前)、错误状态(有结果后)。(先这三个,可扩展,比如按下状态)
3.自定义viewgroup作为九宫格的容器,里面包含9个view(小格子)

一、先从简单的说起吧,9个小格子以及状态

为了扩展性,不自定义view,将三个状态和有关属性提取

1.提取属性,代码如下:

class NineChildInf {
        /**
         * 当前所在9宫格的位置
         * 从1开始
         */
        var index = 0
        /**
         * 是否被点亮
         */
        var isLight = false
        /**
         * 中心点所在父类容器内的坐标
         */
        var centerX = 0.toFloat()
        var centerY = 0.toFloat()
 
        fun setContent(index: Int, centerX: Float, centerY: Float) {
            this.index = index
            this.centerX = centerX
            this.centerY = centerY
        }
 
        constructor()
 
        fun updateCenterPoint(x: Float, y: Float) {
            this.centerX = x
            this.centerY = y
        }
 
        fun reset() {
            this.index = 0
            this.centerX = 0f
            this.centerY = 0f
            this.isLight = false
        }
 
        override fun toString(): String {
            return "NineChildInf(index=$index, isLight=$isLight, centerX=$centerX, centerY=$centerY)"
        }
    }

2.三个状态,代码如下

/**
 * Created by XinHeng on 2019/02/27.
 * describe:9宫格子view必须实现此接口
 */
abstract class NineChildParent<T : View>(var view: T) {
    protected open var context = view.context.applicationContext
    val NINE_CHILD_INF = NineChildInf()
    /**
     * 密码错误时的显示
     */
    abstract fun setErrorStatue()
 
    /**
     * 被选中时的显示
     */
    abstract fun setLightStatue()
 
    /**
     * 默认显示
     */
    abstract fun setDefaultStatue()
 
}

二、自定义九宫格容器,NineViewGroup。

既然是九宫格,那自然少不了这些属性,水平间隔、垂直间隔、最小有效连接数、当前状态、密码是否设置完成等。还需要将开启viewgroup的onDraw()方法。具体代码如下:

class NineViewGroup @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : ViewGroup(context, attrs, defStyleAttr) {
    /**
     * 水平间的间隔
     */
    private var paddingH = 60
    /**
     * 垂直间的间隔
     */
    private var paddingV = 60
    /**
     * 连线最小有效数字
     */
    var minEffectiveSize = 4
    /**
     * 小格子的宽高
     */
    private var childSlide: Int = 30
    private val ERROR_STATUE = 2
    private val LINKING_STATUE = 1
    private val DEFAULT_STATUE = 0
    /**
     * 当前状态
     * 0->最初状态 DEFAULT_STATUE
     * 1->正在连线中 LINKING_STATUE
     * 2->错误状态 ERROR_STATUE
     */
    private var nowStatue = DEFAULT_STATUE
    /**
     * 一次密码设置完成标志
     */
    private var complete = false
    /**
     * 线条宽度
     */
    private var lineWidth = 5
    private var lineColor = Color.parseColor("#33b5e5")
    private var errorLineColor = Color.RED
    private var childViews = ArrayList<NineChildParent<*>>(9)
    init {
        //使能调用onDraw()方法
        setWillNotDraw(false)
        var array = context.obtainStyledAttributes(attrs, R.styleable.NineViewGroup, defStyleAttr, 0)
        (0..array.indexCount).forEach {
            var index = array.getIndex(it)
            when (index) {
                R.styleable.NineViewGroup_nine_child_size -> childSlide = array.getDimensionPixelSize(index, childSlide)
                R.styleable.NineViewGroup_nine_line_color -> lineColor = array.getColor(index, lineColor)
                R.styleable.NineViewGroup_nine_error_line_color -> errorLineColor = array.getColor(index, errorLineColor)
                R.styleable.NineViewGroup_nine_effective_size -> minEffectiveSize = array.getInt(index, minEffectiveSize)
                R.styleable.NineViewGroup_nine_padding_h -> paddingH = array.getDimensionPixelSize(index, paddingH)
                R.styleable.NineViewGroup_nine_padding_v -> paddingV = array.getDimensionPixelSize(index, paddingV)
                R.styleable.NineViewGroup_nine_line_width -> lineWidth = array.getDimensionPixelSize(index, lineWidth)
            }
        }
        array.recycle()
      
    }
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        var width = childSlide * 3 + paddingLeft + paddingRight + paddingH * 2
        var height = childSlide * 3 + paddingTop + paddingBottom + paddingV * 2
        setMeasuredDimension(width, height)
        //又忘了计算子view的大小了。。。
        measureChildren(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
    }
    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        var childView: View
        var top: Int = paddingTop
        var left: Int = paddingLeft
        var right: Int
        var bottom: Int
        if (childCount > 0) {
            (0 until childCount).forEach {
                childView = getChildAt(it)
                right = left + childView.measuredWidth
                bottom = top + childView.measuredHeight
                //Log.e("TAG", "onLayout: $left $top $right $bottom")
                var nineChildInf = (childViews[it]).NINE_CHILD_INF
                nineChildInf.setContent(it + 1, (left + right) / 2f, (top + bottom) / 2f)
                //Log.e("TAG", "onLayout: child=$nineChildInf")
                childView.layout(left, top, right, bottom)
                if ((it + 1) % 3 == 0) {
                    left = paddingLeft
                    top = bottom + paddingV
                } else {
                    left = right + paddingH
                }
            }
        }
    }
}

三、手势监听、连线

1.手势监听,重写onTouchEvent()方法,必要需要时重写onInterceptTouchEvent()方法进行拦截(跟情况而定,这里就不多说了)。简单的三个手势状态按下、移动、抬起。在各个状态下,记录坐标,并且更新子view(小格子)的ui,还有线条。代码片段如下:

override fun onTouchEvent(event: MotionEvent): Boolean {
        if (childCount == 0 || complete) {
            return super.onTouchEvent(event)
        }
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                //记录落点
                lastX = event.x
                lastY = event.y
                downUpdateChild(lastX, lastY)
            }
            MotionEvent.ACTION_MOVE -> {
                lastX = event.x
                lastY = event.y
                moveUpdateChild(lastX, lastY)
            }
            MotionEvent.ACTION_UP -> {
                complete = true
                //统计
                upUpdateChild()
            }
        }
        return true
    }

2.连线,在容器的onDraw()方法,进行画线操作,代码片段如下:

override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (!showLine) {
            return
        }
        paint.color = when (nowStatue) {
            ERROR_STATUE -> errorLineColor
            else -> lineColor
        }
        if (points.size > 1) {
            (1 until points.size).forEach {
                var pointXYStart = points[it - 1].NINE_CHILD_INF
                var pointXYEnd = points[it].NINE_CHILD_INF
                canvas.drawLine(pointXYStart.centerX, pointXYStart.centerY, pointXYEnd.centerX, pointXYEnd.centerY, paint)
            }
        }
        if (lastX > 0 && points.size > 0) {
            var pointXY = points[points.size - 1].NINE_CHILD_INF
            canvas.drawLine(pointXY.centerX, pointXY.centerY, lastX, lastY, paint)
        }
    }

四、进行到这一步,大致的步骤就是这了。

但是还有一些细节:比如连线中需要判断中间是否含有小格子、判断触点是否在小格子上、连接完成后的回调、错误状态显示、恢复初始状态等。粘出部分代码片段(这些只是能实现效果,还可以优化,交给大家了):

1.判断触点是否在小格子上

private fun childContains(x: Float, y: Float): Boolean {
        (0 until childCount).forEach {
            var childAt = getChildAt(it)
            //这一句,循环判断,是否属于其范围
            if (x >= childAt.left && x < childAt.right && y >= childAt.top && y < childAt.bottom) {
                return if (!childViews[it].NINE_CHILD_INF.isLight) {
                    if (points.size > 0) {
                        checkMiddleChild(points[points.size - 1], childViews[it])?.run {
                            if (!NINE_CHILD_INF.isLight) {
                                buffer.append(NINE_CHILD_INF.index)
                                changeLightStatue(this)
                            }
                        }
                    }
                    buffer.append(it + 1)
                    //TODO 改变子view的UI状态
                    changeLightStatue(childViews[it])
                    true
                } else {
                    false
                }
            }
        }
        return false
    }

2.判断中间是否含有小格子

private fun checkMiddleChild(nineChildParent: NineChildParent<*>, nineChildParent1: NineChildParent<*>): NineChildParent<*>? {
        var index = nineChildParent.NINE_CHILD_INF.index
        var index1 = nineChildParent1.NINE_CHILD_INF.index
        var sum = index + index1
        if (sum == 10) {
            return childViews[4]
        } else if (index % 2 != 0 && index1 % 2 != 0) {
            if ((sum == 4 || sum == 16) || (sum == 8 && (index == 1 || index1 == 1))||(sum == 12 && (index == 3 || index1 == 3)))
                return childViews[sum / 2 - 1]
        }
        return null
    }

五、如有bug欢迎留言指出,下面粘出九宫格容器的全部代码。

/**
 * Created by XinHeng on 2019/01/29.
 * describe:九宫格的容器
 */
class NineViewGroup @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : ViewGroup(context, attrs, defStyleAttr) {
    /**
     * 水平间的间隔
     */
    private var paddingH = 60
    /**
     * 垂直间的间隔
     */
    private var paddingV = 60
    /**
     * 是否有第一个选中
     */
    private var firstSelect = true
    private val ERROR_STATUE = 2
    private val LINKING_STATUE = 1
    private val DEFAULT_STATUE = 0
    /**
     * 是否显示线条
     */
    var showLine = false
    /**
     * 连线最小有效数字
     */
    var minEffectiveSize = 4
    /**
     * 当前状态
     * 0->最初状态 DEFAULT_STATUE
     * 1->正在连线中 LINKING_STATUE
     * 2->错误状态 ERROR_STATUE
     */
    private var nowStatue = DEFAULT_STATUE
    /**
     * 一次密码设置完成标志
     */
    private var complete = false
    /**
     * 线条宽度
     */
    private var lineWidth = 5
    private var lastX: Float = 0f
    private var lastY: Float = 0f
    private var buffer = StringBuilder()
    private var points = ArrayList<NineChildParent<*>>(9)
    private var childViews = ArrayList<NineChildParent<*>>(9)
    /**
     * 小格子的宽高
     */
    private var childSlide: Int = 30
    private var lineColor = Color.parseColor("#33b5e5")
    private var errorLineColor = Color.RED
    var onNineViewGroupListener: OnNineViewGroupListener? = null
        set(value) {
            field = value
            value?.let {
                setChildMode(it)
            }
        }
    private val paint = Paint().apply {
        isAntiAlias = true
        isDither = true
    }
 
    init {
        //使能调用onDraw()方法
        setWillNotDraw(false)
        var array = context.obtainStyledAttributes(attrs, R.styleable.NineViewGroup, defStyleAttr, 0)
        (0..array.indexCount).forEach {
            var index = array.getIndex(it)
            when (index) {
                R.styleable.NineViewGroup_nine_child_size -> childSlide = array.getDimensionPixelSize(index, childSlide)
                R.styleable.NineViewGroup_nine_line_color -> lineColor = array.getColor(index, lineColor)
                R.styleable.NineViewGroup_nine_error_line_color -> errorLineColor = array.getColor(index, errorLineColor)
                R.styleable.NineViewGroup_nine_effective_size -> minEffectiveSize = array.getInt(index, minEffectiveSize)
                R.styleable.NineViewGroup_nine_padding_h -> paddingH = array.getDimensionPixelSize(index, paddingH)
                R.styleable.NineViewGroup_nine_padding_v -> paddingV = array.getDimensionPixelSize(index, paddingV)
                R.styleable.NineViewGroup_nine_show_line -> showLine = array.getBoolean(index, showLine)
                R.styleable.NineViewGroup_nine_line_width -> lineWidth = array.getDimensionPixelSize(index, lineWidth)
            }
        }
        array.recycle()
        paint.strokeWidth = lineWidth.toFloat()
    }
 
    private fun setChildMode(onNineViewGroupListener: OnNineViewGroupListener) {
        removeAllViews()
        childViews.clear()
        (0..8).forEach {
            var mode = onNineViewGroupListener.getChildMode()
            mode.NINE_CHILD_INF.index = it + 1
            mode.setDefaultStatue()
            addView(mode.view, getLp())
            childViews.add(mode)
        }
    }
 
    private fun getLp(): LayoutParams {
        return LayoutParams(childSlide, childSlide)
    }
 
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        var width = childSlide * 3 + paddingLeft + paddingRight + paddingH * 2
        var height = childSlide * 3 + paddingTop + paddingBottom + paddingV * 2
        setMeasuredDimension(width, height)
        //又忘了计算子view的大小了。。。
        measureChildren(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
    }
 
    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        var childView: View
        var top: Int = paddingTop
        var left: Int = paddingLeft
        var right: Int
        var bottom: Int
        if (childCount > 0) {
            (0 until childCount).forEach {
                childView = getChildAt(it)
                right = left + childView.measuredWidth
                bottom = top + childView.measuredHeight
                //Log.e("TAG", "onLayout: $left $top $right $bottom")
                var nineChildInf = (childViews[it]).NINE_CHILD_INF
                nineChildInf.setContent(it + 1, (left + right) / 2f, (top + bottom) / 2f)
                //Log.e("TAG", "onLayout: child=$nineChildInf")
                childView.layout(left, top, right, bottom)
                if ((it + 1) % 3 == 0) {
                    left = paddingLeft
                    top = bottom + paddingV
                } else {
                    left = right + paddingH
                }
            }
        }
    }
 
    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
        return true
    }
    override fun onTouchEvent(event: MotionEvent): Boolean {
        if (childCount == 0 || complete) {
            return super.onTouchEvent(event)
        }
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                //记录落点
                lastX = event.x
                lastY = event.y
                downUpdateChild(lastX, lastY)
            }
            MotionEvent.ACTION_MOVE -> {
                lastX = event.x
                lastY = event.y
                moveUpdateChild(lastX, lastY)
            }
            MotionEvent.ACTION_UP -> {
                complete = true
                //统计
                upUpdateChild()
            }
        }
        return true
    }
 
    private fun downUpdateChild(x: Float, y: Float) {
        firstSelect = childContains(x, y)
    }
 
    private fun moveUpdateChild(x: Float, y: Float) {
        if (firstSelect) {
            moveUpdateLineAndChildView(x, y)
        } else {
            downUpdateChild(x, y)
        }
    }
 
    private fun moveUpdateLineAndChildView(x: Float, y: Float) {
        if (points.size != childCount)
            childContains(x, y)
        invalidate()
    }
 
    private fun upUpdateChild() {
        var effective = points.size >= minEffectiveSize
        onNineViewGroupListener?.complete(effective, buffer.toString())
    }
 
    /**
     * 错误状态展示
     */
    fun showErrorStatue() {
        nowStatue = ERROR_STATUE
        points.forEach {
            it.setErrorStatue()
        }
        invalidate()
        resetStatueDelayed(500)
    }
 
    /**
     * 恢复初始状态
     */
    private fun resetStatue() {
        points.clear()
        firstSelect = false
        lastX = 0f
        lastY = 0f
        buffer.clear()
        nowStatue = DEFAULT_STATUE
        (0 until childCount).forEach {
            var nineChildParent = childViews[it]
            nineChildParent.setDefaultStatue()
            nineChildParent.NINE_CHILD_INF.isLight = false
        }
        invalidate()
        complete = false
    }
 
    fun resetStatueDelayed(time: Int) {
        postDelayed({ resetStatue() }, time.toLong())
    }
 
    private fun childContains(x: Float, y: Float): Boolean {
        (0 until childCount).forEach {
            var childAt = getChildAt(it)
            if (x >= childAt.left && x < childAt.right && y >= childAt.top && y < childAt.bottom) {
                return if (!childViews[it].NINE_CHILD_INF.isLight) {
                    if (points.size > 0) {
                        checkMiddleChild(points[points.size - 1], childViews[it])?.run {
                            if (!NINE_CHILD_INF.isLight) {
                                buffer.append(NINE_CHILD_INF.index)
                                changeLightStatue(this)
                            }
                        }
                    }
                    buffer.append(it + 1)
                    //TODO 改变子view的UI状态
                    changeLightStatue(childViews[it])
                    true
                } else {
                    false
                }
            }
        }
        return false
    }
 
    private fun changeLightStatue(childParent: NineChildParent<*>) {
        childParent.NINE_CHILD_INF.isLight = true
        childParent.setLightStatue()
        points.add(childParent)//记录
    }
 
    private fun checkMiddleChild(nineChildParent: NineChildParent<*>, nineChildParent1: NineChildParent<*>): NineChildParent<*>? {
        var index = nineChildParent.NINE_CHILD_INF.index
        var index1 = nineChildParent1.NINE_CHILD_INF.index
        var sum = index + index1
        if (sum == 10) {
            return childViews[4]
        } else if (index % 2 != 0 && index1 % 2 != 0) {
            if ((sum == 4 || sum == 16) || (sum == 8 && (index == 1 || index1 == 1))||(sum == 12 && (index == 3 || index1 == 3)))
                return childViews[sum / 2 - 1]
        }
        return null
    }
 
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (!showLine) {
            return
        }
        paint.color = when (nowStatue) {
            ERROR_STATUE -> errorLineColor
            else -> lineColor
        }
        if (points.size > 1) {
            (1 until points.size).forEach {
                var pointXYStart = points[it - 1].NINE_CHILD_INF
                var pointXYEnd = points[it].NINE_CHILD_INF
                canvas.drawLine(pointXYStart.centerX, pointXYStart.centerY, pointXYEnd.centerX, pointXYEnd.centerY, paint)
            }
        }
        if (lastX > 0 && points.size > 0) {
            var pointXY = points[points.size - 1].NINE_CHILD_INF
            canvas.drawLine(pointXY.centerX, pointXY.centerY, lastX, lastY, paint)
        }
    }
 
    interface OnNineViewGroupListener {
        /**
         * 子view
         */
        fun getChildMode(): NineChildParent<*>
 
        /**
         * 密码设置结束
         * @param effective 是否有效
         * @param password 密码
         */
        fun complete(effective: Boolean, password: String)
    }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android自定义View九宫格手势密码解锁

    由于公司新的项目需要用到九宫格手势密码解锁的功能,于是觉得自己写一个.废话不多说,直接上效果图: 首选我们来分析下实现的思路: 1. 绘制出相对于这个View的居中的九个点,作为默认状态的点 2. 点击屏幕的时候是否点击在这九个点上 3. 在屏幕上滑动的时候,绘制两个点之间的线条,以及选中状态的点 4. 手指离开屏幕的时候判断手势密码是否正确,如若错误这把错误状态下的点和线绘制出来. 具体实现: 首先我们得绘制出默认正常状态下的九个点: /** * 点的bean * Created by Adm

  • Android九宫格手势密码代码设计

    最近因为项目需要用到九宫格密码(也叫手势轨迹密码),特地去学习了一下九宫格密码的实现原来.可能有人认为九宫格密码事例网上到处都有,很多甚至直接拷贝过来就可以运行为什么还要学习呢.还特地写到网上有必要吗?其实写这个目的是为了增强和锻炼自己的语言组织能力.其实如果只是既然是学习就不光是要知道答案(完成效果)更重要的是知道他什么实现. 好了题外话就不多说了,要实现九宫格密码要解决的问题有: 1.给九宫格密码界面布局九个点,即确定每个节点的位置.每个点到上下左右的距离都是相同的.即九个点刚好围成一个正方

  • 纯android代码实现九宫格手势密码

    这几天项目中要加九宫格手势密码,在网上搜了大量资料,大部分都是以图片实现为主,集合部分代码,android实现纯代码九宫格. 好了,不废话了,先上图. 效果大概就是这样,逻辑自己实现,我只上这个自定义控件的代码. 1.    point.Java  点的位置 /** * 点位置 */ public class Point { public static int STATE_NORMAL = 0; public static int STATE_CHECK = 1; // public stati

  • Android实现九宫格手势解锁

    本文为大家分享了Android九宫格手势解锁的具体代码,供大家参考,具体内容如下 这里是使用的开源库GestureLibray 里面有关于这个东西的介绍和接入方式,这里就不累赘了,我只是说下里面没有的. 关于这个库的使用: protected void initViews() { //设置模式 LockMode lockMode = (LockMode) getIntent().getSerializableExtra(Config.INTENT_SECONDACTIVITY_KEY); //是

  • Android 自定义九宫格手势锁

    预览效果图如下: 主要的方法是重写View.onTouchEvent( MotionEvent event ) , 常用的三个操作:ACTION_DOWN 手指触摸屏幕 ; ACTION_UP 手指离开屏幕; ACTION_MOVE手指在屏幕滑动. 如果该方法返回true ,表示该事件已经被View处理,不再向上层的View或Activity传递 : 如果返回false, 表示事件未处理,继续传递. 具体代码如下: package com.ninegrid; import android.con

  • Android实现九宫格手势密码

    本文实例为大家分享了vue + element ui实现锚点定位的具体代码,供大家参考,具体内容如下 介绍下自己编写的九宫格手势密码.先见图 思路:首先是9个格子,接着是格子连线:那么我们的步骤就有了. 1.手势监听,进行连线2.格子的状态未连接(初始状态).已连接的(没有结果前).错误状态(有结果后).(先这三个,可扩展,比如按下状态)3.自定义viewgroup作为九宫格的容器,里面包含9个view(小格子) 一.先从简单的说起吧,9个小格子以及状态 为了扩展性,不自定义view,将三个状态

  • Android自定义UI手势密码终结版

    之前写过3篇手势密码的demo,不过没有集成到真实的企业项目中,这几天正好领到一个手势密码项目,昨天刚好弄完,今天抽空整理下,目前还没有完善,有一些地方需要更改,不过基本的流程都可以跑通了. 源码下载地址:http://xiazai.jb51.net/201610/yuanma/AndroidGestureLock(jb51.net).rar 先看主界面的入口把.里面有2个button(一个是设置手势密码.一个是校验手势密码) activity_main.xml <RelativeLayout

  • Android自定义UI手势密码改进版源码下载

    在之前文章的铺垫下,再为大家分享一篇:Android手势密码,附源码下载,不要错过. 源码下载:http://xiazai.jb51.net/201610/yuanma/androidLock(jb51.net).rar 先看第一张图片的布局文件 activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://sc

  • Android实现支付宝手势密码功能

    今天就给大家分享android实现支付宝手势密码,很常见,像现在用微信支付,支付宝支付的时候都要自己设置的4位PIN码,然后输入PIN码后立即调用支付接口去支付,毫无疑问的安全问题,不会觉得手机被人拿了后却不知道自己网银的钱的去向,这也是正常安全逻辑性问题. 好,下面我们来看下具体的实现: 入口,启动页面就简单的几个按钮,布局贴出来,代码如下: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android

  • Android自定义UI手势密码改进版

    接着第一个Android UI手势密码设计的基础上继续改进,效果图如下 activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layo

  • Android自定义控件实现手势密码

    Android手势解锁密码效果图 首先呢想写这个手势密码的想法呢,完全是凭空而来的,然后笔者就花了一天时间弄出来了.本以为这个东西很简单,实际上手的时候发现,还有很多逻辑需要处理,稍不注意就容易乱套.写个UI效果图大约只花了3个小时,但是处理逻辑就处理了2个小时!废话不多说,下面开始讲解.      楼主呢,自己比较自定义控件,什么东西都掌握在自己的手里感觉那是相当不错(对于赶工期的小伙瓣儿们还是别手贱了,非常容易掉坑),一有了这个目标,我就开始构思实现方式.      1.整个自定义控件是继承

随机推荐