Skip to content

Commit a4a4222

Browse files
authored
Merge pull request #566 from skydoves/compose/repositional
Implement repositioning over scrollable or movable parent for Compose
2 parents 7d27797 + a326ad8 commit a4a4222

File tree

7 files changed

+85
-69
lines changed

7 files changed

+85
-69
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@
2626
android:supportsRtl="true"
2727
android:theme="@style/AppTheme"
2828
tools:ignore="GoogleAppIndexingWarning">
29-
<activity android:name=".CustomActivity" />
29+
<activity android:name=".ComposeActivity" />
3030
<activity android:name=".MainActivity" />
3131
<activity
32-
android:name=".ComposeActivity"
32+
android:name=".CustomActivity"
3333
android:exported="true"
3434
android:theme="@style/AppTheme.NoActionBar">
3535
<intent-filter>

app/src/main/kotlin/com/skydoves/balloondemo/ComposeActivity.kt

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import androidx.compose.foundation.layout.padding
2828
import androidx.compose.foundation.layout.size
2929
import androidx.compose.material.Button
3030
import androidx.compose.material.Text
31+
import androidx.compose.runtime.LaunchedEffect
3132
import androidx.compose.runtime.getValue
3233
import androidx.compose.runtime.mutableStateOf
3334
import androidx.compose.runtime.remember
@@ -87,20 +88,18 @@ class ComposeActivity : ComponentActivity() {
8788
.padding(20.dp)
8889
.align(Alignment.Center),
8990
builder = builder,
90-
onComposedAnchor = {
91-
balloonWindow1?.showAlignTop()
92-
},
93-
onBalloonWindowInitialized = {
94-
balloonWindow1 = it
95-
},
9691
balloonContent = {
9792
Text(
9893
text = "Now you can edit your profile1 profile2 profile3 profile4",
9994
textAlign = TextAlign.Center,
10095
color = Color.White,
10196
)
10297
},
103-
) {
98+
) { balloonWindow ->
99+
LaunchedEffect(key1 = Unit) {
100+
balloonWindow1 = balloonWindow
101+
}
102+
104103
Button(
105104
modifier = Modifier.size(160.dp, 60.dp),
106105
onClick = { balloonWindow1?.showAlignTop() },
@@ -114,20 +113,21 @@ class ComposeActivity : ComponentActivity() {
114113
.padding(20.dp)
115114
.align(Alignment.TopStart),
116115
builder = builder,
117-
onBalloonWindowInitialized = {
118-
balloonWindow2 = it
119-
},
120116
balloonContent = {
121117
Text(
122118
text = "Now you can edit your profile!",
123119
textAlign = TextAlign.Center,
124120
color = Color.White,
125121
)
126122
},
127-
) {
123+
) { balloonWindow ->
124+
LaunchedEffect(key1 = Unit) {
125+
balloonWindow2 = balloonWindow
126+
}
127+
128128
Button(
129129
modifier = Modifier.size(160.dp, 60.dp),
130-
onClick = { balloonWindow2?.showAlignBottom() },
130+
onClick = { balloonWindow2?.showAlignTop() },
131131
) {
132132
Text(text = "wrap balloon")
133133
}
@@ -138,9 +138,6 @@ class ComposeActivity : ComponentActivity() {
138138
.padding(20.dp)
139139
.align(Alignment.TopEnd),
140140
builder = builder,
141-
onBalloonWindowInitialized = {
142-
balloonWindow3 = it
143-
},
144141
balloonContent = {
145142
Box(modifier = Modifier.fillMaxWidth()) {
146143
Box(
@@ -164,7 +161,11 @@ class ComposeActivity : ComponentActivity() {
164161
)
165162
}
166163
},
167-
) {
164+
) { balloonWindow ->
165+
LaunchedEffect(key1 = Unit) {
166+
balloonWindow3 = balloonWindow
167+
}
168+
168169
Button(
169170
modifier = Modifier.size(160.dp, 60.dp),
170171
onClick = { balloonWindow3?.showAlignBottom() },

balloon-compose/src/main/kotlin/com/skydoves/balloon/compose/Balloon.kt

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import androidx.lifecycle.setViewTreeViewModelStoreOwner
5151
import androidx.savedstate.findViewTreeSavedStateRegistryOwner
5252
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
5353
import com.skydoves.balloon.Balloon
54+
import com.skydoves.balloon.BalloonAlign
5455
import java.lang.Integer.max
5556
import java.util.UUID
5657

@@ -156,12 +157,20 @@ public fun Balloon(
156157
}
157158

158159
Box(
159-
modifier = modifier.onSizeChanged {
160-
anchorView.updateLayoutParams {
161-
width = it.width
162-
height = it.height
160+
modifier = modifier
161+
.onGloballyPositioned {
162+
balloonComposeView.updateAlign(
163+
align = balloonComposeView.balloon.currentAlign ?: BalloonAlign.BOTTOM,
164+
xOff = 0,
165+
yOff = 0,
166+
)
163167
}
164-
},
168+
.onSizeChanged {
169+
anchorView.updateLayoutParams {
170+
width = it.width
171+
height = it.height
172+
}
173+
},
165174
) {
166175
AndroidView(
167176
modifier = Modifier.matchParentSize(),

balloon-compose/src/main/kotlin/com/skydoves/balloon/compose/BalloonComposeView.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,6 @@ internal class BalloonComposeView(
242242
balloon.updateAlign(align, anchorView, xOff, yOff)
243243
}
244244

245-
@Deprecated(
246-
"Use updateAlign instead.",
247-
replaceWith = ReplaceWith("updateAlign(BalloonAlign.Top, xOff, yOff)"),
248-
)
249245
override fun update(xOff: Int, yOff: Int): Unit = balloon.update(anchorView, xOff, yOff)
250246

251247
override fun showAlign(

balloon-compose/src/main/kotlin/com/skydoves/balloon/compose/BalloonWindow.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -450,10 +450,6 @@ public interface BalloonWindow {
450450
* @param xOff A horizontal offset from the anchor in pixels.
451451
* @param yOff A vertical offset from the anchor in pixels.
452452
*/
453-
@Deprecated(
454-
"Use updateAlign instead.",
455-
replaceWith = ReplaceWith("updateAlign(BalloonAlign.Top, xOff, yOff)"),
456-
)
457453
public fun update(xOff: Int = 0, yOff: Int = 0)
458454

459455
/** updates the size of the balloon card. */

balloon/api/balloon.api

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ public final class com/skydoves/balloon/Balloon : androidx/lifecycle/DefaultLife
9090
public final fun getBalloonArrowView ()Landroid/view/View;
9191
public final fun getBodyWindow ()Landroid/widget/PopupWindow;
9292
public final fun getContentView ()Landroid/view/ViewGroup;
93+
public final fun getCurrentAlign ()Lcom/skydoves/balloon/BalloonAlign;
9394
public final fun getMeasuredHeight ()I
9495
public final fun getMeasuredWidth ()I
9596
public final fun getOverlayWindow ()Landroid/widget/PopupWindow;
@@ -186,8 +187,6 @@ public final class com/skydoves/balloon/Balloon : androidx/lifecycle/DefaultLife
186187
public final fun showAtCenter (Landroid/view/View;II)V
187188
public final fun showAtCenter (Landroid/view/View;IILcom/skydoves/balloon/BalloonCenterAlign;)V
188189
public static synthetic fun showAtCenter$default (Lcom/skydoves/balloon/Balloon;Landroid/view/View;IILcom/skydoves/balloon/BalloonCenterAlign;ILjava/lang/Object;)V
189-
public final fun update (Landroid/view/View;)V
190-
public final fun update (Landroid/view/View;I)V
191190
public final fun update (Landroid/view/View;II)V
192191
public static synthetic fun update$default (Lcom/skydoves/balloon/Balloon;Landroid/view/View;IIILjava/lang/Object;)V
193192
public final fun updateAlign (Lcom/skydoves/balloon/BalloonAlign;Landroid/view/View;)V

balloon/src/main/kotlin/com/skydoves/balloon/Balloon.kt

Lines changed: 51 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ public class Balloon private constructor(
177177
ViewGroup.LayoutParams.MATCH_PARENT,
178178
)
179179

180+
/** Denotes the align of the currently displaying balloon. */
181+
public var currentAlign: BalloonAlign? = null
182+
private set
183+
180184
/** Denotes the popup is showing or not. */
181185
public var isShowing: Boolean = false
182186
private set
@@ -269,41 +273,48 @@ public class Balloon private constructor(
269273

270274
adjustArrowOrientationByRules(anchor)
271275

272-
when (builder.arrowOrientation.getRTLSupportOrientation(builder.isRtlLayout)) {
273-
ArrowOrientation.BOTTOM -> {
274-
rotation = 180f
275-
x = getArrowConstraintPositionX(anchor)
276-
y = binding.balloonCard.y + binding.balloonCard.height - SIZE_ARROW_BOUNDARY
277-
ViewCompat.setElevation(this, builder.arrowElevation)
278-
runOnAfterSDK23 {
279-
foreground = getArrowForeground(x, binding.balloonCard.height.toFloat())
280-
}
281-
}
276+
updateArrow(anchor)
282277

283-
ArrowOrientation.TOP -> {
284-
rotation = 0f
285-
x = getArrowConstraintPositionX(anchor)
286-
y = binding.balloonCard.y - builder.arrowSize + SIZE_ARROW_BOUNDARY
287-
runOnAfterSDK23 { foreground = getArrowForeground(x, 0f) }
288-
}
278+
visible(builder.isVisibleArrow)
279+
}
280+
}
281+
}
289282

290-
ArrowOrientation.START -> {
291-
rotation = -90f
292-
x = binding.balloonCard.x - builder.arrowSize + SIZE_ARROW_BOUNDARY
293-
y = getArrowConstraintPositionY(anchor)
294-
runOnAfterSDK23 { foreground = getArrowForeground(0f, y) }
283+
private fun updateArrow(anchor: View) {
284+
with(binding.balloonArrow) {
285+
when (builder.arrowOrientation.getRTLSupportOrientation(builder.isRtlLayout)) {
286+
ArrowOrientation.BOTTOM -> {
287+
rotation = 180f
288+
x = getArrowConstraintPositionX(anchor)
289+
y = binding.balloonCard.y + binding.balloonCard.height - SIZE_ARROW_BOUNDARY
290+
ViewCompat.setElevation(this, builder.arrowElevation)
291+
runOnAfterSDK23 {
292+
foreground = getArrowForeground(x, binding.balloonCard.height.toFloat())
295293
}
294+
}
296295

297-
ArrowOrientation.END -> {
298-
rotation = 90f
299-
x = binding.balloonCard.x + binding.balloonCard.width - SIZE_ARROW_BOUNDARY
300-
y = getArrowConstraintPositionY(anchor)
301-
runOnAfterSDK23 {
302-
foreground = getArrowForeground(binding.balloonCard.width.toFloat(), y)
303-
}
296+
ArrowOrientation.TOP -> {
297+
rotation = 0f
298+
x = getArrowConstraintPositionX(anchor)
299+
y = binding.balloonCard.y - builder.arrowSize + SIZE_ARROW_BOUNDARY
300+
runOnAfterSDK23 { foreground = getArrowForeground(x, 0f) }
301+
}
302+
303+
ArrowOrientation.START -> {
304+
rotation = -90f
305+
x = binding.balloonCard.x - builder.arrowSize + SIZE_ARROW_BOUNDARY
306+
y = getArrowConstraintPositionY(anchor)
307+
runOnAfterSDK23 { foreground = getArrowForeground(0f, y) }
308+
}
309+
310+
ArrowOrientation.END -> {
311+
rotation = 90f
312+
x = binding.balloonCard.x + binding.balloonCard.width - SIZE_ARROW_BOUNDARY
313+
y = getArrowConstraintPositionY(anchor)
314+
runOnAfterSDK23 {
315+
foreground = getArrowForeground(binding.balloonCard.width.toFloat(), y)
304316
}
305317
}
306-
visible(builder.isVisibleArrow)
307318
}
308319
}
309320
}
@@ -794,6 +805,7 @@ public class Balloon private constructor(
794805
}
795806

796807
this.isShowing = true
808+
this.currentAlign = placement.align
797809

798810
val dismissDelay = this.builder.autoDismissDuration
799811
if (dismissDelay != NO_LONG_VALUE) {
@@ -1531,7 +1543,7 @@ public class Balloon private constructor(
15311543
@MainThread
15321544
private fun update(placement: BalloonPlacement) {
15331545
if (isShowing) {
1534-
initializeArrow(placement.anchor)
1546+
updateArrow(placement.anchor)
15351547

15361548
val (xOff, yOff) = calculateOffset(placement)
15371549
this.bodyWindow.update(
@@ -1555,20 +1567,23 @@ public class Balloon private constructor(
15551567
* @param xOff A horizontal offset from the anchor in pixels.
15561568
* @param yOff A vertical offset from the anchor in pixels.
15571569
*/
1558-
@JvmOverloads
1559-
@Deprecated(
1560-
"Use updateAlign instead.",
1561-
replaceWith = ReplaceWith("updateAlign(BalloonAlign.Top, anchor, xOff, yOff)"),
1562-
)
15631570
public fun update(anchor: View, xOff: Int = 0, yOff: Int = 0) {
1564-
update(placement = BalloonPlacement(anchor = anchor, xOff = xOff, yOff = yOff))
1571+
update(
1572+
placement = BalloonPlacement(
1573+
anchor = anchor,
1574+
xOff = xOff,
1575+
yOff = yOff,
1576+
type = PlacementType.CENTER,
1577+
),
1578+
)
15651579
}
15661580

15671581
/** dismiss the popup menu. */
15681582
public fun dismiss() {
15691583
if (this.isShowing) {
15701584
val dismissWindow: () -> Unit = {
15711585
this.isShowing = false
1586+
this.currentAlign = null
15721587
this.bodyWindow.dismiss()
15731588
this.overlayWindow.dismiss()
15741589
this.handler.removeCallbacks(autoDismissRunnable)

0 commit comments

Comments
 (0)