自定义滑动解锁View
- 需求如下:
 
近期需要做一个类似屏幕滑动解锁的功能,右划开始,左划暂停。
- 需求效果图如下

 - 实现效果展示

 - 自定义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传进来
- 调用方式如下
 
/**
-  
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专做工具类,大概就这样。
文章来源:网络 版权归原作者所有
上文内容不用于商业目的,如涉及知识产权问题,请权利人联系小编,我们将立即处理









