Custom layout ignores size of composeable - android

I'm currently playing around with custom Layouts in Jetpack Compose. This is what i got so far just for testing:
#Preview(widthDp = 1000, heightDp = 1000)
#Composable
fun MyLayout() {
Layout({ Box(Modifier.size(48.dp).background(Color.Blue)) }) { measurables, constraints ->
val placeables = measurables.map { it.measure(constraints) }
layout(constraints.maxWidth, constraints.maxHeight) {
placeables.forEach { it.place(0, 0) }
}
}
}
My problem here is that the Box fills the whole layout and not only it's size of 48.dp. Can someone explain to me why this is? I read here about creating custom layouts but couldn't find anything usefull.

Layout size is determined by the size passed into layout(width, height).
Your Layout doesn't have any modifiers, that's why it has maxWidth/maxHeight constraints equal to available spacing. Using layout(constraints.maxWidth, constraints.maxHeight) has the same effect as applying Modifier.fillMaxSize. Actually with Modifier.fillMaxSize your'll set both max and min constraints to the available size.
To solve this you have two options:
Apply Modifier.size to the Layout itself, instead of applying to the Box - in this case max/min constraints will be 48.dp
Calculate layout size depending on placeable. The actual code depend on what layout do you expect to get. Your code looks like Box analog, so you need maximum of all placeable sizes:
layout(placeables.maxOfOrNull { it.width } ?: 0, placeables.maxOfOrNull { it.height } ?: 0) {

The reason why the blue box fills the entire Layout is because of a combination of a few things: (1) you specify dimensions for the #Preview annotation, (2) you use the Layout's constraints unmodified to measure the box, and (3) the use of the size modifier.
Because of point 1, the constraints of the Layout will have both the minimum and maximum set to what was specified in the Preview dimensions. Because of point 2 those strict minimums and maximums are used to measure the box. And because of point 3 the incoming constraints take priority over the requested size (that's the way the size modifier works).
Mitigating each of those points will solve your problem.
As you already found out from one of the other answers, removing the width and height from the preview annotation will make those seem right. That is because without the width and height specified in the Preview annotation, the constraints will have a minimum of 0, so the child measurable are now allowed to take the size they want.
The recommended way though is to fix point 2: change the constraints to fit the requirements of your custom layout before you pass them to the measurables' measure methods. In this case, you probably want to create a copy of the constraints and set the minimum width and height to 0.
And finally you can fix point 3 by using requiredSize instead, which will size the measurable to the required size regardless, even though it will still communicate to its parents a size that's within the constraints. This is usually not recommended in production code because it makes sizing absolute, but for a preview it should be fine, and for tests it could even be desirable.

Related

Is there a way to use wrapContentHeight() with Canvas in Android Compose UI?

I need to make a Canvas wrap the content of its children as shown in the image. the label is shown based on a flag (meaning: it is not always visible). and I have another label (not shown in the picture) at the top (That indicates the value in the bar).
What I don't want to have is fixed height Canvas(modifier = modifier.fillMaxWidth().height(50.dp)) but instead Canvas(modifier = modifier.fillMaxWidth().wrapContentHeight())
Note that the height of the bar is fixed. the marker can be fixed as well. but showing/hiding the text need to make the canvas' height updated. I don't want to have a fixed value.
Any ideas?
I tried to use a boolean to give the canvas a different height. but I'am looking for something more elegant
.height(
if (showLabels) {
50.dp
} else {
30.dp
}
)

How to use percentages of screen size to create responsive layouts in Jetpack Compose

In my app, there is a button that the height of it needs to be precisely 0.2 times the screen height. For example, if the screen size is 1000 px, then its height should be 200 px. No matter the screen size, its height should always be 0.2 times that of the screen.
However, whatever I have done, I cannot get it to be work the same in different screen sizes.
These are my approaches:
1)Using DP from LocalConfiguration
// Results in very small for Pixel 2 and almost correct in Pixel 6 emulator
// So small that text in the button disappears and
// seem to took 0.1 of the screen rather than 0.2
Column(
verticalArragment = Arrangment.SpaceBetween
){
Text(titleText)
MyButton(
modifier = Modifier.height(
(LocalConfiguration.current.screenHeightDp * 0.2f).dp
)
)
}
2)Getting height from context.displayMetrics
// Results in almost correct on Pixel 2 and very large on Pixel 6 emulators
// So large that more than 0.2 times of the screen height
Column(
verticalArragment = Arrangment.SpaceBetween
){
Text(titleText)
MyButton(
modifier = Modifier.height(
(LocalContext.current.displayMetrics.heightPixels * 0.2f).dp
)
)
}
Additionally, MyButton is not different from the normal button provided by Compose.
Doing the same thing on SwiftUI with GeometryReader or Flutter with MediaQuery.of(context) results in the correct behaviour compared to Compose.
Any help is appreciated.
I have found my mistake even though I wrote it on the post. I am using the Button component supplied by Compose, which has its own padding. This is why it collapses itself along with the content (text) in it. I have changed it to Surface and implemented my custom button and everything worked fine.

android, how to set a same height for my cards without hardcode in lazy row

I have a lazy row with items that have wrap content height.
my problem is that some texts of these items is visible or invisible base of that item. so the height of that cart is not fix and it will change.
how can I find the max height of that item (with all texts) and set that height to all items.
I do not want to set a hard code height to my items (like 300.dp)
as you can see in this image: the below button change its position based on the card's height.
I want that button fix in its place and not move up and down.
https://i.stack.imgur.com/R0Tc6.png
how can I fix that problem?
Have you tried experimenting around onGloballyPositioned{..} modifier property?
val localDensity = LocalDensity.current
Text(modifier = Modifier
.onGloballyPositioned { thisText ->
with (localDensity) {
thisText.size.height.toDp()
}
},
text = "Text"
)
Edit: Intrinsic Measurement is not allowed in LazyList components, have a look at this post, looks like similar to yours.
Jetpack Compose: Row with all items same height
Also, Constraintlayout maybe a good thing to experiment on.
One of the rules of Compose is that you should only measure your children once; measuring children twice throws a runtime exception. However, there are times when you need some information about your children before measuring them.
If you have a look at the Android documentation for Intrinsic Measurements, you will have a clear idea of what to do. In your scenario, you need to force Compose to measure the size of your children to adjust the parent composable's dimensions accordingly.
This medium article gives an easier example on how to use IntrinsicSize in Compose.

How to fit text into StaticLayout with fixed width and height in Android?

I'm trying to draw text using Canvas and have found that using StaticLayout would take care of the line breaks automatically. I also want to limit its height so that when text is too long it would be ellipsized, but the size of text container is dynamic. I can easily apply the width to StaticLayout, but cant find a way to do height.
I tried to utilize TextUtils.ellipsize(), but having issue to get the spacing between lines.
PerracoLabs has the right answer but, as CheokYanCheng stated, the calculation of the maximum number of lines is off (although it may yield the correct result many if not most of the time).
A maximum height cannot be specified for a StaticLayout except indirectly by specifying the maximum number of lines. Ellipsis is tied to the maximum line count anyway, so determining the maximum number of lines for a specific height to back into a solution is the way to go. So, how do we determine the appropriate maximum line count so that a fixed-size StaticLayout with ellipsis can be created as PerracoLabs has explained.?
If the text has no spans that effect the height any of the lines of text then a simply calculation can determine the maximum number of lines that will fit into a StaticLayout before ellipsis.
The following Kotlin function will determine how many lines of text will fit into a view that has a fixed height and width. It is assumed that each line of a StaticLayout has a set height (no height-effecting spans). The top line has the same height of other lines but it is augmented by a top padding. The bottom line has a bottom padding added to it.
private fun getMaxLines(maxHeight: Int): Int {
// Build a dummy StaticLayout to get the internal measurements.
return makeStaticLayout("", width, 1).run {
val lineHeight = getLineBottom(0) - getLineTop(0) + topPadding - bottomPadding
(maxHeight - topPadding - bottomPadding) / lineHeight
}
However, if the text contains a span that changes the height of one or more lines then the only way to calculate the maximum number of lines is through the creation of the static layout that holds the entire text (no ellipsis) followed by an inspection of the lines within the layout to determine how many complete lines have fit. A new StaticLayout can then be created with the calculated maximum lines determined from the inspection.
The following Kotlin function will calculate the maximum lines by inspecting the StaticLayout for the last full line that is present.
private fun getMaxLinesByInspection(staticLayout: StaticLayout, maxHeight: Int): Int {
var line = staticLayout.lineCount - 1
while (line >= 0 && staticLayout.getLineBottom(line) >= maxHeight) {
line--
}
return line + 1
}
I have posted a small project on GitHub as a demonstration.
Here is a screen shot of the app.
You (and #Cheok Yan Cheng) might try to make use of PagedTextView. The view is intended for Paginating text in Android.
The view partially solves the problem, i.e. reacts to dynamic size changes. As to text ellipsizing, you might achieve this by customising the algorithm I've used for measuring text in height.

How do you set a percent width (or flex) for a React Native Picker on Android?

On iOS, you're able to put a <Picker> in a flex box and make the picker(s) flex the entire width of the screen.
Using the new cross-platform <Picker> element in React Native, what's the easiest way to do this on Android?
The Picker on Android only accepts the width element, and obviously doesn't accept percentage values. It does not scale to the width of its container.
You can set a percentage by getting the width of the screen using Dimensions:
import {
Dimensions
} from 'react-native'
var width = Dimensions.get('window').width;
var fifyPercent = width / 2
var oneHundredPercent = width
Or if you are measuring a component and not the entire device width you can get it's width by using the onLayout function, then use it instead.
onLayout
onLayout implementation
You can kind of set a percentage using layout_weight.
This explanation is extracted from this question.
With layout_weight you can specify a size ratio between multiple
views. E.g. you have a MapView and a table which should show some
additional information to the map. The map should use 3/4 of the
screen and table should use 1/4 of the screen. Then you will set the
layout_weight of the map to 3 and the layout_weight of the table to 1.
To get it work you also have to set the height or width (depending on
your orientation) to 0px.
So, your iOS flex box will be a LinearLayout.
These are also very good and ilustrated examples:
http://www.101apps.co.za/index.php/articles/using-android-s-layout-weight-attribute.html
http://javarticles.com/2015/04/layout_weight-android-example.html

Categories

Resources