Do not clip bounds of AndroidView in Compose - android

From AndroidView documentation:
AndroidView will clip its content to the layout bounds, as being clipped is a common assumption made by Views - keeping clipping disabled might lead to unexpected drawing behavior. Note this deviates from Compose's practice of keeping clipping opt-in, disabled by default.
This seems to suggest that there is a way to turn clipping off, but I can't manage to do so.
I've tried:
Modifier.graphicsLayer(clip = false) on the AndroidView
clipToPadding = false on the View
clipToOutline = false on the View
clipChildren = false on the View
Is it possible to turn off clipping?

It's a known feature request, here's a workaround until it's implemented:
#Composable
fun <T : View> AndroidView(
clipToBounds: Boolean,
factory: (Context) -> T,
modifier: Modifier = Modifier,
update: (T) -> Unit = NoOpUpdate,
) {
androidx.compose.ui.viewinterop.AndroidView(
factory = factory,
modifier = modifier,
update = if (clipToBounds) {
update
} else {
{
(it.parent as? ViewGroup)?.clipChildren = false
update(it)
}
}
)
}

Related

Alternate use of View.INVISIBLE in jetpack compose

In xml we use View.INVISIBLE to just don't show the view at all, but it still takes up space for layout purposes. What is the alternative in jetpack compose?
AnimatedVisibility(
// true or false
) {
Button() // button code.
}
You can build a custom modifier to measure the Composable to take up space.
Something like:
fun Modifier.visible(visible: Boolean) = if (visible) this else this.then(Invisible)
private object Invisible : LayoutModifier {
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
): MeasureResult {
val placeable = measurable.measure(constraints)
return layout(placeable.width, placeable.height) {}
}
}
In Jetpack Compose, you can use the Visibility component to control the visibility of a view. The Visibility component is similar to the View.INVISIBLE property in XML, but it also supports the View.GONE behavior.
As a modifier for a view:
Button(
onClick = { /* Do something */ },
modifier = Modifier.visibility(visible = true)) {Text("Button")}
In this example, the Button view is modified with the visibility modifier. The visible parameter of the modifier determines whether the view should be visible or not.
As a container for a view:
Visibility(visible = true) {
Button(onClick = { /* Do something */ }) {
Text("Button")
}
}
In this example, the Visibility component is used as a container for the Button view. The visible parameter of the Visibility component determines whether the view should be visible or not.
The AnimatedVisibility component is similar to the Visibility component, but it also supports animating the visibility changes. You can use AnimatedVisibility in the same way as Visibility, with the added benefit of animation:
AnimatedVisibility(
visible = true, // true or false
enter = slideInVertically(),
exit = slideOutVertically()
) {
Button(onClick = { /* Do something */ }) {
Text("Button")
}
}
In this example, the Button view is wrapped in an AnimatedVisibility component. The enter and exit parameters of the AnimatedVisibility component define the animation to use when the visibility changes. In this case, the slideInVertically() and slideOutVertically() functions are used to create a slide animation.

Strange scroll in the BasicTextField with IntrinsicSize.Min

I need to implement UI control(decrease button, resizeable text input, increase button)
I was trying both ways - compose and XML version.
Compose version.
I have a strange scroll when I click on BasicTextField and pull horizontally. It just hides TextField value.
BasicTextField(
value = text,
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
keyboardActions = remember { KeyboardActions(onDone = { keyboardController?.hide() }) },
onValueChange = { onTextChanged(it) },
singleLine = true,
decorationBox = { innerTextField ->
Box(modifier = Modifier.width(IntrinsicSize.Min)) {
innerTextField()
}
}
)
Modifier.width(1.dp, Dp.Infinity), doesn't help and takes empty horizontal padding, but it doesn't have the "internal" scroll.
1.1. How to fix the "internal" scroll in the BasicTextField with applied IntrinsicSize.Min?
1.2. Also BasicTextField cuts the left-most digit when you put cursor position. It would be great to have the same behavior as we have in the XML version.
XML version.
I was trying to use the XML version with EditText instead of Compose BasicTextField but stuck with merge compose state and EditText TextWatcher listener.
2.1. To avoid twice setting the same text value to the EditText, I should compare compose state value and EditText value each time in the update {} block, is that ok? Or here can be a better solution?
AndroidView(
factory = {
_binding = L7Binding.inflate(inflater, container, false)
val view = _binding!!.root
_binding!!.editText.doOnTextChanged { text, _, _, _ ->
viewModel.onTextChanged(text.toString())
}
view
},
update = {
if (_binding!!.editText.text.toString() != inputText) {
_binding!!.editText.setText(inputText)
}
}
)
I faced the same problem.
Solved it with the help of this code, I hope it will be useful to you)
BasicTextField(,
modifier = Modifier
.disabledHorizontalPointerInputScroll()...
and also
private val HorizontalScrollConsumer = object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource) = available.copy(y = 0f)
override suspend fun onPreFling(available: Velocity) = available.copy(y = 0f)
}
fun Modifier.disabledHorizontalPointerInputScroll(disabled: Boolean = true) =
if (disabled) this.nestedScroll(HorizontalScrollConsumer) else this

Modifiers depending on other modifier's values in Jetpack Compose?

How can I create new Modifiers that depend on some other Modifiers' values? Say, for the sake of an example, I want to make the child components's height to be half of the one that is passed in from the parent plus some constant (thus not being able to use fillMaxHeight(fraction: Float)).
#Composable
fun View() {
MyComposable(modifier = Modifier.size(40.dp))
}
#Composable
fun MyComposable(
modifier: Modifier // Assuming this modifier contains a size
) {
Box(modifier) { // Setting the the size from the passed in modifier
val childHeightModifier = Modifier.height(???) // <-- Wanted to be some specific value depending on parent size
Box(childHeightModifier) {
...
}
}
}
You can use BoxWithConstraints for this particular usecase, it provides constraints imposed by parent to its children:
#Composable
fun View() {
MyComposable(modifier = Modifier.size(40.dp))
}
#Composable
fun MyComposable(modifier: Modifier) {
BoxWithConstraints(modifier) { // this: BoxWithConstraintsScope
val childHeightModifier = Modifier.height(maxHeight * 0.5f)
Box(childHeightModifier) {
...
}
}
}
As for communication between Modifiers, there's a ModifierLocal system in the works, which will allow doing exactly that.

How to dispatch touch events to parent composables in Jetpack Compose

I can't seem to find much information on touch handling in Compose.
In the specific case I'm looking at I have a list like this:
#Composable
fun MyListComposable(items: List<Item>) {
LazyColumn(
contentPadding = paddingValues(listHorizontalMargin, listVerticalMargin),
) {
// Init items emitted for brevity
}
}
This list is contained in a parent which uses the swipeable modifier, something like this.
Card(
modifier = Modifier.swipeable(
state = state,
anchors = mapOf(
0.dp.value to DrawerState.OFFSCREEN,
50.dp.value to DrawerState.PEEKING,
maxHeight.value to DrawerState.EXPANDED,
),
reverseDirection = true,
thresholds = { _, _ -> FractionalThreshold(0f) },
orientation = Orientation.Vertical
) {
MyListComposable(items)
}
My problem is the list swallows all touches, so the swipable is never invoked. So my question is, is there a way to stop lazy column swallowing touches?

Lottie + Jetpack Compose

How can I use combination of Lottie json animation and Jetpack Compose UI in Android?
I already tried AndroidView(resId = R.layout.animation) where is com.airbnb.lottie.LottieAnimationView with field lottie_rawRes but is it the best way?
Lottie compose has been released:
Dependency:
implementation "com.airbnb.android:lottie-compose:$lottie_version"
Basic usage:
val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.my_animation))
LottieAnimation(composition)
As of compose version alpha05, Lottie seems to be working fine:
#Composable
fun CustomView(modifier: Modifier = Modifier
.fillMaxWidth()) {
val visibility = remember { mutableStateOf(0) }
val context = ContextAmbient.current
val customView = remember { LottieAnimationView(context) }
// Adds view to Compose
AndroidView({ customView }) { view ->
// View's been inflated - add logic here if necessary
with(view) {
setAnimation(R.raw.choose)
playAnimation()
repeatMode = LottieDrawable.INFINITE
foregroundGravity = Gravity.CENTER
}
}
}
First class support for Lottie has not been implemented yet but it will be eventually.
I'm not really sure if it is the best way to do it, but that's the current recommmended way to include an Android View in a #Composable function.
#Composable
fun <T : View> AndroidView(
viewBlock: (Context) -> T,
modifier: Modifier = Modifier,
update: (T) -> Unit = NoOpUpdate
)
It seems that they are working on it: https://github.com/airbnb/lottie-android/commits/gpeal/compose-1

Categories

Resources