Como implementar uma Visualização AlertDialog personalizada

107

Nos documentos do Android em AlertDialog , ele fornece a seguinte instrução e exemplo para definir uma visualização personalizada em um AlertDialog:

Se você deseja exibir uma visão mais complexa, procure o FrameLayout chamado "corpo" e adicione sua visão a ele:

FrameLayout fl = (FrameLayout) findViewById(R.id.body);
fl.add(myView, new LayoutParams(FILL_PARENT, WRAP_CONTENT));

Em primeiro lugar, é bastante óbvio que add()é um erro de digitação e é para ser addView().

Estou confuso com a primeira linha usando R.id.body. Parece que é o elemento do corpo do AlertDialog ... mas eu não posso simplesmente inserir isso no meu código b / c dá um erro de compilação. Onde R.id.body é definido ou atribuído ou qualquer outra coisa?

Aqui está meu código. Tentei usar setView(findViewById(R.layout.whatever)no construtor mas não funcionou. Estou supondo porque não inflou manualmente?

AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Title")
    .setCancelable(false)
    .setPositiveButton("Go", new DialogInterface.OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int id) {
        EditText textBox = (EditText) findViewById(R.id.textbox);
        doStuff();
    }
});

FrameLayout f1 = (FrameLayout)findViewById(R.id.body /*CURRENTLY an ERROR*/);
f1.addView(findViewById(R.layout.dialog_view));

AlertDialog alert = builder.create();
alert.show();
stormin986
fonte
Para encontrar e usar seus objetos em uma caixa de diálogo, siga estas quatro etapas: stackoverflow.com/a/18773261/1699586
Sara
3
Resposta de uma linha: adicione .setView(getLayoutInflater().inflate(R.layout.dialog_view, null))ao construtor. Crédito a Sergio Viudes, abaixo.
1 '' de

Respostas:

49

Você está correto, é porque você não inflou manualmente. Parece que você está tentando "extrair" o id do "corpo" do layout da sua atividade e isso não funcionará.

Você provavelmente quer algo assim:

LayoutInflater inflater = getLayoutInflater();
FrameLayout f1 = (FrameLayout)alert.findViewById(android.R.id.body);
f1.addView(inflater.inflate(R.layout.dialog_view, f1, false));
sínico
fonte
17
Curiosamente, o corpo não é definido como uma constante no android.R.id. Ainda não estou certo sobre como acessar o elemento 'body' do AlertDialog criado. Ainda gostaria de saber como fazer isso, mas, por enquanto, tentarei apenas aumentar uma visualização e usar setView no construtor.
stormin986
2
Na verdade, isso ainda me deixa com uma pergunta então (eu sou novo em aumentar visualizações). Usando builder.setView(inflater.inflate(R.id.dialog, ROOT_VIEWGROUP[, ATTACH_TO_ROOT])), os documentos dizem que o grupo de exibição raiz é opcional. Isso deve ser usado neste caso? Se assim for ... ainda tenho que descobrir como obter uma referência para o AlertDialog ...
stormin986
2
É opcional, mas você não terá uma referência ao pai de dentro do layout que está aumentando. Coisas como android: layout_gravity não funcionam na visualização de nível superior ... e talvez você não precise delas. Ao chamar AlertDialog alert = builder.create (), você tem uma referência ao seu AlertDialog. Resposta longa curta, é opcional. Experimente, dependendo do que você está fazendo em seu layout personalizado, provavelmente funcionará.
synic
2
Não tenho certeza de como fazer referência à exibição no AlertDialog. O que você recomendaria fazer neste caso se eu quisesse fazer referência ao pai? A única coisa que vejo em alertDialog que retorna uma visualização é getCurrentFocus ()
stormin986
5
Segure a Viewvocê inflar. Chame findViewById()-o Viewquando precisar de coisas do seu conteúdo. Consulte: github.com/commonsguy/cw-android/tree/master/Database/Constants
CommonsWare
159

Você pode criar sua visualização diretamente do Layout Inflater, você só precisa usar o nome do seu arquivo XML de layout e a ID do layout no arquivo.

Seu arquivo XML deve ter um ID como este:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/dialog_layout_root"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="wrap_content"
              android:padding="10dp"
              />

E então você pode definir seu layout no construtor com o seguinte código:

LayoutInflater inflater = getLayoutInflater();
View dialoglayout = inflater.inflate(R.layout.dialog_layout, null);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(dialoglayout);
builder.show();
Andrewx2
fonte
4
E onde está R.id.dialog_layout_root neste exemplo? Não é uma visão na atividade atual?
Alex Pretzlav
5
@AlexPretzlav: dialog_layout_root não é necessário neste exemplo. Tudo que você precisa é o nome do seu arquivo xml para R.layout. [Name_of_xml_file].
IgorGanapolsky
7
@Temperage Você adicionou builder.show no final. Eu tentei este código e funcionou. Também passei null como segundo parâmetro para infalter.inflate
Vinoth
2
Esta deveria ter sido selecionada como a melhor resposta.
Salman Khakwani
2
Isso fornece uma ClassCastException quando o layout personalizado contém um EditText, porque getCurrentFocus()retornará o EditText e um EditText não pode ser convertido para um ViewGroup. Usar nullcomo segundo argumento corrige isso.
1 '' de
20

android.R.id.custom estava retornando null para mim. Consegui fazer isso funcionar, caso alguém tenha o mesmo problema,

AlertDialog.Builder builder = new AlertDialog.Builder(context)
            .setTitle("My title")
            .setMessage("Enter password");
final FrameLayout frameView = new FrameLayout(context);
builder.setView(frameView);

final AlertDialog alertDialog = builder.create();
LayoutInflater inflater = alertDialog.getLayoutInflater();
View dialoglayout = inflater.inflate(R.layout.simple_password, frameView);
alertDialog.show();

Para referência, R.layout.simple_password é:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:orientation="vertical"
          android:layout_width="match_parent"
          android:layout_height="match_parent">

<EditText
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/password_edit_view"
        android:inputType="textPassword"/>
<CheckBox
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/show_password"
        android:id="@+id/show_password_checkbox"
        android:layout_gravity="left|center_vertical"
        android:checked="false"/>

</LinearLayout>
John Rellis
fonte
John Rellis. Sua solução funciona bem. Basta ter algo nele. Devemos aumentar antes de builder.create e definir frameView.setLayoutParams (new LayoutParams (LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); ou algo primeiro, que irá prevenir alguns bugs que não
eram
obrigado pelo feedback ... como você aumenta antes de builder.create ()? inflate é chamado no inflador da caixa de diálogo e eu só tenho uma caixa de diálogo porque chamei builder.create ()
John Rellis
podemos usar o inflater de atividade, e não anexar ao FrameLayout, para que possamos reduzir um pequeno código como this.AlertDialog.Builder builder = new AlertDialog.Builder (new ContextThemeWrapper (getActivity (), android.R.style.Theme_Holo)) .setTitle ("título") .setIcon (R.drawable.sample_icon); Exibir troubleView = inflater.inflate (R.layout.sample_layout, null, false); builder.setView (troubleView); alert = builder.create (); Desculpe, não sei como escrever código claramente no comentário
MOBYTELAB
É apenas uma questão de bug de design, se juntarmos tudo no layout xml e esquecermos o Framelayout como uma raiz do diálogo, podemos ficar curiosos se o layout em xml não é fill parent ou algo assim, apenas um caso.
MOBYTELAB
Funcionou para mim, obrigado! Este me permite adicionar um título e meu botão Cancelar e OK, incluindo o EditText sem erros. :) A única questão é: como isso funciona? Atua FrameLayoutcomo uma espécie de fragmento? Quando tentei a resposta de Andrewx2 acima, recebi um erro porque pensei que eu estava inflando dois layouts (imagino).
Azurespot
12

Isso funcionou para mim:

dialog.setView(dialog.getLayoutInflater().inflate(R.layout.custom_dialog_layout, null));
Sergio Viudes
fonte
8

Custom AlertDialog

Este exemplo completo inclui a transmissão de dados de volta para a Activity.

insira a descrição da imagem aqui

Crie um layout personalizado

Um layout com um EditTexté usado para este exemplo simples, mas você pode substituí-lo por qualquer coisa que desejar.

custom_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:paddingLeft="20dp"
              android:paddingRight="20dp"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <EditText
        android:id="@+id/editText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

Use a caixa de diálogo em código

As peças principais são

  • usando setViewpara atribuir o layout personalizado aoAlertDialog.Builder
  • enviando quaisquer dados de volta para a atividade quando um botão de diálogo é clicado.

Este é o código completo do projeto de exemplo mostrado na imagem acima:

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void showAlertDialogButtonClicked(View view) {

        // create an alert builder
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Name");

        // set the custom layout
        final View customLayout = getLayoutInflater().inflate(R.layout.custom_layout, null);
        builder.setView(customLayout);

        // add a button
        builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // send data from the AlertDialog to the Activity
                EditText editText = customLayout.findViewById(R.id.editText);
                sendDialogDataToActivity(editText.getText().toString());
            }
        });

        // create and show the alert dialog
        AlertDialog dialog = builder.create();
        dialog.show();
    }

    // do something with the data coming from the AlertDialog
    private void sendDialogDataToActivity(String data) {
        Toast.makeText(this, data, Toast.LENGTH_SHORT).show();
    }
}

Notas

  • Se você estiver usando isso em vários lugares, considere fazer uma DialogFragmentsubclasse conforme descrito na documentação .

Veja também

Suragch
fonte
1
EditText editText = customLayout.findViewById(R.id.editText); deveria ser EditText editText = (EditText) customLayout.findViewById(R.id.editText);
philcruz
4
@philcruz, se você atualizar para o Android Studio 3.0, não precisará mais lançar a visualização explicitamente. O IDE pode inferir o tipo. É um recurso muito bom. Isso ajuda a limpar muito o código.
Suragch
ótima resposta, muito útil
Noor Hossain
4

As linhas de código mais simples que funcionam para mim são as seguintes:

AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(R.layout.layout_resource_id);
builder.show();

Qualquer que seja o tipo de layout (LinearLayout, FrameLayout, RelativeLayout) funcionará setView e apenas diferirá na aparência e comportamento.

kenix
fonte
incrível, simples e fácil
Frank Eno,
2

A maneira mais fácil de fazer isso é usando em android.support.v7.app.AlertDialogvez de android.app.AlertDialogonde public AlertDialog.Builder setView (int layoutResId)pode ser usado abaixo da API 21.

new AlertDialog.Builder(getActivity())
    .setTitle(title)
    .setView(R.layout.dialog_basic)
    .setPositiveButton(android.R.string.ok,
        new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                //Do something
            }
        }
    )
    .setNegativeButton(android.R.string.cancel,
        new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                //Do something
            }
        }
    )
    .create();
flanaras
fonte
Qualquer pessoa que ler isso depois que a API 21 foi introduzida, deve usar este método. Como a resposta menciona, se seu aplicativo minSDKversion for menor que 21, use o AlerDialog do pacote de suporte. Voila !!!
Dibzmania
2

AlertDialog.setView(View view)adiciona a visualização fornecida ao R.id.custom FrameLayout. A seguir está um trecho do código-fonte do Android a partir do AlertController.setupView()qual finalmente lida com isso ( mViewé a visão dada ao AlertDialog.setViewmétodo).

...
FrameLayout custom = (FrameLayout) mWindow.findViewById(R.id.**custom**);
custom.addView(**mView**, new LayoutParams(FILL_PARENT, FILL_PARENT));
...
Jung Soo Kim
fonte
1

Depois de alterar o ID de android.R.id.custom, precisei adicionar o seguinte para fazer com que a visualização fosse exibida:

((View) f1.getParent()).setVisibility(View.VISIBLE);

No entanto, isso fez com que a nova vista fosse renderizada em uma grande vista pai sem fundo, quebrando a caixa de diálogo em duas partes (texto e botões, com a nova vista no meio). Finalmente consegui o efeito que queria inserindo minha Visualização ao lado da mensagem:

LinearLayout f1 = (LinearLayout)findViewById(android.R.id.message).getParent().getParent();

Eu encontrei essa solução explorando a árvore de visualização com View.getParent () e View.getChildAt (int). Não estou muito feliz com nenhum dos dois. Nada disso está nos documentos do Android e, se eles alterarem a estrutura do AlertDialog, isso pode falhar.

Black Mantha
fonte
1

Faria mais sentido fazer dessa maneira, com menos quantidade de código.

new AlertDialog.Builder(this).builder(this)
        .setTitle("Title")
        .setView(R.id.dialog_view)   //notice this setView was added
        .setCancelable(false)
        .setPositiveButton("Go", new DialogInterface.OnClickListener() {
            @Override 
            public void onClick(DialogInterface dialog, int id) {
                EditText textBox = (EditText) findViewById(R.id.textbox);
                doStuff();
            }
        }).show();

Para obter uma lista expandida de coisas que você pode definir, comece a digitar .setno Android Studio

StackAttack
fonte