Два представления прокрутки, расположенные одно под другим, должны занимать половину пространства, но сжиматься в пользу другого, если они не заполнены полностью

У меня есть два вертикальных элемента ScrollView в вертикальном LinearLayout. Если я установлю для обоих Layout_weight значение 1, они оба будут использовать половину полной высоты. Однако это также имеет место, если одному из них требуется, например, только четверть высоты из-за его содержания. В таком случае я хотел бы сжать первый ScrollView и использовать лишнее пространство для второго ScrollView (и наоборот).

Есть идеи, как я могу этого добиться? Окружающий макет не обязательно должен быть LinearLayout. (Я видел это, но оно не отвечает на мой вопрос)

1
0
68
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Хорошо, поскольку, похоже, нет возможности напрямую использовать LinearLayout, я создал собственный макет. Он должен иметь ровно два дочерних элемента ScrollView, но его можно легко адаптировать, если внутри нужны дополнительные представления.

/**
 * A vertical LinearLayout containing 2 ScrollViews.
 * The height of each ScrollView is growing with its content, as long as both
 * ScrollViews fit into the parent.
 * If one ScrollView needs to scroll its content, and the other is still smaller than half
 * of the parent, the smaller one takes only as much space as needed.
 * Only if both views need more than half of the parent space,
 * each of them receives exactly half of it.
 */
class DoubleScrollView : LinearLayout {

    private lateinit var scrollView1: ScrollView
    private lateinit var scrollView2: ScrollView

    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)

    constructor(context: Context, attrs: AttributeSet, defStyle: Int) :
            super(context, attrs, defStyle)
    
    init {
        orientation = VERTICAL
    }

    override fun onFinishInflate() {
        super.onFinishInflate()
        //In this simple example, we assume to have exactly 2 ScrollViews as children
        scrollView1 = getChildAt(0) as ScrollView
        scrollView2 = getChildAt(1) as ScrollView
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        //Allow the scroll views to set the full content height at first
        scrollView1.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, 0f)
        scrollView2.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, 0f)
        val widthSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY)
        val heightSpec = MeasureSpec.makeMeasureSpec(height, AT_MOST)
        scrollView1.measure(widthSpec, heightSpec)
        scrollView2.measure(widthSpec, heightSpec)
        // Based on the height the full content would need, adapt the LayoutParams
        // of the ScrollViews.
        val height = View.MeasureSpec.getSize(heightMeasureSpec)
        val height1 = scrollView1.measuredHeight
        val height2 = scrollView2.measuredHeight
        if (height1 + height2 >= height) {
            if (height1 < height / 2) {
                //First wrap content, second fills the rest
                scrollView2.setLayout(0, 1f)
            } else if (height2 < height / 2) {
                //Second wrap content, first fills the rest
                scrollView1.setLayout(0, 1f)
            } else {
                //First and second both with same weight
                scrollView1.setLayout(0, 1f)
                scrollView2.setLayout(0, 1f)
            }
        } //else: both views can just wrap content
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }

    private fun View.setLayout(height: Int, weight: Float) {
        this.layoutParams.height = height
        (this.layoutParams as LinearLayout.LayoutParams).weight = weight
    }
}

Для тестирования и демонстрации я создал следующее минимальное приложение.

Активность:

class MainActivity : AppCompatActivity() {

    private val mainViewModel: MainViewModel by viewModels()
    private lateinit var vb: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        vb = ActivityMainBinding.inflate(layoutInflater)
        setContentView(vb.root)
        mainViewModel.text1.observe(this) { text -> updateText(vb.text1, text) }
        mainViewModel.text2.observe(this) { text -> updateText(vb.text2, text) }
        listOf(vb.plus1, vb.minus1, vb.plus2, vb.minus2).forEachIndexed() { i, button ->
            button.setOnClickListener { mainViewModel.changeText(i <= 1, i % 2 == 0) }
        }
    }

    private fun updateText(textView: TextView, text: String?) {
        textView.text = text
    }

}

Модель просмотра:

class MainViewModel : ViewModel() {
    val text1: LiveData<String> = MutableLiveData("")
    val text2: LiveData<String> = MutableLiveData("")

    fun changeText(first: Boolean, plus: Boolean) {
        val liveData = if (first) text1 else text2
        val text = liveData.value!!
        val size = text.count { it == '#' } + if (plus) 1 else -1
        val lines = Array(size.coerceAtLeast(0)) { "This is line #$it" }
        (liveData as MutableLiveData).value = lines.joinToString("\n")
    }
}

Макет:

<?xml version = "1.0" encoding = "utf-8"?>
<yourPackage.DoubleScrollView xmlns:android = "http://schemas.android.com/apk/res/android"
    android:id = "@+id/container"
    android:layout_width = "match_parent"
    android:layout_height = "match_parent"
    android:orientation = "vertical">

    <ScrollView
        android:id = "@+id/scrollView1"
        android:layout_width = "match_parent"
        android:layout_height = "wrap_content">

        <LinearLayout
            android:layout_width = "match_parent"
            android:layout_height = "wrap_content"
            android:orientation = "vertical">

            <Button
                android:id = "@+id/plus1"
                android:layout_width = "wrap_content"
                android:layout_height = "wrap_content"
                android:text = "+"></Button>

            <Button
                android:id = "@+id/minus1"
                android:layout_width = "wrap_content"
                android:layout_height = "wrap_content"
                android:text = "-"></Button>

            <TextView
                android:id = "@+id/text1"
                android:layout_width = "wrap_content"
                android:layout_height = "wrap_content" />
        </LinearLayout>
    </ScrollView>

    <ScrollView
        android:id = "@+id/scrollView2"
        android:layout_width = "match_parent"
        android:layout_height = "wrap_content">

        <LinearLayout
            android:layout_width = "match_parent"
            android:layout_height = "wrap_content"
            android:orientation = "vertical">

            <Button
                android:id = "@+id/plus2"
                android:layout_width = "wrap_content"
                android:layout_height = "wrap_content"
                android:text = "+"></Button>

            <Button
                android:id = "@+id/minus2"
                android:layout_width = "wrap_content"
                android:layout_height = "wrap_content"
                android:text = "-"></Button>

            <TextView
                android:id = "@+id/text2"
                android:layout_width = "wrap_content"
                android:layout_height = "wrap_content" />
        </LinearLayout>
    </ScrollView>

</yourPackage.DoubleScrollView>

Нажимая кнопки + и -, вы можете заполнить представления прокрутки и посмотреть, как на это отреагирует макет.

Другие вопросы по теме

Наложение представления Android не предотвращает нажатие на элементы управления позади него
Как центрировать дочернее представление, но сохранить соотношение сторон в ConstraintLayout
Как сделать так, чтобы за прокручиваемым макетом следовал другой макет в активности Android
Как я могу создать такое нижнее представление навигации с помощью плавающей кнопки действия (FAB), содержащей как значок, так и изображение?
Как сделать FAB с иконкой и текстом
Как правильно выровнять два изображения в Android для всех размеров дисплея, используя макет ограничений
Как удалить дополнительное пространство между ImageView и краем экрана/родителя в Android Studio?
Цвет фона Android ProgressBar становится серым, когда фон родительского макета светлый
Разверните два TextView рядом друг с другом
Как заставить кнопки менять положение в зависимости от размера экрана и/или размера шрифта в Android