@@ -18,11 +18,6 @@ import androidx.core.view.doOnNextLayout
18
18
19
19
class FitOptionMessageView : LinearLayout {
20
20
21
- private val imageView by lazy { children.first { it is ImageView } as ImageView }
22
- private val textView by lazy { children.first { it is TextView } as TextView }
23
- private val initialXPositionOfTextView by lazy { textView.x }
24
- private val initialLeftPaddingOfTextView by lazy { textView.paddingLeft }
25
-
26
21
constructor (context: Context ) : super (context) {
27
22
initialize()
28
23
}
@@ -39,18 +34,34 @@ class FitOptionMessageView : LinearLayout {
39
34
initialize(attrs)
40
35
}
41
36
42
- constructor (
43
- context: Context ,
44
- attrs: AttributeSet ? ,
45
- defStyleAttr: Int ,
46
- defStyleRes: Int
47
- ) : super (context, attrs, defStyleAttr, defStyleRes) {
48
- initialize(attrs)
49
- }
50
-
37
+ private val imageView by lazy { children.first { it is ImageView } as ImageView }
38
+ private val textView by lazy { children.first { it is TextView } as TextView }
39
+ private val initialXPositionOfTextView by lazy { textView.x }
40
+ private val initialLeftPaddingOfTextView by lazy { textView.paddingStart }
51
41
private var cradleMargin: Float = 0f
52
42
private var playRevealAnimation: Boolean = false
53
43
private var revealAnimationDuration: Long = 0
44
+ private var revealAnimationProvider: (ImageView , TextView ) -> Unit = { imageView, _ ->
45
+ val (centerX, centerY) = imageView.x + imageView.measuredWidth / 2.0 to imageView.y + imageView.measuredWidth / 2.0
46
+ val finalRadius = measuredWidth.toFloat()
47
+ ViewAnimationUtils
48
+ .createCircularReveal(
49
+ this @FitOptionMessageView,
50
+ centerX.toInt(),
51
+ centerY.toInt(),
52
+ /* startRadius: */ 0f ,
53
+ finalRadius
54
+ )
55
+ .setDuration(revealAnimationDuration)
56
+ .start()
57
+ }
58
+ private val maskPath = Path ()
59
+ private val clipPath = Path ()
60
+ private val drawPath = Path ()
61
+ private val maskPaint = Paint (Paint .ANTI_ALIAS_FLAG ).apply {
62
+ color = ContextCompat .getColor(context, android.R .color.white)
63
+ xfermode = PorterDuffXfermode (PorterDuff .Mode .DST_OUT )
64
+ }
54
65
55
66
private fun initialize (attrs : AttributeSet ? = null) {
56
67
initializeLinearLayout()
@@ -80,87 +91,112 @@ class FitOptionMessageView : LinearLayout {
80
91
}
81
92
}
82
93
83
- var revealAnimationProvider: (ImageView , TextView ) -> Unit = { imageView, _ ->
84
- val (centerX, centerY) = imageView.x + imageView.measuredWidth / 2.0 to imageView.y + imageView.measuredWidth / 2.0
85
- val finalRadius = measuredWidth.toFloat()
86
- ViewAnimationUtils
87
- .createCircularReveal(
88
- this @FitOptionMessageView,
89
- centerX.toInt(),
90
- centerY.toInt(),
91
- /* startRadius: */ 0f ,
92
- finalRadius)
93
- .setDuration(revealAnimationDuration)
94
- .start()
95
- }
96
-
97
94
private fun startRevealAnimation () {
98
95
revealAnimationProvider.invoke(imageView, textView)
99
96
}
100
97
101
98
override fun onLayout (changed : Boolean , l : Int , t : Int , r : Int , b : Int ) {
102
99
super .onLayout(changed, l, t, r, b)
103
- val radius = imageView.measuredHeight / 2f
104
- textView.x = initialXPositionOfTextView - radius
105
- imageView.y = if (isHeightDeterminedByImage()) 0f else (textView.measuredHeight / 2f ) - radius
100
+ textView.x = getTextViewInitialX()
101
+ imageView.y = getImageViewInitialY()
106
102
imageView.elevation = textView.elevation + 0.01f
103
+ if (layoutDirection == LAYOUT_DIRECTION_RTL ) {
104
+ imageView.x = measuredWidth - 2 * getRadius()
105
+ }
107
106
}
108
107
108
+ private fun getTextViewInitialX (): Float {
109
+ return if (layoutDirection == LAYOUT_DIRECTION_LTR ) {
110
+ initialXPositionOfTextView - getRadius()
111
+ } else {
112
+ initialXPositionOfTextView
113
+ }
114
+ }
115
+
116
+ private fun getImageViewInitialY (): Float {
117
+ return if (isHeightDeterminedByImage()) {
118
+ 0f
119
+ } else {
120
+ (textView.measuredHeight / 2f ) - getRadius()
121
+ }
122
+ }
109
123
110
124
override fun onMeasure (widthMeasureSpec : Int , heightMeasureSpec : Int ) {
111
125
super .onMeasure(widthMeasureSpec, heightMeasureSpec)
112
- val radius = imageView.measuredHeight / 2f
113
126
adjustMeasureSpec(heightMeasureSpec)
114
127
textView.setPadding(
115
- initialLeftPaddingOfTextView + cradleMargin.toInt() + radius.toInt (),
128
+ getTextViewLeftPadding (),
116
129
textView.paddingTop,
117
- textView.paddingRight ,
130
+ getTextViewRightPadding() ,
118
131
textView.paddingBottom
119
132
)
120
133
}
121
134
135
+ private fun getTextViewLeftPadding (): Int {
136
+ return if (layoutDirection == LAYOUT_DIRECTION_LTR ) {
137
+ initialLeftPaddingOfTextView + cradleMargin.toInt() + getRadius().toInt()
138
+ } else {
139
+ textView.paddingLeft
140
+ }
141
+ }
142
+
143
+ private fun getTextViewRightPadding (): Int {
144
+ return if (layoutDirection == LAYOUT_DIRECTION_LTR ) {
145
+ textView.paddingRight
146
+ } else {
147
+ initialLeftPaddingOfTextView + cradleMargin.toInt() + getRadius().toInt()
148
+ }
149
+ }
150
+
151
+ override fun dispatchDraw (canvas : Canvas ) {
152
+ val saveCount = canvas.save()
153
+ super .dispatchDraw(canvas)
154
+ calculateMaskPath()
155
+ canvas.drawPath(maskPath, maskPaint)
156
+ canvas.restoreToCount(saveCount)
157
+ }
158
+
122
159
private fun adjustMeasureSpec (heightMeasureSpec : Int ) {
123
160
val lp = textView.layoutParams as LayoutParams
124
- val radius = imageView.measuredHeight / 2
125
161
126
162
val childHeightMeasureSpec = getChildMeasureSpec(
127
163
heightMeasureSpec,
128
164
paddingTop + paddingBottom + lp.topMargin + lp.bottomMargin,
129
165
lp.height
130
166
)
131
- val childWidthMeasureSpec = MeasureSpec .makeMeasureSpec(measuredWidth- radius, MeasureSpec .EXACTLY )
167
+ val childWidthMeasureSpec =
168
+ MeasureSpec .makeMeasureSpec(measuredWidth - getRadius().toInt(), MeasureSpec .EXACTLY )
132
169
textView.measure(childWidthMeasureSpec, childHeightMeasureSpec)
133
170
}
134
171
135
- private val maskPaint = Paint (Paint .ANTI_ALIAS_FLAG ).apply {
136
- color = ContextCompat .getColor(context, android.R .color.white)
137
- xfermode = PorterDuffXfermode (PorterDuff .Mode .DST_OUT )
138
- }
139
-
140
- private val maskPath = Path ()
141
- private val clipPath = Path ()
142
- private val drawPath = Path ()
143
-
144
172
private fun calculateMaskPath () {
145
173
clipPath.reset()
146
174
drawPath.reset()
147
- val (centerX, centerY) = textView.x to if (isHeightDeterminedByImage()) imageView.measuredHeight / 2f else textView.measuredHeight / 2f
148
- val radius = imageView.measuredHeight / 2f
149
- clipPath.addCircle(centerX, centerY, radius + cradleMargin, Path .Direction .CCW )
150
- drawPath.addCircle(centerX, centerY, radius, Path .Direction .CCW )
175
+ val (centerX, centerY) = getCenterX() to getCenterY()
176
+ clipPath.addCircle(centerX, centerY, getRadius() + cradleMargin, Path .Direction .CCW )
177
+ drawPath.addCircle(centerX, centerY, getRadius(), Path .Direction .CCW )
151
178
clipPath.op(drawPath, Path .Op .DIFFERENCE )
152
179
maskPath.set(clipPath)
153
180
}
154
181
155
- override fun dispatchDraw (canvas : Canvas ) {
156
- val saveCount = canvas.save()
157
- super .dispatchDraw(canvas)
158
- calculateMaskPath()
159
- canvas.drawPath(maskPath, maskPaint)
160
- canvas.restoreToCount(saveCount)
182
+ private fun getCenterX (): Float {
183
+ return if (layoutDirection == LAYOUT_DIRECTION_LTR ) {
184
+ textView.x
185
+ } else {
186
+ textView.x + textView.measuredWidth
187
+ }
188
+ }
189
+
190
+ private fun getCenterY (): Float {
191
+ return if (isHeightDeterminedByImage()) {
192
+ getRadius()
193
+ } else {
194
+ textView.measuredHeight / 2f
195
+ }
161
196
}
162
197
163
198
private fun isHeightDeterminedByImage (): Boolean =
164
199
imageView.measuredHeight > textView.measuredHeight
165
200
201
+ private fun getRadius (): Float = imageView.measuredHeight / 2f
166
202
}
0 commit comments