如何在ConstraintLayout中以编程方式相对于另一个View设置View的宽度/边距 [英] How to set width/margin of View programmatically relative another View within ConstraintLayout
问题描述
我有一个ConstraintLayout.出于本示例的目的,我们可以在其中包含三个视图.
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>
在运行时,我想将bar
的宽度设置为text
与value
之间的距离之间的某个值.
At runtime, I want to set the width of bar
to some value between the distance between text
and value
.
我已经有一个介于0到100之间的值,它是两个文本视图之间的距离的百分比.
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.
尝试1
为此,我觉得我需要text
和value
之间的实际距离以及一种正确设置条形宽度的方法.
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.
我可以通过执行以下操作来设置bar
的宽度:
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
这将设置细条的宽度.但是我需要一种方法来弄清楚实际的数字应该是300,而不是300.类似的东西(百分比值*文本值距离/100).
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).
尝试2
通过查看SO的其他答案,我发现了百分比约束.
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"/>
...
然后,在代码中,我可以执行params.matchConstraintPercentWidth = item.percentageOfMaxValue.toFloat()
Then, in the code, I can just do params.matchConstraintPercentWidth = item.percentageOfMaxValue.toFloat()
这个问题是,当百分比值设置得很高时,它将采用父级的百分比,并且超过了value
的开头.
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.
谢谢.
//更新
我推出了一个简化的版本,类似于我遇到的问题. GitHub上的BarProblem
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.
//更新2
问题已解决. GitHub存储库现在包含该解决方案的代码.
Problem solved. GitHub repo now contains the code for the solution.
推荐答案
关键是我们需要等待ConstraintLayout
布置好.我们可以使用ViewTreeObserver.OnGlobalLayoutListener
.
The key was that we need to wait until the ConstraintLayout
is laid out. We can do this by using a ViewTreeObserver.OnGlobalLayoutListener
.
示例解决方案:
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)
如果使用的是Android KTX,我们可以简化为以下内容:
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
}
}
感谢CommonsWare的答案!
Thanks to CommonsWare for the answer!
这篇关于如何在ConstraintLayout中以编程方式相对于另一个View设置View的宽度/边距的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!