Как предотвратить сокращение текста заголовка диалога, когда положительный текст кнопки становится короче?

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

Все работает нормально, за исключением того, что когда пользователь попадает на третий и последний экран, размер текста заголовка уменьшается! Это происходит потому, что строка для текста положительной кнопки короче для последнего экрана. Если я увеличу его, текст заголовка больше не будет сжиматься на последнем экране.

Вот урезанная версия моего кода. dialog.xml файл макета:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:paddingStart="16dp"
    android:paddingEnd="16dp"
    android:orientation="vertical">

    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"/>

</LinearLayout>

Это код фрагмента диалога:

package com.world.test.fixshrinkingdialogtitletext;

import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;

public class ChangeTitleDialogFragment
        extends DialogFragment {

    private static final String DIALOG_TITLE = "DIALOG_TITLE";
    private static final String TEXT_PROMPT = "TEXT_PROMPT";
    private static final String POSITIVE_BUTTON_TEXT = "POSITIVE_BUTTON_TEXT";
    private static final String SCREEN_COUNT = "SCREEN_COUNT";

    private String dialogTitle = null;
    private String textPrompt = null;
    private String positiveButtonText = null;
    private int screenCount;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState == null) {
            screenCount = 1;
            dialogTitle = "First Screen";
            textPrompt = "Click continue";
            positiveButtonText = "Continue";
        } else {
            dialogTitle = savedInstanceState.getString(DIALOG_TITLE);
            textPrompt = savedInstanceState.getString(TEXT_PROMPT);
            positiveButtonText = savedInstanceState.getString(POSITIVE_BUTTON_TEXT);
            screenCount = savedInstanceState.getInt(SCREEN_COUNT);
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString(DIALOG_TITLE, dialogTitle);
        outState.putString(TEXT_PROMPT, textPrompt);
        outState.putString(POSITIVE_BUTTON_TEXT, positiveButtonText);
        outState.putInt(SCREEN_COUNT, screenCount);
    }

    @Override
    @NonNull
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

        LayoutInflater inflater = getActivity().getLayoutInflater();
        @SuppressLint("InflateParams")
        final View view = inflater.inflate(R.layout.dialog, null);
        builder.setView(view);
        builder.setTitle(dialogTitle);
        builder.setMessage(textPrompt);
        builder.setPositiveButton(positiveButtonText, null);
        builder.setNegativeButton("Cancel", null);

        AlertDialog dialog = builder.create();

        dialog.setOnShowListener(new DialogInterface.OnShowListener() {

            @Override
            public void onShow(final DialogInterface dialog) {
                final AlertDialog alertDialog = (AlertDialog) dialog;

                final Button okButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
                okButton.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {

                        if (screenCount == 3) {
                            dismiss();
                        } else {
                            screenCount++;

                            dialogTitle = "Screen Number " + screenCount;
                            alertDialog.setTitle(dialogTitle);

                            if (screenCount == 3) {
                                // "OKOKOKOKO" won't cause the dialog title text to shrink!
                                positiveButtonText = "OKOKOKOK";
                                okButton.setText(positiveButtonText);
                            }
                        }
                    }
                });
            }
        });

        return dialog;
    }
}

А вот скриншоты 3-х экранов (последние 2 обрезаны):

экран 1

экран 2 (обрезанный

экран 3 (обрезанный

Я заметил, что на последнем экране диалоговое окно немного уменьшилось в ширине, предположительно потому, что макет настроен на перенос содержимого, а текст на положительной кнопке короче. Так что, возможно, Android автоматически сокращает размер текста заголовка, чтобы обеспечить его соответствие. Но это предположение.

Кто-нибудь знает, как обновить текст заголовка, но оставить его одинаковым на всех экранах?

Я нахожу несознательным, что размер текста заголовка изменяется вообще. Кто-нибудь может объяснить, почему он меняет размер? Это предполагаемое поведение или ошибка Android?

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

2 ответа

Решение

Это было трудно решить, поэтому я хочу поделиться этим здесь, чтобы другим не пришлось рвать на себе волосы, как я!

Во-первых, почему текст заголовка в первую очередь меняет размер? Я не знаю наверняка, но я предполагаю, что тема приложения определяет размер текста в единицах "масштабированного пикселя", что означает, что фактический размер текста может измениться, если изменится конфигурация экрана. Поэтому я думаю, что это намеренное поведение.

Макет примера, который я использовал, был немного глупым, потому что у него был неиспользуемый TextView, как указывал @Ruan_Lopes. Это совершенно не нужно, потому что я мог бы использовать alertDialog.setMessage(textPrompt) в любом случае изменить сообщение диалога. Фактически, использование средства просмотра иерархии макетов в Android Device Monitor показало, что макет пользователя в любом случае вставляется под сообщение.

Средство просмотра иерархии также показало мне, что высота и ширина заголовка диалогового окна предупреждения фактически не менялась, даже когда текст уменьшался! Таким образом, установка высоты или ширины TextView для заголовка не устраняет проблему сжатия текста.

В конце я записал размер текста заголовка (в пикселях), когда диалоговое окно было впервые показано, а затем использовал это же значение для установки размера текста всякий раз, когда я обновлял текст заголовка диалога. Поскольку я также хотел сохранить размер текста при поворотах устройства, я решил сохранить размер текста в onSaveInstanceState(Bundle outState), Но вам не нужно этого делать, если вы не возражаете, что размер текста заголовка отличается от поворота экрана.

Обратите внимание, что TextView.getTextSize() возвращает значение в пикселях, поэтому вы должны использовать правильные единицы при вызове TextView.setTextSize(int, float),

Вот окончательный код. Я не изменил макет XML, кроме как удалить TextView и заменить его на EditText хотя это не имеет значения здесь.

package com.world.test.fixshrinkingdialogtitletext;

import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class ChangeTitleDialogFragment
        extends DialogFragment {

    private static final String DIALOG_TITLE = "DIALOG_TITLE";
    private static final String TEXT_PROMPT = "TEXT_PROMPT";
    private static final String POSITIVE_BUTTON_TEXT = "POSITIVE_BUTTON_TEXT";
    private static final String SCREEN_COUNT = "SCREEN_COUNT";
    private static final String TITLE_TEXT_SIZE = "TITLE_TEXT_SIZE";

    private String dialogTitle = null;
    private String textPrompt = null;
    private String positiveButtonText = null;
    private int screenCount;
    private float titleTextSize;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (savedInstanceState == null) {
            screenCount = 1;
            dialogTitle = "First screen";
            textPrompt = "Click continue";
            positiveButtonText = "Continue";
            titleTextSize = -1;
        } else {
            dialogTitle = savedInstanceState.getString(DIALOG_TITLE);
            textPrompt = savedInstanceState.getString(TEXT_PROMPT);
            positiveButtonText = savedInstanceState.getString(POSITIVE_BUTTON_TEXT);
            screenCount = savedInstanceState.getInt(SCREEN_COUNT);
            titleTextSize = savedInstanceState.getFloat(TITLE_TEXT_SIZE);
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        outState.putString(DIALOG_TITLE, dialogTitle);
        outState.putString(TEXT_PROMPT, textPrompt);
        outState.putString(POSITIVE_BUTTON_TEXT, positiveButtonText);
        outState.putInt(SCREEN_COUNT, screenCount);
        outState.putFloat(TITLE_TEXT_SIZE, titleTextSize);
    }

    @Override
    @NonNull
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

        LayoutInflater inflater = getActivity().getLayoutInflater();
        @SuppressLint("InflateParams")
        final View view = inflater.inflate(R.layout.dialog, null);
        builder.setView(view);
        builder.setTitle(dialogTitle);
        builder.setMessage(textPrompt);
        builder.setPositiveButton(positiveButtonText, null);
        builder.setNegativeButton("Cancel", null);

        AlertDialog dialog = builder.create();

        dialog.setOnShowListener(new DialogInterface.OnShowListener() {

            @Override
            public void onShow(final DialogInterface dialog) {
                final AlertDialog alertDialog = (AlertDialog) dialog;

                final int alertTitleId = getActivity().getResources().getIdentifier( "alertTitle", "id", "android" );
                final TextView titleTV = (TextView)alertDialog.findViewById(alertTitleId);
                if (titleTextSize < 0) {
                    // Then user has launched this dialog directly from the calling activity.
                    titleTextSize = titleTV.getTextSize(); // Units = pixels!
                } else {
                    // Use the existing value we saved when the configuration changed:
                    titleTV.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleTextSize);
                }

                final Button okButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
                okButton.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {

                        if (screenCount == 3) {
                            dismiss();
                        } else {
                            screenCount++;

                            textPrompt = "Blah blah blah blah blah blah blah blah";
                            alertDialog.setMessage(textPrompt);

                            dialogTitle = "Screen Number " + screenCount;
                            alertDialog.setTitle(dialogTitle);

                            titleTV.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleTextSize);

                            if (screenCount == 3) {
                                positiveButtonText = "OK";
                                okButton.setText(positiveButtonText);
                            }
                        }
                    }
                });
            }
        });

        return dialog;
    }
}

Вы пытались установить minWidth на линейном макете?

ех.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"

             android:minWidth="150dp"  <!-- The size you want -->

             android:paddingStart="16dp"
             android:paddingEnd="16dp"
             android:orientation="vertical">

<TextView
    android:id="@+id/text_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textAppearance="?android:attr/textAppearanceMedium"/>

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