Create A Single-Choice Mode Dialog with Images

This is the first time I write article in English, please don't hesitate to correct me if I make a mistake.

Preface:
Android has already provided a method to show items in Dialog with Single-Choice mode, we can achieve that via setSingleChoiceItems, the example is shown below.
AlertDialog.Builder mDialog =
    new AlertDialog.Builder(context).
    setSingleChoiceItems(R.array.items, defaultItem, 
    new
DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which){
            ......
            ......
        }
    });

Below is the output of above sample code.

But, this method can only allow developer showing String in, how can I do to display String and Images together in a Dialog? And how to custom a listview with Single-Choice mode for a Dialog? To achieve this feature, I spent two weeks on finding answer, and tried many different kinds of ways, finally I got the answer, and I would like to share this method to all that have the same problems, that's why I write this post here.   

Reference:
[AbhiAndroid] Custom ArrayAdapter Tutorial
[StackOverFlow] Custom listview with radiobutton single choice 

Architecture:
Create a Single-Choice mode ListView in Dialog, you need to prepare some components as shown below. A custom ArrayAdapter which including the definition of each row, a Custom View including your own ListView, then use this custom View in a Dialog.

Step 1:
Write a class named CheckedLinearLayout which inherit LinearLayout and implement Checkable, this class will be used in our custom XML file later.
// Provided by MichelRobico and Yasin Kacmaz
public class CheckedLinearLayout extends LinearLayout implements Checkable {
    private boolean isChecked;
    private List<Checkable> checkableViews;
    public CheckedLinearLayout (Context context, AttributeSet attrs,
int defStyle) {
        super(context, attrs, defStyle);
        initialise(attrs);
    }
    public CheckedLinearLayout (Context context, AttributeSet attrs) {
        super(context, attrs);
        initialise(attrs);
    }
    public CheckedLinearLayout (Context context, int checkableId) {
        super(context);
        initialise(null);
   }
    public boolean isChecked() {
        return isChecked;
    }
    public void setChecked(boolean isChecked) {
        this.isChecked = isChecked;
        for (Checkable c : checkableViews) {
            c.setChecked(isChecked);
        }
    }
    public void toggle() {
        this.isChecked = !this.isChecked;
        for (Checkable c : checkableViews) {
            c.toggle();
        }
    }
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        final int childCount = this.getChildCount();
        for (int i = 0; i < childCount; ++i) {
            findCheckableChildren(this.getChildAt(i));
        }
    }
    // Read the custom XML attributes
    private void initialise(AttributeSet attrs) {
        this.isChecked = false;
        this.checkableViews = new ArrayList<Checkable>(5);
    }
    /* Add to our checkable list all the children of the view that implement the interface Checkable */
    private void findCheckableChildren(View v) {
        if (v instanceof Checkable) {
            this.checkableViews.add((Checkable) v);
        }
        if (v instanceof ViewGroup) {
            final ViewGroup vg = (ViewGroup) v;
            final int childCount = vg.getChildCount();
            for (int i = 0; i < childCount; ++i) {
                findCheckableChildren(vg.getChildAt(i));
            }
        }
    }
}

Step 2:
Add a XML file name as list_item.xml which including the definition what you want to show in ListView, then use CheckedLinearLayout in this file instead of native LinearLayout.
<?xml version="1.0" encoding="utf-8"?>
<chen.kuanlin.library.CheckedLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"         
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <RadioButton
        android:id="@+id/radiobutton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:focusable="false"
        android:clickable="false"
        android:layout_weight="1" />

    <TextView
        android:id="@+id/textview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:textSize="30dp"
        android:layout_weight="1" />

    <ImageView
        android:id="@+id/imageview"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:padding="5dp"
        android:layout_weight="1" />
</chen.kuanlin.library.CheckedLinearLayout>


Step 3:
Add a class named SingleChoooiceAdapter which inherit ArrayAdapter, and set list_item.xml as View. This class is used to initialize each rows how to display in ListView, so you need to pass arguments from outside, then receive and set up these resources in constructor, while the instance of Adapter is being created.
public class SingleChoooiceAdapter extends ArrayAdapter{
    private Context context;
    private String[] textResource;
    private Integer[] imageResource;

    public SingleChoooiceAdapter(Context context, int resource, String[] textResource, Integer[] imageResource){
        super(context, resource, textResource);
        this.context = context;
        this.textResource = textResource;
        this.imageResource = imageResource;
    }
    @Override
    public int getCount(){
        return super.getCount();
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent){
        LayoutInflater inflater = LayoutInflater.from(context);
        View myView = inflater.inflate(R.layout.list_item, null);
        TextView textView = (TextView)myView.findViewById(R.id.textview);
        ImageView imageView = (ImageView)myView.findViewById(R.id.imageview);

        textView.setText(textResource[position]);
        imageView.setImageResource(imageResource[position]);

        return myView;
    }
}
 

Step 4:
Write a method to invoke above code.
public void showDialog(){
    //Create an instance of Adapter for Listview
    SingleChoooiceAdapter adapter = new SingleChoooiceAdapter(context, R.layout.list_item, textResource, imageResource);

    //Create an instance of ListView for AlertDialog
    View dialogView = activity.getLayoutInflater().inflate(R.layout.dialog_main, null);
    final ListView simpleListView = (ListView)dialogView.findViewById(R.id.simpleListView);
    simpleListView.setAdapter(adapter);
    simpleListView.setItemChecked(position,true);

    //Create an instance of AlertDialog
    AlertDialog.Builder dialog = new AlertDialog.Builder(context);
    dialog.setTitle(title);
    dialog.setView(dialogView);
    dialog.setPositiveButton("Done", null);
    dialog.show();
}
 

Step 5:
Now you can call showDialog method when you want to display some items for user to choice one of them. Below is the example, in which we display a list of colors with images in a list view using SingleChoooiceAdapter. Below is the final output.

GitHub:
I have already published this as an Android library, it can be used by adding the dependency into build.gradle. For more details please refer to [Github]SingleChoooiceListView.

留言

張貼留言

這個網誌中的熱門文章

程式語言常用之符號與詞彙 - 中英文對照

Repo 實用指令

什麼是 Bootloader?