Day26 - 收放工具按钮

连假是真的懒,今天继续做点简单的东西。

主要是悬浮视窗的几个按钮,我想做成平常能自动收起,触碰画面时再展开的效果。

收合按纽

首先改一下layout

将几个按钮放到LinearLayout

    <LinearLayout
        android:id="@+id/toolsLayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/one_grid_unit"
        android:layout_marginTop="@dimen/one_grid_unit"
        android:orientation="horizontal"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">
        
        <!-- ... -->
    </LinearLayout>

接着做收起的Runnable

private val collapseAnimatorSet = AnimatorSet()

private val collapseToolsRunnable = Runnable {
    val toolsAnimator = ObjectAnimator.ofFloat(
        binding.toolsLayout,
        "translationY",
        0f,
        -100f.dpToPx(applicationContext).toFloat()
    )
    val closeAnimator = ObjectAnimator.ofFloat(
        binding.close,
        "translationX",
        0f,
        100f.dpToPx(applicationContext).toFloat()
    )
    collapseAnimatorSet.duration = 300L
    collapseAnimatorSet.interpolator = AccelerateInterpolator()
    collapseAnimatorSet.playTogether(toolsAnimator, closeAnimator)
    collapseAnimatorSet.start()
}

在OnCreat注册collapseToolsRunnable

private val collapseToolsInterval = 3000L

override fun onCreate() {
    // ...
    handler.postDelayed(collapseToolsRunnable, collapseToolsInterval)
    // ...
}

展开按钮

展开的时机点是在触碰RecyclerView时,首先先在Day22中客制的ControllableRecyclerView中加入Callback,并在dispatchTouchEvent中呼叫:

class ControllableRecyclerView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : RecyclerView(context, attrs, defStyleAttr) {

    var touchable = true
    var touchCallback: ((MotionEvent) -> Unit)? = null

    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
        touchCallback?.invoke(ev)
        return (touchable && super.dispatchTouchEvent(ev))
    }
}

ObserverService中撰写Callback:

private var isExpanding = false
    
override fun onCreate() {
    // ...
    binding.recyclerView.touchCallback = { event ->
        collapseAnimatorSet.cancel()
        handler.removeCallbacks(collapseToolsRunnable)
        if (event.actionMasked == MotionEvent.ACTION_UP
            || event.actionMasked == MotionEvent.ACTION_CANCEL
        ) {
            handler.postDelayed(collapseToolsRunnable, collapseToolsInterval)
        } else {
            if (binding.toolsLayout.translationY != 0f && !isExpanding) {
                isExpanding = true
                val toolsAnimator = ObjectAnimator.ofFloat(
                    binding.toolsLayout,
                    "translationY",
                    binding.toolsLayout.translationY,
                    0f
                )
                val closeAnimator = ObjectAnimator.ofFloat(
                    binding.close,
                    "translationX",
                    binding.close.translationX,
                    0f
                )
                val animatorSet = AnimatorSet()
                animatorSet.duration = 300L
                animatorSet.interpolator = AccelerateInterpolator()
                animatorSet.playTogether(toolsAnimator, closeAnimator)
                animatorSet.addListener(object : Animator.AnimatorListener {
                    override fun onAnimationEnd(p0: Animator?) {
                        isExpanding = false
                    }

                    override fun onAnimationCancel(p0: Animator?) {
                        binding.toolsLayout.translationY = 0f
                        binding.close.translationX = 0f
                        isExpanding = false
                    }

                    override fun onAnimationStart(p0: Animator?) {}

                    override fun onAnimationRepeat(p0: Animator?) {}

                })
                animatorSet.start()
            }
        }
    }
    // ...
}

基本上就是判断状态以及从几个View的目前位置归0。

其他注意

collapseToolsRunnable除了这两个点外也要在moveresize等功能中做取消/注册,避免功能冲突。
并且在onDestroy中取消Runnable。

实际效果

https://i.imgur.com/ua4zGNj.gif


<<:  [Python 爬虫这样学,一定是大拇指拉!] DAY26 - 实战演练:多执行绪 - 抓取多个个股日成交资讯

>>:  29. CSS 水平置中/ 垂直置中的方法

[Day5] 函式(数)介绍

1.前言 今天来讲讲函式(不是韩式料理的韩式),而是Coding时会用到的程序方法(你到底在讲啥?)...

Day 02: JavaScript 与 物件导向程序设计

物件导向程序设计是什麽? 英文原文:Object-oriented programming,简称 O...

案例:在AWS上透过SageMaker跟CodePipeline驾驭MLOps的参考架构(下)

接续上一篇关於专案参加角色与pipeline的介绍,这一篇继续谈论每一区块需要的服务以及如何依照使用...

[区块链&DAPP介绍 Day3] 什麽是智能合约

今天来聊聊我们接下来的27天会环绕的议题,就是智能合约(samrt contract)。 智能合约(...

ASP.NET MVC 从入门到放弃(Day8) -C# try catch常见异常和自定义异常 using 介绍

接着来讲讲try catch 部分.... 一般来说是要避免程序因为出现错误讯息挂掉的处理方式......