问题
I have a ConstraintLayout. For the purposes of this example, we can have three views inside.
<android.support.constraint.ConstraintLayout ...>
<TextView
android:id="@+id/text"
.../>
<TextView
android:id="@+id/value"
.../>
<View
android:id="@+id/bar"
android:layout_width="0dp"
android:layout_height="8dp"
android:background="@color/bar_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@+id/text"
app:layout_constraintEnd_toStartOf="@id/value"
/>
</android.support.constraint.ConstraintLayout>
At runtime, I want to set the width of bar
to some value between the distance between text
and value
.
I already have a value between 0 and 100 which is a percentage of the distance between the two text views.
I've tried two methods so far but have gotten stuck.
Attempt 1
In order to do this, I feel I need the actual distance between text
and value
and a method to set the bar's width correctly.
I'm able to set the width of bar
by doing the following:
val params: ConstraintLayout.LayoutParams = percentageBar.layoutParams as LayoutParams
params.matchConstraintMaxWidth = 300 // should be actual value rather than 300
percentageBar.layoutParams = params
This sets the width of the bar fine. But I need some way of figuring out what the actual number should be rather than 300. Something like (percent value * text-value-distance / 100).
Attempt 2
From looking at other answers on SO, I found out about the percent constraint.
...
<View
android:id="@+id/bar"
android:layout_width="0dp"
android:layout_height="8dp"
android:background="@color/bar_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/value"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@id/text"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent="1.0"/>
...
Then, in the code, I can just do params.matchConstraintPercentWidth = item.percentageOfMaxValue.toFloat()
The problem with this is that when the percent value is set high, it's taking the percentage of the parent and goes past the start of value
.
Am I heading in the right direction?
I hope I've described the problem clearly enough.
Thanks in advance.
// Update
I've pushed a simplified version with what resembles the problem I'm having. BarProblem on GitHub
You can actually see the problem from the design tab of the layout editor.
// Update 2
Problem solved. GitHub repo now contains the code for the solution.
回答1:
I good solution for your case is guidelines. Those helpers are anchors that won’t be displayed in your app, they are like one line of a grid above your layout and can be used to attach or constraint your widgets to it.
回答2:
The key was that we need to wait until the ConstraintLayout
is laid out. We can do this by using a ViewTreeObserver.OnGlobalLayoutListener
.
Example solution:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
text.text = "Text"
value.text = "Value"
val globalLayoutListener = MainActivityGlobalListener(item_view, text, value, percentage_bar)
item_view.viewTreeObserver.addOnGlobalLayoutListener(globalLayoutListener)
}
}
class MainActivityGlobalListener(private val itemView: View,
private val text: View,
private val value: View,
private val bar: View) : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
Log.i("yoyo", "in onGlobalLayout")
val width = value.x - (text.x)
Log.i("yoyo", "Width: $width")
val params: ConstraintLayout.LayoutParams = bar.layoutParams as ConstraintLayout.LayoutParams
params.matchConstraintMaxWidth = (1.0 * width).toInt() // using 0.1 here - this value is dynamic in real life scenario
bar.layoutParams = params
// required to prevent infinite loop
itemView.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
}
Note the need to remove the listener on the last line to prevent an infinite loop from occurring (listen for layout -> set bar width -> triggers re-layout etc)
If using Android KTX, we can simplify to the following:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
text.text = "Text"
value.text = "Value"
item_view.doOnLayout {
Log.i("yoyo", "in onGlobalLayout")
val width = value.x - (text.x)
Log.i("yoyo", "Width: $width")
val params: ConstraintLayout.LayoutParams = percentage_bar.layoutParams as ConstraintLayout.LayoutParams
params.matchConstraintMaxWidth = (1.0 * width).toInt() // using 0.1 here - this value is dynamic in real life scenario
percentage_bar.layoutParams = params
}
}
Thanks to CommonsWare for the answer!
GitHub repo with code
来源:https://stackoverflow.com/questions/55801511/how-to-set-width-margin-of-view-programmatically-relative-another-view-within-co