打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
android开发之Android 自定义滑动解锁View
userphoto

2023.08.09 上海

关注

自定义滑动解锁View             

1. 需求如下:

近期需要做一个类似屏幕滑动解锁的功能,右划开始,左划暂停。

2. 需求效果图如下

3. 实现效果展示

4. 自定义view如下

/**

 * Desc 自定义滑动解锁View

 * Author ZY

 * Mail sunnyfor98@gmail.com

 * Date 2021/5/17 11:52

 */

@SuppressLint("ClickableViewAccessibility")

class SlideSwitchButton : ViewGroup {

    constructor(context: Context?) : this(context, null)

    constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0)

    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : this(

        context,

        attrs,

        defStyleAttr, 0

    )

    constructor(

        context: Context?,

        attrs: AttributeSet?,

        defStyleAttr: Int,

        defStyleRes: Int

    ) : super(context, attrs, defStyleAttr, defStyleRes)

    var duration = 300

    var isOpen = false

    var scrollView: ScrollView? = null

    var onSwitchListener: ((isOpen: Boolean) -> Unit)? = null

    private var itemHeight = 0

    private var itemPadding = 0

    private var parentWidth = 0

    private val stopImgView: ImageView by lazy {

        ImageView(context).apply {

            setImageResource(R.drawable.f1_svg_btn_stop)

        }

    }

    private val startImgView: ImageView by lazy {

        ImageView(context).apply {

            setImageResource(R.drawable.f1_svg_btn_start)

        }

    }

    private val hintView: TextView by lazy {

        TextView(context).apply {

            setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.dp_14))

            compoundDrawablePadding = resources.getDimension(R.dimen.dp_5).toInt()

            setTextColor(Color.parseColor("#727b9f"))

        }

    }

    init {

        setBackgroundResource(R.drawable.f1_sel_bg_slide_btn)

        addView(hintView)

        updateHint()

        addView(stopImgView)

        addView(startImgView)

        var x = 0

        startImgView.setOnTouchListener { v, event ->

            when (event.action) {

                MotionEvent.ACTION_DOWN -> {

                    scrollView?.requestDisallowInterceptTouchEvent(true)

                    x = event.x.toInt()

                }

                MotionEvent.ACTION_UP -> {

                    if (startImgView.x < (parentWidth - startImgView.width) / 2) {

                        play(false)

                    } else {

                        play(true)

                    }

                    scrollView?.requestDisallowInterceptTouchEvent(false)

                }

                MotionEvent.ACTION_MOVE -> {

                    val lastX = event.x - x

                    if (startImgView.x + lastX > parentWidth - itemPadding - startImgView.width) {

                        return@setOnTouchListener true

                    }

                    if (startImgView.x + lastX < itemPadding) {

                        return@setOnTouchListener true

                    }

                    startImgView.x += lastX

                }

            }

            return@setOnTouchListener true

        }

    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {

        super.onMeasure(widthMeasureSpec, heightMeasureSpec)

        setMeasuredDimension(widthMeasureSpec, resources.getDimension(R.dimen.dp_90).toInt())

        itemPadding = resources.getDimension(R.dimen.dp_5).toInt()

        itemHeight = resources.getDimension(R.dimen.dp_80).toInt()

        parentWidth = MeasureSpec.getSize(widthMeasureSpec)

    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {

        stopImgView.layout(

            itemPadding,

            itemPadding,

            itemPadding + itemHeight,

            itemPadding + itemHeight

        )

        startImgView.layout(

            itemPadding,

            itemPadding,

            itemPadding + itemHeight,

            itemPadding + itemHeight

        )

        val len =

            hintView.paint.measureText(hintView.text.toString()) + resources.getDimension(R.dimen.dp_24)

        val let = (r - len) / 2

        hintView.layout(

            let.toInt(),

            resources.getDimension(R.dimen.dp_35).toInt(),

            (let + len).toInt(),

            resources.getDimension(R.dimen.dp_55).toInt()

        )

    }

    /**

     * flag tue为开始 false为停止

     */

    private fun play(flag: Boolean) {

        val mStart = startImgView.x

        val mEnd = if (flag) {

            parentWidth - itemPadding * 2 - startImgView.width.toFloat()

        } else {

            stopImgView.x - itemPadding

        }

        val animatorOBJ =

            ObjectAnimator.ofFloat(startImgView, "translationX", mStart, mEnd)

        animatorOBJ.duration = duration.toLong()

        animatorOBJ.addListener(object : Animator.AnimatorListener {

            override fun onAnimationRepeat(animation: Animator?) {

            }

            override fun onAnimationEnd(animation: Animator?) {

                updateHint(flag)

                if (flag != isOpen) {

                    isOpen = flag

                    onSwitchListener?.invoke(flag)

                }

            }

            override fun onAnimationCancel(animation: Animator?) {

            }

            override fun onAnimationStart(animation: Animator?) {

            }

        })

        animatorOBJ.start()

    }

    private fun updateHint(lock: Boolean = false) {

        val icon = if (lock) {

            hintView.text = "滑动停止"

            ResourcesCompat.getDrawable(resources, R.drawable.f1_svg_left_arrow, null)

        } else {

            hintView.text = "滑动开始"

            ResourcesCompat.getDrawable(resources, R.drawable.f1_svg_right_arrow, null)

        }

        icon?.setBounds(

            0,

            0,

            resources.getDimension(R.dimen.dp_14).toInt(),

            resources.getDimension(R.dimen.dp_12).toInt()

        )

        if (lock) {

            hintView.setCompoundDrawables(icon, null, null, null)

        } else {

            hintView.setCompoundDrawables(null, null, icon, null)

        }

    }

    fun stop() {

        play(false)

    }

    fun start() {

        play(true)

    }

}

这里需要注意一点:页面过长时,ScrollView和SlideSwitchButton滑动事件会冲突,所以需要吧scrollView传进来

5. 调用方式如下

/**

 * Desc 自定义滑动解锁View

 * Author ZY

 * Mail sunnyfor98@gmail.com

 * Date 2021/5/28 17:48

 */

class SlideSwitchButtonActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView(R.layout.f1_act_main)

        btn_start.scrollView = scrollView

        btn_start.onSwitchListener = {

            if (it) {

                Toast.makeText(this,"开始操作",Toast.LENGTH_LONG).show()

                btn_start.start()

            } else {

                Toast.makeText(this,"停止操作",Toast.LENGTH_LONG).show()

                btn_start.stop()

            }

        }

    }

}

之前封装了一版ZyFrame框架,集工具类、自定义组件、网络请求框架一体,感觉用起来有些厚重,接下来会抽时间做拆分,ZyFrame保留网络请求功能,ZyUI专做自定义组件,ZyTool专做工具类,大概就这样。

文章来源:网络  版权归原作者所有

上文内容不用于商业目的,如涉及知识产权问题,请权利人联系小编,我们将立即处理

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
开发一个android桌面
Android中dp,px,sp概念梳理以及如何做到屏幕适配
Android Dimension转换算法原理分析
日历控件开发详解0
Android GradientDrawable高级应用 以后完全用不上美工了
Android 中自定义属性(attr.xml,...
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服