Cách triển khai Chế độ xem AlertDialog tùy chỉnh


107

Trong tài liệu Android trên AlertDialog , nó cung cấp hướng dẫn và ví dụ sau để đặt chế độ xem tùy chỉnh trong AlertDialog:

Nếu bạn muốn hiển thị một chế độ xem phức tạp hơn, hãy tra cứu FrameLayout được gọi là "body" và thêm chế độ xem của bạn vào đó:

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

Trước hết, rõ ràng đó add()là lỗi đánh máy và có nghĩa là như vậy addView().

Tôi bối rối bởi dòng đầu tiên sử dụng R.id.body. Có vẻ như đó là phần tử nội dung của AlertDialog ... nhưng tôi không thể chỉ nhập nó vào mã b / c của mình, nó gây ra lỗi biên dịch. R.id.body được xác định hoặc chỉ định ở đâu hoặc bất cứ điều gì?

Đây là mã của tôi. Tôi đã cố gắng sử dụng setView(findViewById(R.layout.whatever)trên trình xây dựng nhưng nó không hoạt động. Tôi giả sử vì tôi đã không thổi phồng nó theo cách thủ công?

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

Để tìm và sử dụng các đối tượng của bạn trên Hộp thoại, hãy làm theo bốn bước sau: stackoverflow.com/a/18773261/1699586
Sara

3
Câu trả lời một dòng: thêm .setView(getLayoutInflater().inflate(R.layout.dialog_view, null))vào trình tạo. Tín dụng cho Sergio Viudes, bên dưới.
1 ''

Câu trả lời:


49

Bạn nói đúng, đó là do bạn không tự thổi phồng nó lên. Có vẻ như bạn đang cố gắng "trích xuất" id "body" khỏi bố cục Hoạt động của mình và điều đó sẽ không hoạt động.

Bạn có thể muốn một cái gì đó như thế này:

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

17
Điều thú vị là body không được định nghĩa là một hằng số trong android.R.id. Tôi vẫn chưa rõ về cách truy cập phần tử 'body' của AlertDialog đã tạo. Tôi vẫn muốn biết cách thực hiện việc này, nhưng bây giờ tôi sẽ chỉ cố gắng tăng cường một chế độ xem và sử dụng setView trong trình tạo.
Stormin986,

2
Thực sự thì điều này vẫn để lại cho tôi một câu hỏi sau đó (tôi là người mới muốn thổi phồng quan điểm). Sử dụng builder.setView(inflater.inflate(R.id.dialog, ROOT_VIEWGROUP[, ATTACH_TO_ROOT])), các tài liệu nói rằng nhóm xem gốc là tùy chọn. Có nên dùng cái này trong trường hợp này không? Nếu vậy ... vẫn phải tìm cách lấy tham chiếu đến AlertDialog ...
Stormin986,

2
Nó là tùy chọn, nhưng sau đó bạn sẽ không có tham chiếu đến cha mẹ từ bên trong bố cục mà bạn đang thổi phồng. Những thứ như android: layout_gravity sẽ không hoạt động trên chế độ xem cấp cao nhất ... và có thể bạn không cần chúng. Khi bạn gọi AlertDialog alert = builder.create (), bạn có một tham chiếu đến AlertDialog của mình. Câu trả lời dài ngắn, nó tùy chọn. Hãy thử, tùy thuộc vào những gì bạn đang làm trong bố cục tùy chỉnh của mình, nó có thể sẽ hoạt động.
synic

2
Tôi không rõ về cách tham chiếu chế độ xem trong AlertDialog. Bạn khuyên bạn nên làm gì trong trường hợp này nếu tôi muốn tham chiếu đến phụ huynh? Điều duy nhất tôi thấy trong alertDialog trả về chế độ xem là
getCurrentFocus

5
Giữ chặt Viewbạn thổi phồng. Gọi findViewById()về điều đó Viewkhi bạn cần công cụ từ nội dung của nó. Xem: github.com/commonsguy/cw-android/tree/master/Database/Constants
CommonsWare

159

Bạn có thể tạo chế độ xem của mình trực tiếp từ Layout Inflater, bạn chỉ cần sử dụng tên của tệp XML bố cục của mình và ID của bố cục trong tệp.

Tệp XML của bạn phải có một ID như sau:

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

Và sau đó, bạn có thể đặt bố cục của mình trên trình tạo bằng mã sau:

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
Và đâu là R.id.dialog_layout_root trong ví dụ này? Đó không phải là một chế độ xem trong Hoạt động hiện tại?
Alex Pretzlav

5
@AlexPretzlav: không cần hộp thoại_layout_root trong ví dụ này. Tất cả những gì bạn cần là tên tệp xml của bạn cho R.layout. [Name_of_xml_file].
IgorGanapolsky

7
@Temperage Bạn đã thêm builder.show vào cuối. Tôi đã thử mã này và nó hoạt động. Ngoài ra tôi qua null như tham số thứ hai cho infalter.inflate
Vinoth

2
Điều này lẽ ra phải được chọn là Câu trả lời hay nhất.
Salman Khakwani,

2
Điều này tạo ra một ClassCastException khi bố cục tùy chỉnh chứa một EditText, vì getCurrentFocus()sẽ trả về EditText và một EditText không thể được truyền vào một ViewGroup. Sử dụng nulllàm đối số thứ hai sẽ khắc phục điều này.
1 ''

20

android.R.id.custom đã trả về null cho tôi. Tôi đã quản lý để điều này hoạt động trong trường hợp có ai đó gặp phải vấn đề tương tự,

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

Để tham khảo, R.layout.simple_password là:

<?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. Giải pháp của bạn hoạt động tốt. Chỉ cần có một cái gì đó trong đó. Chúng ta nên tăng cường trước khi builder.create và đặt frameView.setLayoutParams (LayoutParams mới (LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); hay cái gì đầu tiên, điều đó sẽ ngăn chặn một số lỗi không mong đợi
MOBYTELAB

cảm ơn vì đã phản hồi..làm thế nào để bạn tăng giá trị trước khi builder.create ()? thổi phồng được kêu gọi inflater của hộp thoại và tôi chỉ có một hộp thoại bởi vì tôi đã gọi builder.create ()
John Rellis

chúng tôi có thể sử dụng trình tăng cường hoạt động và không đính kèm vào FrameLayout, vì vậy chúng tôi có thể giảm bớt một chút mã như thế này. ("title") .setIcon (R.drawable.sample_icon); Xem rắc rốiView = Inflater.inflate (R.layout.sample_layout, null, false); builder.setView (rắc rối); alert = builder.create (); Xin lỗi, tôi không biết làm thế nào để viết mã rõ ràng trên bình luận
MOBYTELAB

Đó chỉ là về lỗi thiết kế, nếu chúng ta tập hợp tất cả trong layout xml và quên Framelayout làm gốc của hộp thoại, chúng ta có thể tò mò nếu layout trong xml không phải là cha mẹ hay cái gì đó, chỉ là một trường hợp.
MOBYTELAB

Làm việc cho tôi, cảm ơn! Điều này cho phép tôi thêm tiêu đề và nút Hủy và Đồng ý, bao gồm cả EditText mà không có lỗi. :) Chỉ có câu hỏi là, cách này hoạt động như thế nào? Có FrameLayouthoạt động như một loại phân mảnh không? Khi tôi thử câu trả lời của Andrewx2 ở trên, tôi đã gặp lỗi vì nó cho rằng tôi đã thổi phồng hai bố cục (theo suy đoán của tôi).
Azurespot


12

Điều này đã làm việc cho tôi:

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

8

Cảnh báo tùy chỉnh

Ví dụ đầy đủ này bao gồm chuyển dữ liệu trở lại Hoạt động.

nhập mô tả hình ảnh ở đây

Tạo bố cục tùy chỉnh

Bố cục với một EditTextđược sử dụng cho ví dụ đơn giản này, nhưng bạn có thể thay thế nó bằng bất kỳ thứ gì bạn thích.

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>

Sử dụng hộp thoại trong mã

Các phần quan trọng là

  • sử dụng setViewđể gán bố cục tùy chỉnh choAlertDialog.Builder
  • gửi bất kỳ dữ liệu nào trở lại hoạt động khi một nút hộp thoại được nhấp vào.

Đây là mã đầy đủ từ dự án ví dụ được hiển thị trong hình trên:

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

Ghi chú

  • Nếu bạn thấy mình đang sử dụng nó ở nhiều nơi, thì hãy cân nhắc tạo một DialogFragmentlớp con như được mô tả trong tài liệu .

Xem thêm


1
EditText editText = customLayout.findViewById(R.id.editText); nên được EditText editText = (EditText) customLayout.findViewById(R.id.editText);
philcruz

4
@philcruz, Nếu nâng cấp lên Android Studio 3.0, bạn không còn phải truyền chế độ xem một cách rõ ràng nữa. IDE có thể suy ra loại. Đó là một tính năng rất hay. Nó giúp làm sạch mã rất nhiều.
Suragch

câu trả lời tuyệt vời, thực sự hữu ích
Noor Hossain

4

Các dòng mã đơn giản nhất phù hợp với tôi như sau:

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

Dù loại bố cục nào (LinearLayout, FrameLayout, RelativeLayout) sẽ hoạt động setView và sẽ chỉ khác về hình thức và hành vi.


tuyệt vời, đơn giản và dễ dàng
Frank Eno

2

Cách dễ nhất để làm điều này là sử dụng android.support.v7.app.AlertDialogthay vì android.app.AlertDialognơi public AlertDialog.Builder setView (int layoutResId)có thể được sử dụng bên dưới 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();

Bất kỳ ai đọc tài liệu này sau khi API 21 được giới thiệu, nên sử dụng phương pháp này. Như câu trả lời đã đề cập, nếu minSDKversion ứng dụng của bạn nhỏ hơn 21, hãy sử dụng AlerDialog từ gói hỗ trợ. Thì đấy !!!
Dibzmania

2

AlertDialog.setView(View view)không thêm chế độ xem đã cho vào R.id.custom FrameLayout. Sau đây là một đoạn mã nguồn Android AlertController.setupView()mà từ đó cuối cùng xử lý điều này ( mViewlà dạng xem được cấp cho AlertDialog.setViewphương thức).

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

1

Sau khi thay đổi ID thành android.R.id.custom, tôi cần thêm những thứ sau để Chế độ xem hiển thị:

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

Tuy nhiên, điều này khiến Chế độ xem mới hiển thị trong một chế độ xem mẹ lớn không có nền, chia hộp thoại thành hai phần (văn bản và các nút, với Chế độ xem mới ở giữa). Cuối cùng tôi đã có được hiệu ứng mà tôi muốn bằng cách chèn Chế độ xem của mình bên cạnh thông báo:

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

Tôi đã tìm thấy giải pháp này bằng cách khám phá cây View với View.getParent () và View.getChildAt (int). Tuy nhiên, không thực sự hài lòng về điều này. Không có điều này trong tài liệu Android và nếu họ thay đổi cấu trúc của AlertDialog, điều này có thể bị hỏng.


1

Sẽ là hợp lý nhất nếu làm theo cách này, với số lượng mã ít nhất.

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

Để có danh sách mở rộng những thứ bạn có thể đặt, hãy bắt đầu nhập .setvào Android Studio

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.