Cara menerapkan Tampilan AlertDialog kustom

107

Dalam dokumen Android di AlertDialog , ini memberikan instruksi dan contoh berikut untuk menyetel tampilan kustom di AlertDialog:

Jika Anda ingin menampilkan tampilan yang lebih kompleks, cari FrameLayout yang disebut "body" dan tambahkan tampilan Anda ke sana:

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

Pertama, cukup jelas bahwa add()itu salah ketik dan memang seharusnya begitu addView().

Saya bingung dengan baris pertama yang menggunakan R.id.body. Tampaknya itu adalah elemen tubuh dari AlertDialog ... tetapi saya tidak bisa memasukkannya begitu saja dalam kode saya b / c itu memberikan kesalahan kompilasi. Di mana R.id.body ditetapkan atau ditugaskan atau apa pun?

Ini kode saya. Saya mencoba menggunakan setView(findViewById(R.layout.whatever)pada pembangun tetapi tidak berhasil. Saya berasumsi karena saya tidak mengembang secara manual?

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
sumber
Untuk menemukan dan menggunakan objek Anda pada Dialog, ikuti empat langkah ini: stackoverflow.com/a/18773261/1699586
Sara
3
Jawaban satu baris: tambahkan .setView(getLayoutInflater().inflate(R.layout.dialog_view, null))ke pembangun. Penghargaan untuk Sergio Viudes, di bawah.
1 ''

Jawaban:

49

Anda benar, itu karena Anda tidak mengembang secara manual. Tampaknya Anda mencoba "mengekstrak" id "body" dari tata letak Aktivitas Anda, dan itu tidak akan berhasil.

Anda mungkin menginginkan sesuatu seperti ini:

LayoutInflater inflater = getLayoutInflater();
FrameLayout f1 = (FrameLayout)alert.findViewById(android.R.id.body);
f1.addView(inflater.inflate(R.layout.dialog_view, f1, false));
sinis
sumber
17
Menariknya, body tidak didefinisikan sebagai konstanta di android.R.id. Saya masih belum jelas tentang cara mengakses elemen 'body' dari AlertDialog yang dibuat. Saya masih ingin tahu cara melakukan ini, tetapi untuk saat ini saya hanya akan mencoba memekarkan tampilan dan menggunakan setView dalam pembangun.
Stormin986
2
Sebenarnya ini masih meninggalkan saya dengan pertanyaan (saya baru dalam meningkatkan pandangan). Menggunakan builder.setView(inflater.inflate(R.id.dialog, ROOT_VIEWGROUP[, ATTACH_TO_ROOT])), dokumen mengatakan grup tampilan root adalah opsional. Haruskah ini digunakan dalam kasus ini? Jika demikian ... masih harus mencari cara untuk mendapatkan referensi ke AlertDialog ...
stormin986
2
Ini opsional, tetapi Anda tidak akan memiliki referensi ke induk dari dalam tata letak yang Anda kembangkan. Hal-hal seperti android: layout_gravity tidak akan berfungsi pada tampilan tingkat atas ... dan mungkin Anda tidak membutuhkannya. Saat Anda memanggil AlertDialog alert = builder.create (), Anda memiliki referensi ke AlertDialog Anda. Jawaban panjang pendek, ini opsional. Cobalah, tergantung pada apa yang Anda lakukan dalam tata letak khusus Anda, itu mungkin akan berhasil.
sinis
2
Saya tidak jelas tentang cara mereferensikan tampilan dalam AlertDialog. Apa yang akan Anda rekomendasikan untuk dilakukan dalam kasus ini jika saya memang ingin merujuk orang tua? Satu-satunya hal yang saya lihat dalam alertDialog yang mengembalikan tampilan adalah getCurrentFocus ()
stormin986
5
Pegang ViewAnda mengembang. Panggil findViewById()itu Viewketika Anda membutuhkan barang dari isinya. Lihat: github.com/commonsguy/cw-android/tree/master/Database/Constants
CommonsWare
159

Anda dapat membuat tampilan langsung dari Layout Inflater, Anda hanya perlu menggunakan nama file XML layout dan ID layout dalam file.

File XML Anda harus memiliki ID seperti ini:

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

Dan kemudian Anda dapat menyetel tata letak Anda pada pembuat dengan kode berikut:

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
sumber
4
Dan di mana R.id.dialog_layout_root dalam contoh ini? Bukankah itu tampilan dalam Aktivitas saat ini?
Alex Pretzlav
5
@AlexPretzlav: dialog_layout_root tidak diperlukan dalam contoh ini. Yang Anda butuhkan hanyalah nama file xml Anda untuk R.layout. [Name_of_xml_file].
IgorGanapolsky
7
@Temperage Apakah Anda menambahkan builder.show di akhir. Saya mencoba kode ini dan berhasil. Juga saya mengirimkan null sebagai parameter kedua untuk infalter.inflate
Vinoth
2
Ini seharusnya dipilih sebagai Jawaban Terbaik.
Salman Khakwani
2
Ini memberikan ClassCastException saat tata letak kustom berisi EditText, karena getCurrentFocus()akan mengembalikan EditText dan EditText tidak dapat dilemparkan ke ViewGroup. Menggunakan nullsebagai argumen kedua memperbaiki ini.
1 ''
20

android.R.id.custom mengembalikan null untuk saya. Saya berhasil membuat ini berfungsi jika ada yang menemukan masalah yang sama,

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

Sebagai referensi, R.layout.simple_password adalah:

<?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
sumber
John Rellis. Solusi Anda berfungsi dengan baik. Ada sesuatu di dalamnya. Kita harus memekarkan sebelum builder.create dan menyetel frameView.setLayoutParams (baru LayoutParams (LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); atau sesuatu yang pertama, yang akan mencegah beberapa bug yang tidak diharapkan
MOBYTELAB
terima kasih atas tanggapannya..bagaimana Anda mengembang sebelum builder.create ()? inflate dipanggil pada inflater dialog dan saya hanya memiliki dialog karena saya telah memanggil builder.create ()
John Rellis
kita bisa menggunakan activity inflater, dan tidak melampirkan ke FrameLayout, jadi kita bisa mengurangi sedikit kode seperti ini.AlertDialog.Builder builder = new AlertDialog.Builder (new ContextThemeWrapper (getActivity (), android.R.style.Theme_Holo)) .setTitle ("judul") .setIcon (R.drawable.sample_icon); Lihat masalahView = inflater.inflate (R.layout.sample_layout, null, false); builder.setView (troubleView); alert = builder.create (); Maaf, saya tidak tahu cara menulis kode dengan jelas di komentar
MOBYTELAB
Ini hanya tentang bug desain, jika kita mengumpulkan semua di layout xml dan melupakan Framelayout sebagai root dialog, kita bisa penasaran apakah layout di xml tidak mengisi orang tua atau semacamnya, hanya kasus.
MOBYTELAB
Bekerja untuk saya, terima kasih! Yang ini memungkinkan saya untuk menambahkan judul dan tombol Batal dan Oke saya, termasuk EditText tanpa kesalahan. :) Satu-satunya pertanyaan adalah, bagaimana cara kerjanya? Apakah FrameLayoutbertindak sebagai semacam fragmen? Ketika saya mencoba jawaban Andrewx2 di atas, saya mendapat kesalahan karena mengira saya menggembungkan dua tata letak (tebakan saya).
Azurespot
12

Ini berhasil untuk saya:

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

AlertDialog Kustom

Contoh lengkap ini termasuk meneruskan data kembali ke Aktivitas.

masukkan deskripsi gambar di sini

Buat tata letak khusus

Tata letak dengan an EditTextdigunakan untuk contoh sederhana ini, tetapi Anda dapat menggantinya dengan apa pun yang Anda suka.

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>

Gunakan dialog dalam kode

Bagian kuncinya adalah

  • gunakan setViewuntuk menetapkan tata letak khusus keAlertDialog.Builder
  • mengirim data apa pun kembali ke aktivitas saat tombol dialog diklik.

Ini adalah kode lengkap dari proyek contoh yang ditunjukkan pada gambar di atas:

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

Catatan

  • Jika Anda menemukan diri Anda menggunakan ini di banyak tempat, pertimbangkan untuk membuat DialogFragmentsubclass seperti yang dijelaskan dalam dokumentasi .

Lihat juga

Suragch
sumber
1
EditText editText = customLayout.findViewById(R.id.editText); harus EditText editText = (EditText) customLayout.findViewById(R.id.editText);
philcruz
4
@philcruz, Jika Anda mengupgrade ke Android Studio 3.0, Anda tidak lagi harus mentransmisikan tampilan secara eksplisit. IDE dapat menyimpulkan jenisnya. Ini adalah fitur yang sangat bagus. Ini sangat membantu untuk membersihkan kode.
Suragch
jawaban yang bagus, sangat membantu
Noor Hossain
4

Baris kode paling sederhana yang berfungsi untuk saya adalah sebagai berikut:

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

Apa pun jenis tata letaknya (LinearLayout, FrameLayout, RelativeLayout) akan berfungsi setView dan hanya akan berbeda dalam tampilan dan perilakunya.

kenix
sumber
mengagumkan, sederhana dan mudah
Frank Eno
2

Cara termudah untuk melakukannya adalah dengan menggunakan android.support.v7.app.AlertDialogbukannya android.app.AlertDialogmana public AlertDialog.Builder setView (int layoutResId)dapat digunakan di bawah 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
sumber
Siapa saja yang membaca ini setelah API 21 diperkenalkan, harus menggunakan metode ini. Seperti yang disebutkan dalam jawaban, jika aplikasi Anda minSDKversion kurang dari 21, gunakan AlerDialog dari paket dukungan. Voila !!!
Dibzmania
2

AlertDialog.setView(View view)menambahkan tampilan yang diberikan ke R.id.custom FrameLayout. Berikut ini adalah cuplikan kode sumber Android AlertController.setupView()yang akhirnya menangani ini ( mViewadalah tampilan yang diberikan ke AlertDialog.setViewmetode).

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

Setelah mengubah ID menjadi android.R.id.custom, saya perlu menambahkan yang berikut ini untuk menampilkan View:

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

Namun, hal ini menyebabkan Tampilan baru dirender dalam tampilan induk besar tanpa latar belakang, memutus kotak dialog menjadi dua bagian (teks dan tombol, dengan Tampilan baru di antaranya). Saya akhirnya mendapatkan efek yang saya inginkan dengan menyisipkan View saya di sebelah pesan:

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

Saya menemukan solusi ini dengan menjelajahi pohon View dengan View.getParent () dan View.getChildAt (int). Tapi tidak terlalu senang. Semua ini tidak ada di dokumen Android dan jika mereka pernah mengubah struktur AlertDialog, ini mungkin rusak.

Mantha Hitam
sumber
1

Akan sangat masuk akal untuk melakukannya dengan cara ini, jumlah kode yang paling sedikit.

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

Untuk daftar tambahan hal-hal yang bisa Anda setel, mulailah mengetik .setdi Android Studio

StackAttack
sumber