iT邦幫忙

2021 iThome 鐵人賽

DAY 26
0
Mobile Development

花30天做個Android小專案系列 第 26

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


上一篇
Day25 - 加入簡單的動畫
下一篇
Day27 - 登出及連線中斷
系列文
花30天做個Android小專案30

尚未有邦友留言

立即登入留言