Jak zaimplementować niestandardowy widok AlertDialog


107

W dokumentacji systemu Android w AlertDialog zawiera następujące instrukcje i przykład ustawiania widoku niestandardowego w AlertDialog:

Jeśli chcesz wyświetlić bardziej złożony widok, wyszukaj FrameLayout o nazwie „body” i dodaj do niego swój widok:

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

Po pierwsze, jest całkiem oczywiste, że add()jest to literówka i tak ma być addView().

Jestem zdezorientowany pierwszą linią używającą R.id.body. Wygląda na to, że jest to element treści AlertDialog ... ale nie mogę po prostu wpisać tego w moim kodzie b / c, daje to błąd kompilacji. Gdzie R.id.body jest definiowany lub przypisywany czy cokolwiek?

Oto mój kod. Próbowałem użyć setView(findViewById(R.layout.whatever)na konstruktorze, ale to nie zadziałało. Zakładam, bo nie nadmuchałem go ręcznie?

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

Aby znaleźć i użyć swoich obiektów w oknie dialogowym, wykonaj następujące cztery kroki: stackoverflow.com/a/18773261/1699586
Sara

3
Odpowiedź w jednym wierszu: dodaj .setView(getLayoutInflater().inflate(R.layout.dialog_view, null))do kreatora. Podziękowania dla Sergio Viudesa, poniżej.
1 ''

Odpowiedzi:


49

Masz rację, to dlatego, że nie nadmuchałeś go ręcznie. Wygląda na to, że próbujesz „wyodrębnić” identyfikator „treści” z układu działania, ale to nie zadziała.

Prawdopodobnie chcesz coś takiego:

LayoutInflater inflater = getLayoutInflater();
FrameLayout f1 = (FrameLayout)alert.findViewById(android.R.id.body);
f1.addView(inflater.inflate(R.layout.dialog_view, f1, false));

17
Co ciekawe, body nie jest zdefiniowane jako stała w android.R.id. Nadal nie wiem, jak uzyskać dostęp do elementu „body” utworzonego elementu AlertDialog. Nadal chciałbym wiedzieć, jak to zrobić, ale na razie spróbuję po prostu zawyżyć widok i użyć setView w kreatorze.
stormin986

2
Właściwie to nadal pozostawia mi pytanie (jestem nowy w zawyżaniu wyświetleń). Używając builder.setView(inflater.inflate(R.id.dialog, ROOT_VIEWGROUP[, ATTACH_TO_ROOT])), dokumenty mówią, że główna grupa widoków jest opcjonalna. Czy powinno to być używane w tym przypadku? Jeśli tak ... nadal muszę dowiedzieć się, jak uzyskać odniesienie do AlertDialog ...
stormin986

2
Jest to opcjonalne, ale wtedy nie będziesz mieć odniesienia do rodzica z wnętrza układu, który nadmuchujesz. Rzeczy takie jak android: layout_gravity nie będą działać w widoku najwyższego poziomu ... i może nie potrzebujesz ich. Kiedy wywołujesz AlertDialog alert = builder.create (), masz odniesienie do swojego AlertDialog. Krótka odpowiedź krótka, jest opcjonalna. Spróbuj, w zależności od tego, co robisz w niestandardowym układzie, prawdopodobnie zadziała.
synic

2
Nie wiem, jak odwołać się do widoku w AlertDialog. Co byś polecił zrobić w tym przypadku, gdybym chciał odnieść się do rodzica? Jedyne, co widzę w alertDialog, które zwraca widok, to getCurrentFocus ()
stormin986

5
Trzymaj się Viewnapompowania. Wzywaj findViewById()to, Viewgdy potrzebujesz czegoś z jego zawartości. Zobacz: github.com/commonsguy/cw-android/tree/master/Database/Constants
CommonsWare

159

Możesz utworzyć widok bezpośrednio z modułu Layout Inflater, wystarczy użyć nazwy pliku XML układu i identyfikatora układu w pliku.

Twój plik XML powinien mieć następujący identyfikator:

<?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"
              />

Następnie możesz ustawić układ w kreatorze za pomocą następującego kodu:

LayoutInflater inflater = getLayoutInflater();
View dialoglayout = inflater.inflate(R.layout.dialog_layout, null);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(dialoglayout);
builder.show();

4
A gdzie w tym przykładzie jest R.id.dialog_layout_root? Czy to nie jest widok w bieżącej aktywności?
Alex Pretzlav

5
@AlexPretzlav: dialog_layout_root nie jest potrzebny w tym przykładzie. Wszystko czego potrzebujesz to nazwa twojego pliku xml dla R.layout. [Nazwa_pliku_xml].
IgorGanapolsky,

7
@Temperage Czy na końcu dodano builder.show. Wypróbowałem ten kod i zadziałało. Przekazałem również null jako drugi parametr infalter.inflate
Vinoth

2
Powinno to zostać wybrane jako najlepsza odpowiedź.
Salman Khakwani

2
Daje to ClassCastException, gdy układ niestandardowy zawiera EditText, ponieważ getCurrentFocus()zwróci EditText i nie można rzutować EditText na ViewGroup. Użycie nulldrugiego argumentu rozwiązuje ten problem.
1 ''

20

android.R.id.custom zwracał dla mnie wartość zerową. Udało mi się to uruchomić na wypadek, gdyby ktoś napotkał ten sam problem,

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

Dla porównania, R.layout.simple_password to:

<?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. Twoje rozwiązanie działa dobrze. Po prostu coś w tym jest. Powinniśmy nadmuchać przed builder.create i ustawić frameView.setLayoutParams (new LayoutParams (LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); lub coś najpierw, co zapobiegnie niektórym błędom, których się nie spodziewano
MOBYTELAB

dzięki za informację zwrotną ... jak nadmuchujesz przed builder.create ()? inflate jest wywoływane w inflaterze okna dialogowego i mam tylko okno dialogowe, ponieważ zadzwoniłem do builder.create ()
John Rellis

możemy użyć inflatera aktywności i nie dołączać go do FrameLayout, więc możemy zredukować taki mały kod, jak ten. ("tytuł") .setIcon (R.drawable.sample_icon); Wyświetl problemView = inflater.inflate (R.layout.sample_layout, null, false); builder.setView (kłopotyView); alert = builder.create (); Przepraszam, nie wiem, jak wyraźnie napisać kod w komentarzu
MOBYTELAB

Chodzi tylko o błąd projektowy, jeśli zbierzemy wszystko w układzie XML i zapomnimy o Framelayout jako źródle okna dialogowego, możemy być ciekawi, czy układ w xml nie jest wypełnieniem nadrzędnym lub czymś w tym rodzaju, tylko przypadkiem.
MOBYTELAB

Pracował dla mnie, dzięki! Ten pozwala mi dodać tytuł i mój przycisk Anuluj i OK, w tym EdytujTekst bez błędów. :) Pytanie tylko, jak to działa? Działa FrameLayoutjak fragment? Kiedy wypróbowałem powyższą odpowiedź Andrewx2, pojawił się błąd, ponieważ myślałem, że nadmuchuję dwa układy (to moje przypuszczenie).
Azurespot


12

To zadziałało dla mnie:

dialog.setView(dialog.getLayoutInflater().inflate(R.layout.custom_dialog_layout, null));

8

Custom AlertDialog

Ten pełny przykład obejmuje przekazywanie danych z powrotem do działania.

wprowadź opis obrazu tutaj

Utwórz niestandardowy układ

W EditTexttym prostym przykładzie zastosowano układ z symbolem, ale możesz go zastąpić dowolnym innym.

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>

Użyj okna dialogowego w kodzie

Kluczowe części to

  • za pomocą setViewprzypisać niestandardowy układ do plikuAlertDialog.Builder
  • wysyłanie danych z powrotem do działania po kliknięciu przycisku dialogowego.

To jest pełny kod z przykładowego projektu pokazanego na powyższym obrazku:

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

Uwagi

  • Jeśli zauważysz, że używasz tego w wielu miejscach, rozważ utworzenie DialogFragmentpodklasy zgodnie z opisem w dokumentacji .

Zobacz też


1
EditText editText = customLayout.findViewById(R.id.editText); powinno być EditText editText = (EditText) customLayout.findViewById(R.id.editText);
philcruz

4
@philcruz, Jeśli zaktualizujesz do Android Studio 3.0, nie musisz już jawnie przesyłać widoku. IDE może wywnioskować typ. To bardzo fajna funkcja. Bardzo pomaga oczyścić kod.
Suragch

świetna odpowiedź, bardzo pomocna
Noor Hossain

4

Najprostsze wiersze kodu, które działają dla mnie, to:

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

Niezależnie od typu układu (LinearLayout, FrameLayout, RelativeLayout) będzie działać setView i będzie się różnić wyglądem i zachowaniem.


niesamowite, proste i łatwe
Frank Eno

2

Najłatwiej to zrobić, używając android.support.v7.app.AlertDialogzamiast android.app.AlertDialoggdzie public AlertDialog.Builder setView (int layoutResId)można użyć poniżej 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();

Każdy, kto czyta to po wprowadzeniu API 21, powinien skorzystać z tej metody. Jak wspomniano w odpowiedzi, jeśli twoja wersja minSDK jest mniejsza niż 21, użyj AlerDialog z pakietu wsparcia. Voila !!!
Dibzmania

2

AlertDialog.setView(View view)dodaje dany widok do pliku R.id.custom FrameLayout. Poniżej znajduje się fragment kodu źródłowego Androida, z AlertController.setupView()którego w końcu to obsługuje ( mViewjest to widok przekazany AlertDialog.setViewmetodzie).

...
FrameLayout custom = (FrameLayout) mWindow.findViewById(R.id.**custom**);
custom.addView(**mView**, new LayoutParams(FILL_PARENT, FILL_PARENT));
...

1

Po zmianie identyfikatora na android.R.id.custom musiałem dodać następujące elementy, aby wyświetlić widok:

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

Jednak spowodowało to renderowanie nowego widoku w dużym widoku nadrzędnym bez tła, dzieląc okno dialogowe na dwie części (tekst i przyciski, a nowy widok pomiędzy nimi). W końcu uzyskałem efekt, który chciałem, wstawiając mój Widok obok wiadomości:

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

Znalazłem to rozwiązanie, eksplorując drzewo widoku za pomocą View.getParent () i View.getChildAt (int). Jednak nie jestem z tego zadowolony. Nic z tego nie jest w dokumentacji systemu Android i jeśli kiedykolwiek zmienią strukturę AlertDialog, może to się zepsuć.


1

Najbardziej sensowne byłoby zrobienie tego w ten sposób, przy najmniejszej ilości kodu.

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

Aby zobaczyć rozszerzoną listę rzeczy, które możesz ustawić, zacznij pisać .setw Android Studio

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.