|
1 | 1 | package io.nekohasekai.sagernet.ui |
2 | 2 |
|
3 | | -import android.animation.Animator |
4 | | -import android.animation.ValueAnimator |
5 | 3 | import android.content.Context |
6 | 4 | import android.os.Bundle |
7 | 5 | import android.view.LayoutInflater |
8 | 6 | import android.view.View |
9 | 7 | import android.view.ViewGroup |
10 | | -import android.view.animation.DecelerateInterpolator |
11 | 8 | import android.widget.CheckedTextView |
12 | 9 | import android.widget.ImageView |
| 10 | +import androidx.transition.AutoTransition |
| 11 | +import androidx.transition.TransitionManager |
13 | 12 | import com.google.android.material.bottomsheet.BottomSheetDialogFragment |
14 | 13 | import io.nekohasekai.sagernet.GroupOrder |
15 | 14 | import io.nekohasekai.sagernet.R |
16 | 15 |
|
17 | 16 |
|
18 | | -private const val ANIMATION_DURATION = 300L |
| 17 | +private const val TRANSITION_DURATION = 300L |
19 | 18 |
|
20 | | -private fun View.expand() { |
21 | | - measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) |
22 | | - val targetHeight = measuredHeight |
| 19 | +private fun View.toggleWithTransition(parentView: ViewGroup, isExpanding: Boolean) { |
| 20 | + |
| 21 | + TransitionManager.beginDelayedTransition(parentView, AutoTransition().setDuration(TRANSITION_DURATION)) |
23 | 22 |
|
24 | | - layoutParams.height = 1 |
25 | | - visibility = View.VISIBLE |
26 | | - |
27 | | - val valueAnimator = ValueAnimator.ofInt(1, targetHeight) |
28 | | - valueAnimator.addUpdateListener { animator -> |
29 | | - layoutParams.height = animator.animatedValue as Int |
30 | | - requestLayout() |
31 | | - } |
32 | | - |
33 | | - valueAnimator.interpolator = DecelerateInterpolator() |
34 | | - valueAnimator.duration = ANIMATION_DURATION |
35 | | - valueAnimator.start() |
36 | | -} |
37 | | - |
38 | | -private fun View.collapse() { |
39 | | - val initialHeight = measuredHeight |
40 | | - |
41 | | - val valueAnimator = ValueAnimator.ofInt(initialHeight, 0) |
42 | | - valueAnimator.addUpdateListener { animator -> |
43 | | - layoutParams.height = animator.animatedValue as Int |
44 | | - requestLayout() |
45 | | - } |
46 | | - |
47 | | - valueAnimator.interpolator = DecelerateInterpolator() |
48 | | - valueAnimator.duration = ANIMATION_DURATION |
49 | | - valueAnimator.start() |
50 | | - |
51 | | - valueAnimator.addListener(object : Animator.AnimatorListener { |
52 | | - override fun onAnimationStart(animation: Animator) {} |
53 | | - override fun onAnimationEnd(animation: Animator) { |
54 | | - visibility = View.GONE |
55 | | - layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT // Reset height |
56 | | - } |
57 | | - override fun onAnimationCancel(animation: Animator) {} |
58 | | - override fun onAnimationRepeat(animation: Animator) {} |
59 | | - }) |
| 23 | + this.visibility = if (isExpanding) View.VISIBLE else View.GONE |
60 | 24 | } |
61 | 25 |
|
62 | 26 | private fun View.animateRotation(endDegree: Float) { |
63 | 27 | this.animate() |
64 | 28 | .rotation(endDegree) |
65 | | - .setDuration(ANIMATION_DURATION) |
| 29 | + .setDuration(TRANSITION_DURATION) |
66 | 30 | .start() |
67 | 31 | } |
68 | 32 |
|
@@ -114,13 +78,17 @@ class OtherMenuBottomSheet : BottomSheetDialogFragment() { |
114 | 78 | GroupOrder.BY_DELAY -> checkDelay?.isChecked = true |
115 | 79 | } |
116 | 80 |
|
| 81 | + val menuContainerParent = view.findViewById<ViewGroup>(R.id.menu_container_parent) ?: view as ViewGroup |
| 82 | + |
117 | 83 | setupExpandable( |
| 84 | + parent = menuContainerParent, |
118 | 85 | toggleHeader = view.findViewById(R.id.action_group_setting_1), |
119 | 86 | expandableContent = view.findViewById(R.id.expandable_setting_content_1), |
120 | 87 | arrowIcon = view.findViewById(R.id.arrow_icon_1) |
121 | 88 | ) |
122 | 89 |
|
123 | 90 | setupExpandable( |
| 91 | + parent = menuContainerParent, |
124 | 92 | toggleHeader = view.findViewById(R.id.action_group_setting_2), |
125 | 93 | expandableContent = view.findViewById(R.id.expandable_setting_content_2), |
126 | 94 | arrowIcon = view.findViewById(R.id.arrow_icon_2) |
@@ -149,28 +117,21 @@ class OtherMenuBottomSheet : BottomSheetDialogFragment() { |
149 | 117 | } |
150 | 118 | } |
151 | 119 |
|
152 | | - private fun setupExpandable(toggleHeader: View?, expandableContent: View?, arrowIcon: ImageView?) { |
| 120 | + private fun setupExpandable(parent: ViewGroup, toggleHeader: View?, expandableContent: View?, arrowIcon: ImageView?) { |
153 | 121 | if (toggleHeader != null && expandableContent != null) { |
154 | 122 |
|
155 | | - if (expandableContent.visibility == View.VISIBLE) { |
156 | | - arrowIcon?.rotation = 90f |
157 | | - expandableContent.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT |
158 | | - } else { |
159 | | - arrowIcon?.rotation = 0f |
| 123 | + if (arrowIcon != null) { |
| 124 | + arrowIcon.rotation = if (expandableContent.visibility == View.VISIBLE) 90f else 0f |
160 | 125 | } |
161 | | - |
| 126 | + |
162 | 127 | toggleHeader.setOnClickListener { |
163 | | - if (expandableContent.visibility == View.GONE) { |
164 | | - |
165 | | - expandableContent.visibility = View.VISIBLE |
166 | | - |
167 | | - expandableContent.post { |
168 | | - expandableContent.expand() |
169 | | - arrowIcon?.animateRotation(90f) |
170 | | - } |
171 | | - |
| 128 | + val isExpanding = expandableContent.visibility == View.GONE |
| 129 | + |
| 130 | + if (isExpanding) { |
| 131 | + expandableContent.toggleWithTransition(parent, true) |
| 132 | + arrowIcon?.animateRotation(90f) |
172 | 133 | } else { |
173 | | - expandableContent.collapse() |
| 134 | + expandableContent.toggleWithTransition(parent, false) |
174 | 135 | arrowIcon?.animateRotation(0f) |
175 | 136 | } |
176 | 137 | } |
|
0 commit comments