Как программно установить ширину / границу View относительно другого View внутри ConstraintLayout - PullRequest
1 голос
/ 23 апреля 2019

У меня есть ConstraintLayout.Для целей этого примера у нас может быть три вида внутри.

<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.

У меня уже есть значение от 0 до 100, которое представляет собой процент от расстояния между двумя текстовыми представлениями.

До сих пор я пробовал два метода, но застрял.

Попытка 1

Чтобы сделать это, я чувствую, что мне нужно фактическое расстояние между text и value и метод для правильной установки ширины полосы.

Я могу установить ширину bar, выполнив следующие действия:

val params: ConstraintLayout.LayoutParams = percentageBar.layoutParams as LayoutParams

params.matchConstraintMaxWidth = 300  // should be actual value rather than 300
percentageBar.layoutParams = params

Это точно устанавливает ширину полосы.Но мне нужен какой-то способ выяснить, каким должно быть действительное число, а не 300. Что-то вроде (процентное значение * text-value-distance / 100).

Попытка 2

Из других ответов по SO я узнал об процентном ограничении.

...

<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()

Проблема сэто то, что, когда значение процента установлено высоким, оно берет процент от родительского и проходит после начала value.

Я двигаюсь в правильном направлении?

Я надеюсьЯ достаточно ясно описал проблему.

Заранее спасибо.

// Обновление

Я выдвинул упрощенную версию с тем, что напоминаетпроблема у меня BarProblem на GitHub

Вы можете увидеть проблему на вкладке дизайна редактора макетов.

// Обновление 2

Проблема решена.GitHub repo теперь содержит код для решения.

Ответы [ 2 ]

0 голосов
/ 27 апреля 2019

Ключ был в том, что нам нужно подождать, пока не выложится ConstraintLayout. Мы можем сделать это, используя 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)
    }
}

Обратите внимание на необходимость удалить прослушиватель в последней строке, чтобы предотвратить возникновение бесконечного цикла (прослушивание макета -> установка ширины полосы -> триггеры и т.д.)

При использовании Android KTX мы можем упростить до следующего:

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 за ответ!

GitHub репо с кодом

0 голосов
/ 23 апреля 2019

Хорошее решение для вашего случая - рекомендации . Эти помощники - это якоря, которые не будут отображаться в вашем приложении, они похожи на одну линию сетки над вашим макетом и могут использоваться для добавления или ограничения ваших виджетов.

...