How to set width/margin of View programmatically relative another View within ConstraintLayout

我的未来我决定 提交于 2019-12-11 06:09:32

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!