cashapp / contour Goto Github PK
View Code? Open in Web Editor NEWLayouts with lambdas π
License: Apache License 2.0
Layouts with lambdas π
License: Apache License 2.0
In #28 we introduced automatically taking view padding into account when laying out subviews.
For existing code, this might require some migration work, and we don't want adoption of the latest and greatest release of Contour to be hindered by this.
Therefore, let's add a new field to ContourLayout
that goes like this:
@Deprecated("Migrate view to properly take padding into account or explicitly null it out")
val respectPadding: Boolean = true
When this field is set to false
, the view's padding fields should not be taken into account anywhere in the Contour stack.
There are methods in XFloat/YFloat/XInt/YInt which do not have tests. Let's add some.
When a child View's visibility is set to GONE
, traditional layouts skip laying the child and measure it as 0 width & height. One can argue that this is kind of an implicit behavior, but given that other layouts have always carried this behaved, should we consider matching this expected behavior?
It could be answered by #31
But let me single out this question here:
Consider:
private val name: TextView =
TextView(context).apply {
text = "Ben Sisko"
setTextColor(White)
setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18f)
applyLayout(
x = leftTo { parent.left() + 15.dip },
y = topTo { parent.top() + 15.dip }
)
}
How to update that name TextView to "John Doe"?
In SampleActivity, there's views[i]()
which calls invoke()
.
Using breakpoints, it seems invoke()
reinstantiate everything in SampleView1 and SampleView2
Is that correct?
Or is invoke()
kinda redraw the view with a new state or sth like that?
Couldn't find invoke()
function defined inside ContourLayout that's why I couldn't help myself.
Are there use cases for parameters to be passed to the ContourLayout.invoke()
function?
This would otherwise crash when casting the LayoutSpec
private inline fun View.spec(): LayoutSpec {
if (parent !== this@ContourLayout) {
throw IllegalArgumentException("Referencing view outside of ViewGroup.")
}
return layoutParams as LayoutSpec
}
After running the Sample app without any modifications, and clicking the first card; I've noticed that the text is cut by some margin (I think) during the transition.
See the following video: https://imgur.com/amsY2I8
The avatar is acting normally. It is "cut" by the card only at the very bottom.
I'm not sure what's causing this bug as I'm not very familiar with Contour yet, but I believe it could be because of the centerY()
part, on this line:
Hi, may you update readme about setup contour dependency from scratch please
I have a header and chip group that can be visible, or gone. What is the best way to handle this and not have views overlap? It seems that if the view that visible relies on the view that gone it doesn't always evaluate correct.
Here is the sample in the init of the conditional fields. if for example there is no demographic then i hide the text and chips. This seems to work in some scenarios. One that does does not work is if contentType, format, theme are gone. Then the genre text overlaps in the demographic chip.
demographicText.layoutBy(
x = leftTo { parent.left() },
y = topTo { descriptionExpanded.bottom() }
)
demographicChips.layoutBy(
x = matchParentX(),
y = topTo { demographicText.bottom() - 8.ydip }
)
contentTypeText.layoutBy(
x = leftTo { parent.left() },
y = topTo { demographicChips.bottom() }
)
contentTypeChips.layoutBy(
x = matchParentX(),
y = topTo { contentTypeText.bottom() - 8.ydip }
)
formatText.layoutBy(
x = leftTo { parent.left() },
y = topTo { contentTypeChips.bottom() }
)
formatChips.layoutBy(
x = matchParentX(),
y = topTo { formatText.bottom() - 8.ydip }
)
themeText.layoutBy(
x = leftTo { parent.left() },
y = topTo { formatChips.bottom() }
)
themeChips.layoutBy(
x = matchParentX(),
y = topTo { themeText.bottom() - 8.ydip }
)
genreText.layoutBy(
x = leftTo { parent.left() },
y = topTo { themeChips.bottom() }
)
genreChips.layoutBy(
x = matchParentX(),
y = topTo { genreText.bottom() - 8.ydip }
)
altTitlesText.layoutBy(
x = leftTo { parent.left() },
y = topTo { genreChips.bottom() }
)
altTitles.layoutBy(
x = matchParentX(),
y = topTo { altTitlesText.bottom() - 12.ydip }
)
We've discussed creating a Recipes section in the README for some patterns of use of Contour APIs that aren't necessarily the most straightforward.
One such example is, in the words of @aerb ,
if you find yourself wanting to use measuredWidth / measuredHeight there is an extension you can use called preferredWidth / preferredHeight on Contour which will trigger a measure() without requiring a full layout. That should help you avoid scenarios where you are asking for measuredWidth before measure has occurred
Scenario:
ContourLayout
inside of a ScrollView
.
Wanted API (name TBD):
init {
contourMaxHeightOf { lastView.bottom() }
}
Behavior:
Sets height to max(availableSpace, lambda)
I tried this using contourHeightOf
and couldn't get it to work:
contourHeightOf { maxOf((parent as ViewGroup).height.toYInt(), lastView.bottom()) }
I want to know the performance compare between contour
vs constraintLayout
. I have seen in some places that if a simple layout uses constraintLayout
, it will not be faster than using frameLayout
or linearLayout
, so contour
?
The layout api must specify the solver for the x and y axes. If I want the x axis to have similar behaviors like wrap_content, what should I do?
I only saw matchParentX/Y, why not provide wrapContentX/Y?
In native, if you don't specify width or height, LayoutParams.WRAP_CONTENT will be used by default. Can contour do the same thing?
When laying out a view, the parent ContourLayout
's padding values are not respected. Aligning a subview to parent.top()
will place the view at the top of the parent, even if the parent has a non-zero top padding. Similarly, constraining a view to parent.width()
or parent.height()
will constrain the view to the parent's total width and height, ignoring the padding.
CHANGELOG.md Suggests that 0.1.8
version was released on 2020-06-11.
But I could not find it in maven.
Also README.md points to 0.1.7
version. And that makes me wonder 0.1.8 version is released for public use?
This is something accomplished using Barrier
in ConstraintLayout
, but I think we can make it more straightforward.
Given views A, B, C:
When the layout of B or C is updated, the positioning of A must be updated.
The idea here would be to print to logcat whenever the ContourLayout or one of its direct subviews has its position or size recalculated, i.e. whenever one of the lambdas defined in the context of that layout gets run.
It would be useful for debugging view layouts (especially for more complex layouts or layouts whose size changes frequently.
This can be easily reproduced by changing the dependency on the sample app from the module to the binary:
implementation project(':contour')
-> implementation 'app.cash.contour:contour:1.0.0'
Here's the stack trace:
java.lang.NoSuchMethodError: No virtual method getYdip-dBGyhoQ(I)I in class
Lcom/squareup/contour/sample/SampleView; or its super classes (declaration of
'com.squareup.contour.sample.SampleView' appears in /data/app/~~0Ba-
93BI78uARYazH4agJA==/com.squareup.contour.sample-VKrVvhwusycb2ElCbF70RQ==/base.apk!classes2.dex)
at com.squareup.contour.sample.SampleView$3.invoke(SampleView.kt:56)
Int.dip
doesn't seem to be an issue.
At least on my project, reverting to 1.4.21 fixes the issue immediately.
I've only now watched Adam's presentation about Contour and while I was using it for some time now, this slide blew my mind
I've been occasionally struggling with the fact that sometimes I want one solver and sometimes (depending on view config) another. As a result I had to invent some clever tricks, while I didn't know about this feature.
Can it be somehow made more obvious? Perhaps a mention in README or in some other documentation source?
Given that XML isn't involved, the layout preview in studio doesn't apply here - what's the recommended way of previewing contour layouts (if any) besides building/deploying?
Just curious if the cash app team has the same problem, has found another solution, or if its not really an issue re-deploying after making changes.
Thanks!
Since ContourLayout
extends ViewGroup
, it doesn't support foreground
on API below 23, a common workaround is to wrap the layout in a FrameLayout
, but this seems redundant
It is not the first time I have this usecase, where I want more than two arguments for minOf
.
minOf
currently supports XInt/YInt
and XAxisSolver/YAxisSolver
(but only 2 args for both kinds)...
I have just ran into another case with the latter. The layout is like this:
TITLE
PRIMARY_ACTION (optional, maybe GONE)
SECONDARY_ACTION (optional, maybe GONE)
---screen-bottom---
I want to do something like this:
titleView.layoutBy(
x = matchParentX(),
y = minOf(
bottomTo { primaryAction.top() - 16.ydip },
bottomTo { secondaryAction.top() - 16.ydip },
bottomTo { parent.bottom() - 16.ydip },
)
)
but minOf
has only 2 args... Similar situation is for xint/yint, I often find myself wishing for more args.
Is this possible or maybe I also miss some easy workaround to achive what I want? I know I can put if (someView.isVisible) rule1 else rule2
in my lambdas, but that looks not very elegant and a bit messy...
P.S. Can't help but think that if we'd be in the functional programming land, XInt/Yint, and axis solvers could be an instance of Monoid + Ord and all the stdlib functions (including min
) would just work on them. But oh well :)
In the code, the example shows the name being constrained to the top:
y = topTo { parent.top() + 15.dip }
With the avatar being constrained below it:
y = topTo { name.bottom() }
But the gif shows it constrained to the bottom with the avatar above it.
I'm trying to achieve an effect similar to #116, but with the maxOf
function. Unfortunately for me, I'm a little bit confused by Contour's behavior when I change the visibility of one of the optional views.
We can easily reproduce the behavior that's confusing me using the sample app.
private val card3 = ExpandableBioCard1(context)
to SampleView
:SampleView
's init
blockcard3.layoutBy(
x = matchParentX(marginLeft = 24.dip, marginRight = 24.dip),
y = maxOf(topTo { card2.bottom() + 24.ydip }, topTo { card1.bottom() + 24.ydip })
)
isVisible = false
to ExpandableBioCard2
's init
block (in my real app this can happen in the render method, but it has the same effect here)card3
and card1
I expected card2
's visibility change to GONE
to result in card2.bottom()
evaluating to 0
, causing maxOf
to pick the second argument.
I expected this for two reasons:
View.GONE
state that the "view is invisible, and it doesn't take any space for layout purposes"LinearLayout
that contains two views, if you set the visibility of the first view to GONE
the second view automatically takes its placeOf course, this is not what's happening. maxOf
is always picking topTo { card2.bottom() + 24.ydip }
because while card2.height()=0
, card2.bottom()
is still > card1.bottom()
.
I can get the desired behavior by doing this:
card3.layoutBy(
x = matchParentX(marginLeft = 24.dip, marginRight = 24.dip),
y = topTo {
when {
card2.isVisible -> card2.bottom() + 24.ydip
else -> card1.bottom() + 24.ydip
}
}
)
card2.bottom()
not evaluate to 0?topTo
the recommended approach for achieving this effect?Thank you all for this amazing library! I won't ever write another layout in XML if I can help it π .
java.lang.NoSuchMethodError: No virtual method getYdip-dBGyhoQ(I)I in class
Tried with contour 1.1.0-SNAPSHOT as well, same error.
When I downgrade kotlin to 1.4.20 everything works.
I have a header and chip group that can be visible, or gone. What is the best way to handle this and not have views overlap? It seems that if the view that visible relies on the view that gone it doesn't always evaluate correct.
Here is the sample in the init of the conditional fields. if for example there is no demographic then i hide the text and chips. This seems to work in some scenarios. One that does does not work is if contentType, format, theme are gone. Then the genre text overlaps in the demographic chip.
demographicText.layoutBy(
x = leftTo { parent.left() },
y = topTo { descriptionExpanded.bottom() }
)
demographicChips.layoutBy(
x = matchParentX(),
y = topTo { demographicText.bottom() - 8.ydip }
)
contentTypeText.layoutBy(
x = leftTo { parent.left() },
y = topTo { demographicChips.bottom() }
)
contentTypeChips.layoutBy(
x = matchParentX(),
y = topTo { contentTypeText.bottom() - 8.ydip }
)
formatText.layoutBy(
x = leftTo { parent.left() },
y = topTo { contentTypeChips.bottom() }
)
formatChips.layoutBy(
x = matchParentX(),
y = topTo { formatText.bottom() - 8.ydip }
)
themeText.layoutBy(
x = leftTo { parent.left() },
y = topTo { formatChips.bottom() }
)
themeChips.layoutBy(
x = matchParentX(),
y = topTo { themeText.bottom() - 8.ydip }
)
genreText.layoutBy(
x = leftTo { parent.left() },
y = topTo { themeChips.bottom() }
)
genreChips.layoutBy(
x = matchParentX(),
y = topTo { genreText.bottom() - 8.ydip }
)
altTitlesText.layoutBy(
x = leftTo { parent.left() },
y = topTo { genreChips.bottom() }
)
altTitles.layoutBy(
x = matchParentX(),
y = topTo { altTitlesText.bottom() - 12.ydip }
)
Originally posted by @CarlosEsco in #125
The following code causes a stack overflow during layout:
class ParentView(context: Context): ContourLayout(context) {
init {
setBackgroundColor(Color.RED)
}
val childView = ChildView(context).apply {
applyLayout(
x = leftTo { parent.left() }.widthOf { 100.xdip },
y = topTo { parent.top() }.heightOf { 100.ydip }
)
}
}
class ChildView(context: Context) : ContourLayout(context) {
init {
setBackgroundColor(Color.BLUE)
}
}
However, moving ChildView
's applyLayout
into it's onInitializeLayout
solves the issue:
class ParentView(context: Context): ContourLayout(context) {
init {
setBackgroundColor(Color.RED)
}
val childView = ChildView(context).also { addView(it) }
}
class ChildView(context: Context) : ContourLayout(context) {
init {
setBackgroundColor(Color.BLUE)
}
override fun onInitializeLayout() {
super.onInitializeLayout()
applyLayout(
x = leftTo { parent.left() }.widthOf { 100.xdip },
y = topTo { parent.top() }.heightOf { 100.ydip }
)
}
}
I don't know if this is a bug or not, but my hunch is it's the intended behavior (if not, I'd be happy to look into it).
I realize nesting layouts is discouraged, but there are going to be situations where it is useful and people are going to do it anyway.
The pattern Contour uses for configuring child views (MyView(context).apply { applyLayout() }
) is great, and it seems clunky to have to violate it iff the child happens to be another ContourLayout (I can imagine during refactoring a View that didn't start as Contour might become one later).
Could we make it so layout constraints declared at construction time get deferred until onInitializeLayout
? Either by having applyLayout
behave differently for ContourLayouts or with an onInitializeLayout
DSL:
class ParentView(context: Context): ContourLayout(context) {
init {
setBackgroundColor(Color.RED)
}
val childView = ChildView(context).apply {
onInitializeLayout {
applyLayout(
x = leftTo { parent.left() }.widthOf { 100.xdip },
y = topTo { parent.top() }.heightOf { 100.ydip }
)
}
}
}
class ChildView(context: Context) : ContourLayout(context) {
init {
setBackgroundColor(Color.BLUE)
}
}
Yo guys I like what I see here and want to start using this library but can't find it on either
https://bintray.com/bintray/jcenter or search.maven.org.
example:
topTo { otherThing.top() * 0.4f }
Hi, there
Can I ask a question? just noticed that 1.0.0 snapshot was released, so I was wondering if we can use it, any bugs were fixed or behavior changes in this snapshot?
By the way, is the new release coming soon?
thanks
I'm using ContourLayout
to build a view that will be placed inside a ScrollView
(with fillViewport="true"
). When scrolling is not required, I'd like for the contents to be anchored to the bottom of the ScrollView
.
Here is how I'm going about it (for simplicity, let's just say there are 2 children views):
class MyContourView(
context: Context,
private val view1: View,
private val view2: View,
private val interItemMarginPx: Int,
private val lrMarginPx: Int
) : ContourLayout(context) {
init {
contourHeightOf { availableHeight ->
maxOf(availableHeight, view2.bottom() + interItemMarginPx
}
}
override fun onInitializeLayout() {
view2.applyLayout(
leftTo { parent.left() + lrMarginPx }.rightTo { parent.right() - lrMarginPx },
bottomTo { parent.bottom() - interItemMarginPx }
)
view1.applyLayout(
leftTo { parent.left() + lrMarginPx }.rightTo { parent.right() - lrMarginPx },
bottomTo { view2.top() - interItemMarginPx }
)
}
}
But I get a CircularReferenceDetected error (at the line where I call view2.bottom()
in contourHeightOf {}
). I can see why that's the case: view2
references parent.bottom()
and the parent references view2.bottom()
in determining its height.
Everything works if I remove the contourHeightOf {}
block -- in which case it's always going to be availableHeight
. But this breaks the scrolling if the height of the contents is greater than availableHeight
.
How do I go about this use case?
I needed a textView and a button, one below the other, both right aligned in the parent (while still respecting the left constraint of the parent's edge).
For the textView, I did the following and it worked:
textView.applyLayout(
rightTo { parent.right() - 32.dip }.leftTo { parent.left() + 32.dip },
// some YAxisSolver
)
The textView was wrap_content and right aligned, and if the text grew to multiple lines, the left constraint was still respected. However, doing the same for the button resulted in an always-match_parent button, even if the contents inside were much narrower. To achieve what I wanted, I had to use SizeMode.AtMost
like so:
button.applyLayout(
rightTo { parent.right() - 32.dip }.leftTo(SizeMode.AtMost) { parent.left() + 32.dip },
topTo { textView.bottom() + 16.dip }
)
Why is this the case? Shouldn't the default width strategy be the same for all view types?
kotlin 1.4.0
works fine on 1.3.72
stack trace:
java.lang.NoSuchMethodError: No virtual method bottom-h0YXg9w(Landroid/view/View;)I in class Lcom/priceline/android/contour/CollectionsView; or its super classes (declaration of 'com.priceline.android.contour.CollectionsView' appears in /data/app/com.priceline.android.contour-jK1itqBV6DI3fBgme768dQ==/base.apk!classes2.dex)
at com.priceline.android.contour.CollectionsView$4.invoke(CollectionsView.kt:16)
at com.priceline.android.contour.CollectionsView$4.invoke(CollectionsView.kt:7)
at com.squareup.contour.utils.XYIntUtilsKt$unwrapYIntToYIntLambda$1.invoke(XYIntUtils.kt:60)
at com.squareup.contour.utils.XYIntUtilsKt$unwrapYIntToYIntLambda$1.invoke(Unknown Source:6)
at com.squareup.contour.constraints.SizeConfig.resolve(SizeConfig.kt:27)
at com.squareup.contour.ContourLayout.onMeasure(ContourLayout.kt:194)
at android.view.View.measure(View.java:25086)
at androidx.constraintlayout.widget.ConstraintLayout$Measurer.measure(ConstraintLayout.java:763)
at androidx.constraintlayout.solver.widgets.analyzer.BasicMeasure.measure(BasicMeasure.java:426)
at androidx.constraintlayout.solver.widgets.analyzer.BasicMeasure.measureChildren(BasicMeasure.java:105)
at androidx.constraintlayout.solver.widgets.analyzer.BasicMeasure.solverMeasure(BasicMeasure.java:247)
at androidx.constraintlayout.solver.widgets.ConstraintWidgetContainer.measure(ConstraintWidgetContainer.java:117)
at androidx.constraintlayout.widget.ConstraintLayout.resolveSystem(ConstraintLayout.java:1532)
at androidx.constraintlayout.widget.ConstraintLayout.onMeasure(ConstraintLayout.java:1607)
at android.view.View.measure(View.java:25086)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6872)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:146)
at android.view.View.measure(View.java:25086)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6872)
at androidx.appcompat.widget.ActionBarOverlayLayout.onMeasure(ActionBarOverlayLayout.java:490)
at android.view.View.measure(View.java:25086)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6872)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:25086)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6872)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
at android.view.View.measure(View.java:25086)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6872)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at com.android.internal.policy.DecorView.onMeasure(DecorView.java:742)
at android.view.View.measure(View.java:25086)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:3083)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1857)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2146)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1745)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7768)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:967)
at android.view.Choreographer.doCallbacks(Choreographer.java:791)
at android.view.Choreographer.doFrame(Choreographer.java:726)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:952)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
We recently started using contour and are really liking the no-XML approach. However, I'm a little confused as to the best way of saving state.
Say I'm using ContourLayout for my fragment like so:
class MyFragmentView(context: Context) : ContourLayout(context) {
val editText1
val editText2
val editText3
override fun onInitializeLayout() {
// applyLayout calls
}
}
and then in my Fragment,
class MyFragment : Fragment() {
private lateinit var contourView: MyFragmentView
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = MyFragmentView(requireContext()).apply {
contourView = this
}
// other fragment functions
}
Now if the fragment view hierarchy is re-created (say via the Developer Options setting "Don't keep activities"), naturally, user input in the editTexts is not restored.
To get around this, we can do:
class MyFragment : Fragment() {
// other fragment functions
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("input1", contourView.editText1.getText())
outState.putString("input2", contourView.editText2.getText())
outState.putString("input3", contourView.editText3.getText())
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
savedInstanceState?.let {
contourView.editText1.setText(it.getString("input1"))
contourView.editText2.setText(it.getString("input1"))
contourView.editText3.setText(it.getString("input1"))
}
}
}
But I have a gut feeling this may not be the best way. Is there some way view state restoration can happen internally inside ContourLayout given the way we're using it in MyFragment
above?
Is it possibile to add a ContourLayout to an existing layout? When i try to add it to a FrameLayout i obtain ths exception:
java.lang.ClassCastException: com.squareup.contour.ContourLayout$LayoutSpec cannot be cast to android.view.ViewGroup$MarginLayoutParams
Hi folks! π
I just wanted to ask if it's in the plans to add support for right to left layouts in the library. I guess that library users are mostly interested in supporting LTR locales, but I was wondering what are your thoughts/plans on this.
Hi! Using 2 nested contour layout, is it possible to have something like wrap_content?
Some of the points that are great with contour which are stated in the readme are:
ViewHolders arenβt needed
Add & remove components based on app state
Lazy loading elements of the UI.
These points together sound like this layout could replace recycler views.
In the application I'm currently working on about 95% of the screens are scrollable lists, so basically every screen is made of a single RecyclerView.
How is this library designed to be used? Would you use one contourlayout inside a each ViewHolder
?
It would be pretty cool to use information about the other Axis to build constraints.
E.g. Imagine building a square like:
private val square = View(context).applyLayout(
x = leftTo { parent.left() }.rightTo { parent.right() },
y = topTo { parent.bottom() }.heightOf { width().toY() }
)
This would currently fail, because of "circular reference".
Just wanted to understand if that's the case. And whether you are planning to improve and promote this library more for Android UI development.
.FooterView$4.invoke(FooterView.kt:47)
at ....FooterView$4.invoke(FooterView.kt:13)
at com.squareup.contour.utils.XYIntUtilsKt$unwrapYIntLambda$1.invoke(XYIntUtils.kt:51)
at com.squareup.contour.utils.XYIntUtilsKt$unwrapYIntLambda$1.invoke(Unknown Source:2)
at com.squareup.contour.constraints.Constraint.resolve(Constraint.kt:48)
at com.squareup.contour.solvers.SimpleAxisSolver.measureSpec(SimpleAxisSolver.kt:173)
at com.squareup.contour.ContourLayout$LayoutSpec.measureSelf$contour_release(ContourLayout.kt:758)
at com.squareup.contour.solvers.SimpleAxisSolver.max(SimpleAxisSolver.kt:110)
at com.squareup.contour.ContourLayout$LayoutSpec.bottom-h0YXg9w$contour_release(ContourLayout.kt:729)
at com.squareup.contour.ContourLayout.bottom-h0YXg9w(ContourLayout.kt:469)
at ....FooterView$6.invoke(FooterView.kt:52)
at ....FooterView$6.invoke(FooterView.kt:13)
at com.squareup.contour.utils.XYIntUtilsKt$unwrapYIntLambda$1.invoke(XYIntUtils.kt:51)
at com.squareup.contour.utils.XYIntUtilsKt$unwrapYIntLambda$1.invoke(Unknown Source:2)
at com.squareup.contour.constraints.Constraint.resolve(Constraint.kt:48)
at com.squareup.contour.solvers.SimpleAxisSolver.resolveAxis(SimpleAxisSolver.kt:131)
at com.squareup.contour.solvers.SimpleAxisSolver.max(SimpleAxisSolver.kt:111)
at com.squareup.contour.ContourLayout$LayoutSpec.bottom-h0YXg9w$contour_release(ContourLayout.kt:729)
at com.squareup.contour.ContourLayout.bottom-h0YXg9w(ContourLayout.kt:469)
at ....FooterView$11.invoke(FooterView.kt:62)
at ....FooterView$11.invoke(FooterView.kt:13)
at com.squareup.contour.utils.XYIntUtilsKt$unwrapYIntLambda$1.invoke(XYIntUtils.kt:51)
at com.squareup.contour.utils.XYIntUtilsKt$unwrapYIntLambda$1.invoke(Unknown Source:2)
at com.squareup.contour.constraints.Constraint.resolve(Constraint.kt:48)
at com.squareup.contour.solvers.SimpleAxisSolver.resolveAxis(SimpleAxisSolver.kt:131)
at com.squareup.contour.solvers.SimpleAxisSolver.max(SimpleAxisSolver.kt:111)
at com.squareup.contour.ContourLayout$LayoutSpec.bottom-h0YXg9w$contour_release(ContourLayout.kt:729)
at com.squareup.contour.ContourLayout.bottom-h0YXg9w(ContourLayout.kt:469)
at ....FooterView$16.invoke(FooterView.kt:75)
at ....FooterView$16.invoke(FooterView.kt:13)
at com.squareup.contour.utils.XYIntUtilsKt$unwrapYIntToYIntLambda$1.invoke(XYIntUtils.kt:61)
at com.squareup.contour.utils.XYIntUtilsKt$unwrapYIntToYIntLambda$1.invoke(Unknown Source:6)
at com.squareup.contour.constraints.SizeConfig.resolve(SizeConfig.kt:29)
at com.squareup.contour.ContourLayout.onMeasure(ContourLayout.kt:213)
at android.view.View.measure(View.java:25466)
Is there an easier way to achieve a MATCH_PARENT
parameter for widths and heights?
So far I've done it this way:
applyLayout(
x = leftTo { parent.left() }
.rightTo { parent.right() },
y = topTo { parent.top() }
)
But it is not straightforward and it looks like a workaround.
There are certain scenarios (like the one described in #59) in which it is handy to do something like this:
if (view1.bottom() < view2.bottom()) view1.bottom() else otherView.top()
Currently XInt
/YInt
lack operator fun compareTo
which prevents the above from compiling.
The workaround could be to use minOf(view1.bottom() - view2.bottom(), 0.ydip) == 0.ydip
but that's a bit ugly.
Can those be added (I can forge a PR) or do you have something specific in mind why this is not a good idea?
Since Contour is using experimental features (inline classes), it should be either propagating the warnings through annotations or Gradle flags (see Experimental API markers), or introducing its own experimental annotation (see an example of this in KotlinPoet). An argument in favor of a library-specific annotation is that developers might be fine with using experimental features in their own code, but reluctant to use 3rd party libraries that use them, hence the library should require explicit opt-in.
I would be very grateful if you can give a best practices in the demo about how to use recyclerView in this layout
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.