Как заставить GridLayout соответствующим образом изменять размер дочерних представлений?

Фон

Мне нужно сделать вид, похожий на панель набора номера, как в приложении "Телефон".

Я использую GridLayout представлений. Каждая ячейка имеет одинаковый размер и содержит простой TextView, который при необходимости должен изменить размер шрифта.

Эта проблема

Мне это удалось, но по какой-то причине это не работает в соответствии с предоставленным пространством.

Если на нем много места, он отлично работает:

введите здесь описание изображения

Однако, когда он становится меньше (пример: маленькие экраны, альбомная ориентация, разделенное окно ...), становятся видимыми только верхние кнопки сетки, и они даже не меняют размер своего шрифта, как будто все они хотят быть самого большого размера они могут:

введите здесь описание изображения

Что я пробовал

Я пытался изменить различные атрибуты представлений, но ничего не помогло.

Я знаю, однако, что панель набора номера в приложении «Телефон» на самом деле не меняет размер шрифта. До некоторого размера он отображается нормально, а если он слишком мал, он меняется на другой макет. Это особенно важно для ландшафтного режима и режима разделенного окна.

Вот код, который я сделал (я изменяю значение layout_constraintHeight_percent, чтобы проверить различные размеры верхней области):

градиент

...
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.core:core-ktx:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.gridlayout:gridlayout:1.0.0'
...

QueryKeyboard.kt

class QueryKeyboard @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : GridLayout(context, attrs, defStyle) {
    init {
        orientation = HORIZONTAL
        clipChildren = false
        clipToPadding = false
        columnCount = 3
        rowCount = 4
        //workaround for a weird issue of seeing just 3 huge buttons, instead of all
        val runnable = Runnable {
            for (i in 1..9)
                addView(generateGridTextButton(i.toString()))
            addView(generateGridTextButton("*"))
            addView(generateGridTextButton("0"))
            addView(generateGridTextButton("+"))
        }
        if (isInEditMode)
            runnable.run()
        else
            this.doOnPreDraw { runnable.run() }
    }

    private fun generateGridTextButton(textToShowAndAddUponClick: CharSequence): TextView {
        val tv = LayoutInflater.from(context).inflate(R.layout.grid_text_button, this, false) as TextView
        tv.text = textToShowAndAddUponClick
        return tv
    }
}

grid_text_button.xml

<androidx.appcompat.widget.AppCompatTextView 
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:background="?attr/selectableItemBackgroundBorderless"
   android:breakStrategy="balanced"
   android:clickable="true"
   android:focusable="true"
   android:focusableInTouchMode="false"
   android:gravity="center"
   android:textColor="#000"
   android:textSize="36dp"
   app:autoSizeMaxTextSize="36dp"
   app:autoSizeMinTextSize="12dp"
   app:layout_columnWeight="1"
   app:layout_gravity="fill"
   app:layout_rowWeight="1"
   tools:layout_gravity="center"
   tools:targetApi="m"
   tools:text="1" />

Использование в activity_main.xml:

<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="0px"
        android:background="#33ff0000" android:gravity="center" android:text="some content"
        app:layout_constraintBottom_toTopOf="@id/queryKeyboard" app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHeight_percent="0.6" app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.sample.myapplication.QueryKeyboard
        android:id="@+id/queryKeyboard" android:layout_width="match_parent" android:layout_height="0px"
        app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/textView" />

</androidx.constraintlayout.widget.ConstraintLayout>

РЕДАКТИРОВАТЬ: я попытался обернуть TextView с помощью FrameLayout, чтобы показать размер каждой ячейки:

grid_text_button.xml

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="wrap_content" android:layout_height="wrap_content"
    app:layout_columnWeight="1" app:layout_gravity="fill" app:layout_rowWeight="1">

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:background="?attr/selectableItemBackgroundBorderless" android:breakStrategy="balanced"
        android:clickable="true" android:focusable="true" android:focusableInTouchMode="false" android:gravity="center"
        android:textColor="#000" android:textSize="36sp" app:autoSizeMaxTextSize="36sp" app:autoSizeMinTextSize="12sp"
        tools:layout_gravity="center" tools:targetApi="m" tools:text="1" />
</FrameLayout>

QueryKeyboard.kt

class QueryKeyboard @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : GridLayout(context, attrs, defStyle) {
    private var cellBackgroundColor = 0xffff0000.toInt()

    init {
        orientation = HORIZONTAL
        clipChildren = false
        clipToPadding = false
        columnCount = 3
        rowCount = 4
        //workaround for a weird issue of seeing just 3 huge buttons, instead of all
        val runnable = Runnable {
            for (i in 1..9) {
                addView(generateGridTextButton(i.toString()))
            }
            addView(generateGridTextButton("*"))
            addView(generateGridTextButton("0"))
            addView(generateGridTextButton("+"))
        }
        if (isInEditMode)
            runnable.run()
        else
            this.doOnPreDraw { runnable.run() }
    }

    private fun switchColor() {
        cellBackgroundColor = if (cellBackgroundColor == 0xffff0000.toInt()) 0xff00ff00.toInt() else 0xffff0000.toInt()
    }

    private fun generateGridTextButton(textToShowAndAddUponClick: CharSequence): View {
        val view = LayoutInflater.from(context).inflate(R.layout.grid_text_button, this, false)
        switchColor()
        view.setBackgroundColor(cellBackgroundColor)
        view.textView.text = textToShowAndAddUponClick
        return view
    }
}

И вот 2 случая, когда он работает нормально, а когда нет:

введите здесь описание изображения

введите здесь описание изображения

Как и раньше. Получение 3 ячеек, текст не по центру и автоматическое изменение размера шрифта.

Вопросы

  1. Почему ячейки не регулируют свои размеры, включая размер шрифта каждой из них? Почему я вижу всего 3 клетки, когда они слишком маленькие? Как я могу это исправить?

  2. Есть ли лучшая альтернатива? Думаю, я мог бы использовать LinearLayout из нескольких экземпляров LinearLayout, но это просто странно для этого случая ... В конце концов, как часто вы используете GridLayout ... :)

  3. Как я могу определить, когда он слишком мал, чтобы переключиться на другой макет, как в приложении «Телефон», включая все используемые ими случаи (даже с разделенным окном)? Возможно, они просто использовали квалификатор для макетов? Если да, что рекомендуется для этого случая?


person android developer    schedule 22.08.2019    source источник
comment
Можете ли вы использовать макет сетки с настраиваемым классом высоты? Высота будет зависеть от размера экрана.   -  person BlackBlind    schedule 22.08.2019
comment
@BlackBlind Размер ячеек должен быть изменен на основе GridLayoutView.   -  person android developer    schedule 22.08.2019
comment
можешь поделиться со мной в драйве.?   -  person BlackBlind    schedule 22.08.2019
comment
Если вы не возражаете, вы можете поделиться со мной только кодом клавиатуры, чтобы сделать его ответственным.   -  person BlackBlind    schedule 23.08.2019
comment
@BlackBlind Код уже здесь. Что вы имеете в виду, говоря об ответственности?   -  person android developer    schedule 23.08.2019


Ответы (2)


Почему ячейки не регулируют свои размеры, включая размер шрифта каждой из них? Как я могу это исправить?

Ячейка может динамически регулировать свой размер на основе двух основных ограничений: the weight of its parent и the value of its textsize or childview. Фиксированный размер текста в ячейках может привести к несогласованности в дизайне. Чтобы решить эту проблему, вы можете либо установить вес макета в parentview с шириной и высотой ячеек, совпадающими с родительскими, либо создать разные размеры текста для целевых устройств.

Есть ли лучшая альтернатива? Думаю, я мог бы использовать LinearLayout из нескольких экземпляров LinearLayout, но это просто странно для этого случая ... В конце концов, как часто вы используете GridLayout ... :)

Есть способ получше, и это то, что вы сейчас реализуете The GridLayout. Использование LinearLayouts с отступом ограничит вас множеством преимуществ и заставит вас писать больше кода, например, в случаях, когда вам нужно переключать или анимировать ячейки, получать доступ к n-му столбцу n-й строки, динамически изменять диапазон ячеек и т. Д. Все это может быть выполняется через Gridlayout с помощью нескольких строк кода. Это сильнее, чем вы думаете.

Как я могу определить, когда он слишком мал, чтобы переключиться на другой макет, как в приложении «Телефон», включая все используемые ими случаи (даже с разделенным окном)? Возможно, они просто использовали квалификатор для макетов? Если да, что рекомендуется для этого случая?

Вам нечего обнаруживать, просто следуйте инструкциям, и Android выполнит обнаружение.

Вот несколько способов управления сценарием на основе рекомендаций по дизайну Android.

Первое: создайте альбомный и портретный режим макета для своего занятия (layout/activity.xml и layout-land/activity.xml).

layout / activity.xml

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android
 xmlns:app="http://schemas.android.com/apk/res-auto"
 xmlns:tools="http://schemas.android.com/tools" 
 android:layout_width="match_parent"
android:layout_height="match_parent" 
android:orientation="vertical"
 android:weightSum="2"
tools:context=".MainActivity">

<TextView
    android:id="@+id/textView" 
    android:layout_width="match_parent" 
    android:layout_height="0dp"
    android:background="#33ff0000"
    android:gravity="center" android:text="some content"
    android:layout_weight="1" />

 <com.sample.myapplication.QueryKeyboard
    android:id="@+id/queryKeyboard" 
   android:layout_width="match_parent" 
    android:layout_height="0dp"
    android:layout_weight="1" />
 </LinearLayout>

layout-land / activity.xml

  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android
 xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" 
android:layout_width="match_parent"
android:layout_height="match_parent" 
android:orientation="horizontal"
 android:weightSum="2"
tools:context=".MainActivity">
<TextView
    android:id="@+id/textView" 
    android:layout_width="0dp" 
    android:layout_height="match_parent"
    android:background="#33ff0000"
    android:gravity="center" android:text="some content"
    android:layout_weight="1" />

 <com.sample.myapplication.QueryKeyboard
    android:id="@+id/queryKeyboard" 
   android:layout_width="0dp" 
    android:layout_height="match_parent"
    android:layout_weight="1" />
 </LinearLayout>

Кроме того, вам нужно обрабатывать текст для различного размера экрана.

Это вычисление размера текста можно выполнить автоматически, добавив библиотеку Android Intuit в список зависимостей в Gradle.

dependencies {
   implementation 'com.intuit.ssp:ssp-android:1.0.6'
 }

Затем в вашей сетке кнопка textize call

 android:textSize="@dimens/_30ssp"
person Giddy Naya    schedule 23.08.2019
comment
Я уже установил автоматическую настройку ячеек, используя атрибуты. Пожалуйста, проверьте код, который я показал. Что касается квалификатора ландшафта, как я уже писал, этого недостаточно, потому что проблема может возникнуть даже при портретной ориентации. Вы изменили вес на 50%, но я показал, что проблема может возникнуть при других размерах. Что касается автоматического изменения размера TextView, я уже установил его, используя атрибуты библиотеки поддержки. Мой вопрос был: почему он автоматически не изменяет размер ячеек, размер шрифта и не центрирует TextView? - person android developer; 26.08.2019
comment
Во всяком случае, я написал собственное решение, в том числе и на основе LinearLayout. - person android developer; 26.08.2019

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

Если кто-нибудь узнает, почему это происходит, дайте мне знать. На данный момент я считаю это ошибкой, поэтому я сообщил здесь .

grid_text_button.xml

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="wrap_content"
    android:layout_height="wrap_content" android:background="?attr/selectableItemBackgroundBorderless"
    android:clickable="true" android:focusable="true" android:focusableInTouchMode="false" app:layout_columnWeight="1"
    app:layout_gravity="fill" app:layout_rowWeight="1" tools:layout_gravity="center">

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:layout_gravity="center" android:breakStrategy="balanced" android:gravity="center"
        android:textColor="#000" app:autoSizeMaxTextSize="36sp" app:autoSizeMinTextSize="12sp" tools:targetApi="m"
        tools:text="1" />
</FrameLayout>

QueryKeyboard.kt

class QueryKeyboard @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : GridLayout(context, attrs, defStyle) {
    //    private var cellBackgroundColor = 0xffff0000.toInt()
    init {
        orientation = HORIZONTAL
        clipChildren = false
        clipToPadding = false
        columnCount = 3
        rowCount = 4
        for (i in 1..9)
            addView(generateGridTextButton(i.toString()))
        addView(generateGridTextButton("*"))
        addView(generateGridTextButton("0"))
        addView(generateGridTextButton("+"))
    }

    //    private fun switchColor() {
    //        cellBackgroundColor = if (cellBackgroundColor == 0xffff0000.toInt()) 0xff00ff00.toInt() else 0xffff0000.toInt()
    //    }
    private fun generateGridTextButton(textToShowAndAddUponClick: CharSequence): View {
        val view = LayoutInflater.from(context).inflate(R.layout.grid_text_button, this, false)
        //        switchColor()
        //        view.setBackgroundColor(cellBackgroundColor)
        view.textView.text = textToShowAndAddUponClick
        return view
    }
}

Альтернативой реализации GridLayout, в которой нет этой проблемы, но все же странно, что я ее использовал, является, как я уже писал, LinearLayout из LinearLayouts:

class QueryKeyboard2 @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : LinearLayout(context, attrs, defStyleAttr) {
    //private var cellBackgroundColor = 0xffff0000.toInt()

    init {
        orientation = VERTICAL
        clipChildren = false
        clipToPadding = false
        val columnCount = 3
        val rowCount = 4
        val cellsList = ArrayList<View>()
        for (i in 1..9)
            cellsList.add(generateGridTextButton(i.toString()))
        cellsList.add(generateGridTextButton("*"))
        cellsList.add(generateGridTextButton("0"))
        cellsList.add(generateGridTextButton("+"))
        for (i in 0 until rowCount) {
            val rowLayout = generateRowLayout(context)
            for (j in 0 until columnCount) {
                val cellView = cellsList[i * columnCount + j]
                val cellLayoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT)
                cellLayoutParams.weight = 1f
                rowLayout.addView(cellView, cellLayoutParams)
            }
            //            switchColor()
            //            rowLayout.setBackgroundColor(cellBackgroundColor)
            addView(rowLayout)
        }
    }

    private fun generateRowLayout(context: Context): LinearLayout {
        val result = LinearLayout(context)
        result.layoutParams = LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0)
        (result.layoutParams as LayoutParams).weight = 1f
        result.orientation = HORIZONTAL
        return result
    }

    //private fun switchColor() {
    //    cellBackgroundColor = if (cellBackgroundColor == 0xffff0000.toInt()) 0xff00ff00.toInt() else 0xffff0000.toInt()
    //}

    private fun generateGridTextButton(textToShowAndAddUponClick: CharSequence): View {
        val view = LayoutInflater.from(context).inflate(R.layout.grid_text_button, this, false)
        //switchColor()
        //view.setBackgroundColor(cellBackgroundColor)
        view.textView.text = textToShowAndAddUponClick
        return view
    }
}

Что касается попытки отобразить его только тогда, когда есть достаточно места (высоты), я установил макет для его использования на «res / layout-h400dp» (может быть изменен в соответствии с потребностями) и другой, где панель набора номера находится справа, для обычного "разрешения / раскладки".

person android developer    schedule 26.08.2019