Переглянути джерело

Completely retired AlertDialogWrapper. Moved completely away from ListView support. Other fixes. Resolves #1136.

Aidan Follestad 8 роки тому
батько
коміт
33bf2717c2

+ 17 - 19
README.md

@@ -466,35 +466,33 @@ You can also pass a literal integer array (`int[]`) in place of an array resourc
 # Custom List Dialogs
 
 Like Android's native dialogs, you can also pass in your own adapter via `.adapter()` to customize
-exactly how you want your list to work.
+exactly how you want your list to work. **Note that Material Dialogs only
 
 ```java
 new MaterialDialog.Builder(this)
         .title(R.string.socialNetworks)
-        .adapter(new ButtonItemAdapter(this, R.array.socialNetworks),
-                new MaterialDialog.ListCallback() {
-                    @Override
-                    public void onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text) {
-                        Toast.makeText(MainActivity.this, "Clicked item " + which, Toast.LENGTH_SHORT).show();
-                    }
-                })
+        .adapter(new ButtonItemAdapter(this, R.array.socialNetworks), null)
         .show();
 ```
 
-If you need access to the `ListView`, you can use the `MaterialDialog` instance:
+**Note** that with newer releases, Material Dialogs no longer supports `ListView` and `ListAdapter`.
+It's about time that everyone uses `RecyclerView`. *Your custom adapters will have to handle item click
+events on their own; this library's classes have some good examples of how that is done correctly.*
+
+If you need access to the `RecyclerView`, you can use the `MaterialDialog` instance:
 
 ```java
 MaterialDialog dialog = new MaterialDialog.Builder(this)
         ...
         .build();
 
-ListView list = dialog.getListView();
+RecyclerView list = dialog.getRecyclerView();
 // Do something with it
 
 dialog.show();
 ```
 
-Note that you don't need to be using a custom adapter in order to access the `ListView`, it's there for single/multi choice dialogs, regular list dialogs, etc.
+Note that you don't need to be using a custom adapter in order to access the `RecyclerView`, it's there for single/multi choice dialogs, regular list dialogs, etc.
 
 ---
 
@@ -1276,7 +1274,13 @@ Simple List Dialogs are a specific style of list dialogs taken from the Material
 This library's implementation is just a pre-made adapter that you can pass to the `MaterialDialog.Builder`.
 
 ```java
-final MaterialSimpleListAdapter adapter = new MaterialSimpleListAdapter(this);
+final MaterialSimpleListAdapter adapter = new MaterialSimpleListAdapter(new MaterialSimpleListAdapter.Callback() {
+    @Override
+    public void onMaterialListItemSelected(int index, MaterialSimpleListItem item) {
+        // TODO
+    }
+});
+
 adapter.add(new MaterialSimpleListItem.Builder(this)
     .content("username@gmail.com")
     .icon(R.drawable.ic_account_circle)
@@ -1295,13 +1299,7 @@ adapter.add(new MaterialSimpleListItem.Builder(this)
 
 new MaterialDialog.Builder(this)
     .title(R.string.set_backup)
-    .adapter(adapter, new MaterialDialog.ListCallback() {
-        @Override
-        public void onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text) {
-            MaterialSimpleListItem item = adapter.getItem(which);
-            // TODO
-        }
-    })
+    .adapter(adapter, null)
     .show();
 ```
 

+ 3 - 7
commons/src/main/java/com/afollestad/materialdialogs/prefs/MaterialListPreference.java

@@ -4,24 +4,20 @@ import android.annotation.TargetApi;
 import android.app.Dialog;
 import android.content.Context;
 import android.content.DialogInterface;
-import android.content.res.TypedArray;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.preference.ListPreference;
-import android.preference.PreferenceManager;
 import android.support.annotation.NonNull;
+import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
 import android.view.View;
-import android.widget.ListView;
 
 import com.afollestad.materialdialogs.DialogAction;
 import com.afollestad.materialdialogs.MaterialDialog;
-import com.afollestad.materialdialogs.commons.R;
 
 import java.lang.reflect.Field;
-import java.lang.reflect.Method;
 
 /**
  * @author Marc Holder Kluver (marchold), Aidan Follestad (afollestad)
@@ -72,9 +68,9 @@ public class MaterialListPreference extends ListPreference {
         return mDialog;
     }
 
-    public ListView getListView() {
+    public RecyclerView getRecyclerView() {
         if (getDialog() == null) return null;
-        return ((MaterialDialog) getDialog()).getListView();
+        return ((MaterialDialog) getDialog()).getRecyclerView();
     }
 
     @Override

+ 67 - 32
commons/src/main/java/com/afollestad/materialdialogs/simplelist/MaterialSimpleListAdapter.java

@@ -1,10 +1,10 @@
 package com.afollestad.materialdialogs.simplelist;
 
-import android.content.Context;
 import android.graphics.PorterDuff;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -12,63 +12,98 @@ import com.afollestad.materialdialogs.MaterialDialog;
 import com.afollestad.materialdialogs.commons.R;
 import com.afollestad.materialdialogs.internal.MDAdapter;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * See the sample project to understand how this is used. Mimics the Simple List dialog style
  * displayed on Google's guidelines site: https://www.google.com/design/spec/components/dialogs.html#dialogs-simple-dialogs
  *
  * @author Aidan Follestad (afollestad)
  */
-public class MaterialSimpleListAdapter extends ArrayAdapter<MaterialSimpleListItem> implements MDAdapter {
+public class MaterialSimpleListAdapter extends RecyclerView.Adapter<MaterialSimpleListAdapter.SimpleListVH> implements MDAdapter {
+
+    public interface Callback {
+        void onMaterialListItemSelected(int index, MaterialSimpleListItem item);
+    }
 
     private MaterialDialog dialog;
+    private List<MaterialSimpleListItem> mItems;
+    private Callback mCallback;
 
-    public MaterialSimpleListAdapter(Context context) {
-        super(context, R.layout.md_simplelist_item, android.R.id.title);
+    public MaterialSimpleListAdapter(Callback callback) {
+        mItems = new ArrayList<>(4);
+        mCallback = callback;
     }
 
-    @Override
-    public void setDialog(MaterialDialog dialog) {
-        this.dialog = dialog;
+    public void add(MaterialSimpleListItem item) {
+        mItems.add(item);
+        notifyItemInserted(mItems.size() - 1);
+    }
+
+    public void clear() {
+        mItems.clear();
+        notifyDataSetChanged();
+    }
+
+    public MaterialSimpleListItem getItem(int index) {
+        return mItems.get(index);
     }
 
     @Override
-    public boolean hasStableIds() {
-        return true;
+    public void setDialog(MaterialDialog dialog) {
+        this.dialog = dialog;
     }
 
     @Override
-    public long getItemId(int position) {
-        return position;
+    public SimpleListVH onCreateViewHolder(ViewGroup parent, int viewType) {
+        final View view = LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.md_simplelist_item, parent, false);
+        return new SimpleListVH(view, this);
     }
 
     @Override
-    public View getView(final int index, View convertView, ViewGroup parent) {
-        final View view = super.getView(index, convertView, parent);
+    public void onBindViewHolder(SimpleListVH holder, int position) {
         if (dialog != null) {
-            final MaterialSimpleListItem item = getItem(index);
-            ImageView ic = (ImageView) view.findViewById(android.R.id.icon);
+            final MaterialSimpleListItem item = mItems.get(position);
             if (item.getIcon() != null) {
-                ic.setImageDrawable(item.getIcon());
-                ic.setPadding(item.getIconPadding(), item.getIconPadding(),
+                holder.icon.setImageDrawable(item.getIcon());
+                holder.icon.setPadding(item.getIconPadding(), item.getIconPadding(),
                         item.getIconPadding(), item.getIconPadding());
-                ic.getBackground().setColorFilter(item.getBackgroundColor(),
+                holder.icon.getBackground().setColorFilter(item.getBackgroundColor(),
                         PorterDuff.Mode.SRC_ATOP);
             } else {
-                ic.setVisibility(View.GONE);
+                holder.icon.setVisibility(View.GONE);
             }
-            TextView tv = (TextView) view.findViewById(android.R.id.title);
-            tv.setTextColor(dialog.getBuilder().getItemColor());
-            tv.setText(item.getContent());
-            dialog.setTypeface(tv, dialog.getBuilder().getRegularFont());
+            holder.title.setTextColor(dialog.getBuilder().getItemColor());
+            holder.title.setText(item.getContent());
+            dialog.setTypeface(holder.title, dialog.getBuilder().getRegularFont());
         }
-        return view;
     }
 
-//    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
-//    private boolean isRTL() {
-//        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
-//            return false;
-//        Configuration config = getContext().getResources().getConfiguration();
-//        return config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-//    }
+    @Override
+    public int getItemCount() {
+        return mItems.size();
+    }
+
+    public static class SimpleListVH extends RecyclerView.ViewHolder implements View.OnClickListener {
+
+        final ImageView icon;
+        final TextView title;
+        final MaterialSimpleListAdapter adapter;
+
+        public SimpleListVH(View itemView, MaterialSimpleListAdapter adapter) {
+            super(itemView);
+            icon = (ImageView) itemView.findViewById(android.R.id.icon);
+            title = (TextView) itemView.findViewById(android.R.id.title);
+            this.adapter = adapter;
+            itemView.setOnClickListener(this);
+        }
+
+        @Override
+        public void onClick(View view) {
+            if (adapter.mCallback != null)
+                adapter.mCallback.onMaterialListItemSelected(getAdapterPosition(), adapter.getItem(getAdapterPosition()));
+        }
+    }
 }

+ 0 - 354
core/src/main/java/com/afollestad/materialdialogs/AlertDialogWrapper.java

@@ -1,354 +0,0 @@
-package com.afollestad.materialdialogs;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.ArrayRes;
-import android.support.annotation.AttrRes;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.StringRes;
-import android.support.annotation.UiThread;
-import android.view.View;
-import android.widget.ListAdapter;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Convenience class for migrating old dialogs code. Not all methods are implemented yet. Using MaterialDialog.Builder directly is recommended.
- */
-public class AlertDialogWrapper {
-
-    public static class Builder {
-
-        private final MaterialDialog.Builder builder;
-
-        private DialogInterface.OnClickListener negativeDialogListener;
-        private DialogInterface.OnClickListener positiveDialogListener;
-        private DialogInterface.OnClickListener neutralDialogListener;
-        private DialogInterface.OnClickListener onClickListener;
-
-        public Builder(@NonNull Context context) {
-            builder = new MaterialDialog.Builder(context);
-        }
-
-        public Builder autoDismiss(boolean dismiss) {
-            builder.autoDismiss(dismiss);
-            return this;
-        }
-
-        public Builder setMessage(@StringRes int messageId) {
-            builder.content(messageId);
-            return this;
-        }
-
-        public Builder setMessage(@NonNull CharSequence message) {
-            builder.content(message);
-            return this;
-        }
-
-        public Builder setTitle(@StringRes int titleId) {
-            builder.title(titleId);
-            return this;
-        }
-
-        public Builder setTitle(@NonNull CharSequence title) {
-            builder.title(title);
-            return this;
-        }
-
-        public Builder setIcon(@DrawableRes int iconId) {
-            builder.iconRes(iconId);
-            return this;
-        }
-
-        public Builder setIcon(Drawable icon) {
-            builder.icon(icon);
-            return this;
-        }
-
-        public Builder setIconAttribute(@AttrRes int attrId) {
-            builder.iconAttr(attrId);
-            return this;
-        }
-
-        public Builder setNegativeButton(@StringRes int textId,
-                                         DialogInterface.OnClickListener listener) {
-            builder.negativeText(textId);
-            negativeDialogListener = listener;
-            return this;
-        }
-
-        public Builder setNegativeButton(@NonNull CharSequence text,
-                                         DialogInterface.OnClickListener listener) {
-            builder.negativeText(text);
-            negativeDialogListener = listener;
-            return this;
-        }
-
-        public Builder setPositiveButton(@StringRes int textId,
-                                         DialogInterface.OnClickListener listener) {
-            builder.positiveText(textId);
-            positiveDialogListener = listener;
-            return this;
-        }
-
-        public Builder setPositiveButton(@NonNull CharSequence text,
-                                         DialogInterface.OnClickListener listener) {
-            builder.positiveText(text);
-            positiveDialogListener = listener;
-            return this;
-        }
-
-        public Builder setNeutralButton(@StringRes int textId,
-                                        DialogInterface.OnClickListener listener) {
-            builder.neutralText(textId);
-            neutralDialogListener = listener;
-            return this;
-        }
-
-        public Builder setNeutralButton(@NonNull CharSequence text,
-                                        DialogInterface.OnClickListener listener) {
-            builder.neutralText(text);
-            neutralDialogListener = listener;
-            return this;
-        }
-
-        public Builder setCancelable(boolean cancelable) {
-            builder.cancelable(cancelable);
-            return this;
-        }
-
-        public Builder setItems(@ArrayRes int itemsId, DialogInterface.OnClickListener listener) {
-            builder.items(itemsId);
-            onClickListener = listener;
-            return this;
-        }
-
-        public Builder setItems(CharSequence[] items, DialogInterface.OnClickListener listener) {
-            builder.items(items);
-            onClickListener = listener;
-            return this;
-        }
-
-        /**
-         * @param adapter The adapter to set.
-         * @return An instance of the Builder for chaining.
-         * @deprecated Use {@link #setAdapter(ListAdapter, DialogInterface.OnClickListener)} instead.
-         */
-        @Deprecated
-        public Builder setAdapter(ListAdapter adapter) {
-            return setAdapter(adapter, null);
-        }
-
-        /**
-         * @param adapter  The adapter to set.
-         * @param listener The listener called when list items are clicked.
-         * @return An instance of the Builder for chaining.
-         */
-        public Builder setAdapter(ListAdapter adapter, final DialogInterface.OnClickListener listener) {
-            builder.adapter = adapter;
-            builder.listCallbackCustom = new MaterialDialog.ListCallback() {
-                @Override
-                public void onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text) {
-                    listener.onClick(dialog, which);
-                }
-            };
-            return this;
-        }
-
-        @UiThread
-        public Dialog create() {
-            addButtonsCallback();
-            addListCallbacks();
-            return builder.build();
-        }
-
-        @UiThread
-        public Dialog show() {
-            Dialog dialog = create();
-            dialog.show();
-            return dialog;
-        }
-
-        private void addListCallbacks() {
-            if (onClickListener != null) {
-                builder.itemsCallback(new MaterialDialog.ListCallback() {
-                    @Override
-                    public void onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text) {
-                        onClickListener.onClick(dialog, which);
-                    }
-                });
-            }
-        }
-
-        private void addButtonsCallback() {
-            if (positiveDialogListener != null || negativeDialogListener != null) {
-                builder.callback(new MaterialDialog.ButtonCallback() {
-                    @Override
-                    public void onNeutral(MaterialDialog dialog) {
-                        if (neutralDialogListener != null) {
-                            neutralDialogListener.onClick(dialog, DialogInterface.BUTTON_NEUTRAL);
-                        }
-                    }
-
-                    @Override
-                    public void onPositive(MaterialDialog dialog) {
-                        if (positiveDialogListener != null) {
-                            positiveDialogListener.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
-                        }
-                    }
-
-                    @Override
-                    public void onNegative(MaterialDialog dialog) {
-                        if (negativeDialogListener != null) {
-                            negativeDialogListener.onClick(dialog, DialogInterface.BUTTON_NEGATIVE);
-                        }
-                    }
-                });
-            }
-        }
-
-        public Builder setView(@NonNull View view) {
-            builder.customView(view, false);
-            return this;
-        }
-
-        /**
-         * Set a list of items to be displayed in the dialog as the content, you will be notified of the selected item via the supplied listener.
-         *
-         * @param itemsId      A resource ID for the items (e.g. R.array.my_items)
-         * @param checkedItems specifies which items are checked. It should be null in which case no items are checked. If non null it must be exactly the same length as the array of items.
-         * @param listener     notified when an item on the list is clicked. The dialog will not be dismissed when an item is clicked. It will only be dismissed if clicked on a button, if no buttons are supplied it's up to the user to dismiss the dialog.		 * @return
-         * @return This
-         */
-        public Builder setMultiChoiceItems(@ArrayRes int itemsId, @Nullable final boolean[] checkedItems, final DialogInterface.OnMultiChoiceClickListener listener) {
-            builder.items(itemsId);
-            setUpMultiChoiceCallback(checkedItems, listener);
-            return this;
-        }
-
-        /**
-         * Set a list of items to be displayed in the dialog as the content, you will be notified of the selected item via the supplied listener.
-         *
-         * @param items        the text of the items to be displayed in the list.
-         * @param checkedItems specifies which items are checked. It should be null in which case no items are checked. If non null it must be exactly the same length as the array of items.
-         * @param listener     notified when an item on the list is clicked. The dialog will not be dismissed when an item is clicked. It will only be dismissed if clicked on a button, if no buttons are supplied it's up to the user to dismiss the dialog.		 * @return
-         * @return This
-         */
-        public Builder setMultiChoiceItems(@NonNull String[] items, @Nullable final boolean[] checkedItems, final DialogInterface.OnMultiChoiceClickListener listener) {
-            builder.items(items);
-            setUpMultiChoiceCallback(checkedItems, listener);
-            return this;
-        }
-
-        public Builder alwaysCallSingleChoiceCallback() {
-            builder.alwaysCallSingleChoiceCallback();
-            return this;
-        }
-
-        public Builder alwaysCallMultiChoiceCallback() {
-            builder.alwaysCallMultiChoiceCallback();
-            return this;
-        }
-
-        private void setUpMultiChoiceCallback(@Nullable final boolean[] checkedItems, final DialogInterface.OnMultiChoiceClickListener listener) {
-            Integer selectedIndicesArr[] = null;
-            /* Convert old style array of booleans-per-index to new list of indices */
-            if (checkedItems != null) {
-                ArrayList<Integer> selectedIndices = new ArrayList<>();
-                for (int i = 0; i < checkedItems.length; i++) {
-                    if (checkedItems[i]) {
-                        selectedIndices.add(i);
-                    }
-                }
-                selectedIndicesArr = selectedIndices.toArray(new Integer[selectedIndices.size()]);
-            }
-
-            builder.itemsCallbackMultiChoice(selectedIndicesArr, new MaterialDialog.ListCallbackMultiChoice() {
-                @Override
-                public boolean onSelection(MaterialDialog dialog, Integer[] which, CharSequence[] text) {
-                    /* which is a list of selected indices */
-                    List<Integer> whichList = Arrays.asList(which);
-                    if (checkedItems != null) {
-                        for (int i = 0; i < checkedItems.length; i++) {
-                            /* save old state */
-                            boolean oldChecked = checkedItems[i];
-                            /* Record new state */
-                            checkedItems[i] = whichList.contains(i);
-                            /* Fire the listener if it changed */
-                            if (oldChecked != checkedItems[i]) {
-                                listener.onClick(dialog, i, checkedItems[i]);
-                            }
-                        }
-                    }
-                    return true;
-                }
-            });
-        }
-
-        /**
-         * Set a list of items to be displayed in the dialog as the content, you will be notified of the selected item via the supplied listener.
-         *
-         * @param items       the items to be displayed.
-         * @param checkedItem specifies which item is checked. If -1 no items are checked.
-         * @param listener    notified when an item on the list is clicked. The dialog will not be dismissed when an item is clicked. It will only be dismissed if clicked on a button, if no buttons are supplied it's up to the user to dismiss the dialog.
-         * @return This
-         */
-        public Builder setSingleChoiceItems(@NonNull String[] items, int checkedItem, final DialogInterface.OnClickListener listener) {
-            builder.items(items);
-            builder.itemsCallbackSingleChoice(checkedItem, new MaterialDialog.ListCallbackSingleChoice() {
-                @Override
-                public boolean onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text) {
-                    listener.onClick(dialog, which);
-                    return true;
-                }
-            });
-            return this;
-        }
-
-        /**
-         * Set a list of items to be displayed in the dialog as the content, you will be notified of the selected item via the supplied listener.
-         *
-         * @param itemsId     the resource id of an array i.e. R.array.foo
-         * @param checkedItem specifies which item is checked. If -1 no items are checked.
-         * @param listener    notified when an item on the list is clicked. The dialog will not be dismissed when an item is clicked. It will only be dismissed if clicked on a button, if no buttons are supplied it's up to the user to dismiss the dialog.
-         * @return This
-         */
-        public Builder setSingleChoiceItems(@ArrayRes int itemsId, int checkedItem, final DialogInterface.OnClickListener listener) {
-            builder.items(itemsId);
-            builder.itemsCallbackSingleChoice(checkedItem, new MaterialDialog.ListCallbackSingleChoice() {
-                @Override
-                public boolean onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text) {
-                    listener.onClick(dialog, which);
-                    return true;
-                }
-            });
-            return this;
-        }
-
-        public Builder setOnCancelListener(@NonNull DialogInterface.OnCancelListener listener) {
-            builder.cancelListener(listener);
-            return this;
-        }
-
-        public Builder setOnDismissListener(@NonNull DialogInterface.OnDismissListener listener) {
-            builder.dismissListener(listener);
-            return this;
-        }
-
-        public Builder setOnShowListener(@NonNull DialogInterface.OnShowListener listener) {
-            builder.showListener(listener);
-            return this;
-        }
-
-        public Builder setOnKeyListener(@NonNull DialogInterface.OnKeyListener listener) {
-            builder.keyListener(listener);
-            return this;
-        }
-    }
-}

+ 54 - 32
core/src/main/java/com/afollestad/materialdialogs/DefaultAdapter.java → core/src/main/java/com/afollestad/materialdialogs/DefaultRvAdapter.java

@@ -5,11 +5,11 @@ import android.annotation.TargetApi;
 import android.content.res.Configuration;
 import android.os.Build;
 import android.support.annotation.LayoutRes;
+import android.support.v7.widget.RecyclerView;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.BaseAdapter;
 import android.widget.CheckBox;
 import android.widget.CompoundButton;
 import android.widget.LinearLayout;
@@ -19,52 +19,47 @@ import android.widget.TextView;
 import com.afollestad.materialdialogs.internal.MDTintHelper;
 import com.afollestad.materialdialogs.util.DialogUtils;
 
-class DefaultAdapter extends BaseAdapter {
+/**
+ * @author Aidan Follestad (afollestad)
+ */
+class DefaultRvAdapter extends RecyclerView.Adapter<DefaultRvAdapter.DefaultVH> {
+
+    public interface InternalListCallback {
+        void onItemSelected(MaterialDialog dialog, View itemView, int position, CharSequence text);
+    }
 
     private final MaterialDialog dialog;
     @LayoutRes
     private final int layout;
-
     private final GravityEnum itemGravity;
+    private InternalListCallback callback;
 
-    public DefaultAdapter(MaterialDialog dialog, @LayoutRes int layout) {
+    public DefaultRvAdapter(MaterialDialog dialog, @LayoutRes int layout) {
         this.dialog = dialog;
         this.layout = layout;
         this.itemGravity = dialog.mBuilder.itemsGravity;
     }
 
-    @Override
-    public boolean hasStableIds() {
-        return true;
-    }
-
-    @Override
-    public int getCount() {
-        return dialog.mBuilder.items != null ? dialog.mBuilder.items.length : 0;
-    }
-
-    @Override
-    public Object getItem(int position) {
-        return dialog.mBuilder.items[position];
+    public void setCallback(InternalListCallback callback) {
+        this.callback = callback;
     }
 
     @Override
-    public long getItemId(int position) {
-        return position;
+    public DefaultVH onCreateViewHolder(ViewGroup parent, int viewType) {
+        final View view = LayoutInflater.from(parent.getContext())
+                .inflate(layout, parent, false);
+        DialogUtils.setBackgroundCompat(view, dialog.getListSelector());
+        return new DefaultVH(view, this);
     }
 
-    @SuppressLint("WrongViewCast")
     @Override
-    public View getView(final int index, View view, ViewGroup parent) {
-        if (view == null)
-            view = LayoutInflater.from(dialog.getContext()).inflate(layout, parent, false);
+    public void onBindViewHolder(DefaultVH holder, int index) {
+        final View view = holder.itemView;
         boolean disabled = DialogUtils.isIn(index, dialog.mBuilder.disabledIndices);
-
-        TextView tv = (TextView) view.findViewById(R.id.md_title);
         switch (dialog.listType) {
             case SINGLE: {
                 @SuppressLint("CutPasteId")
-                RadioButton radio = (RadioButton) view.findViewById(R.id.md_control);
+                RadioButton radio = (RadioButton) holder.control;
                 boolean selected = dialog.mBuilder.selectedIndex == index;
                 MDTintHelper.setTint(radio, dialog.mBuilder.widgetColor);
                 radio.setChecked(selected);
@@ -73,7 +68,7 @@ class DefaultAdapter extends BaseAdapter {
             }
             case MULTI: {
                 @SuppressLint("CutPasteId")
-                CheckBox checkbox = (CheckBox) view.findViewById(R.id.md_control);
+                CheckBox checkbox = (CheckBox) holder.control;
                 boolean selected = dialog.selectedIndicesList.contains(index);
                 MDTintHelper.setTint(checkbox, dialog.mBuilder.widgetColor);
                 checkbox.setChecked(selected);
@@ -82,11 +77,10 @@ class DefaultAdapter extends BaseAdapter {
             }
         }
 
-        tv.setText(dialog.mBuilder.items[index]);
-        tv.setTextColor(dialog.mBuilder.itemColor);
-        dialog.setTypeface(tv, dialog.mBuilder.regularFont);
+        holder.title.setText(dialog.mBuilder.items[index]);
+        holder.title.setTextColor(dialog.mBuilder.itemColor);
+        dialog.setTypeface(holder.title, dialog.mBuilder.regularFont);
 
-        view.setTag(index + ":" + dialog.mBuilder.items[index]);
         setupGravity((ViewGroup) view);
 
         if (dialog.mBuilder.itemIds != null) {
@@ -105,8 +99,11 @@ class DefaultAdapter extends BaseAdapter {
                     group.getChildAt(1).setBackground(null);
             }
         }
+    }
 
-        return view;
+    @Override
+    public int getItemCount() {
+        return dialog.mBuilder.items != null ? dialog.mBuilder.items.length : 0;
     }
 
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
@@ -149,4 +146,29 @@ class DefaultAdapter extends BaseAdapter {
         Configuration config = dialog.getBuilder().getContext().getResources().getConfiguration();
         return config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
     }
+
+    public static class DefaultVH extends RecyclerView.ViewHolder implements View.OnClickListener {
+
+        final CompoundButton control;
+        final TextView title;
+        final DefaultRvAdapter adapter;
+
+        public DefaultVH(View itemView, DefaultRvAdapter adapter) {
+            super(itemView);
+            control = (CompoundButton) itemView.findViewById(R.id.md_control);
+            title = (TextView) itemView.findViewById(R.id.md_title);
+            this.adapter = adapter;
+            itemView.setOnClickListener(this);
+        }
+
+        @Override
+        public void onClick(View view) {
+            if (adapter.callback != null) {
+                CharSequence text = null;
+                if (adapter.dialog.mBuilder.items != null && getAdapterPosition() < adapter.dialog.mBuilder.items.length)
+                    text = adapter.dialog.mBuilder.items[getAdapterPosition()];
+                adapter.callback.onItemSelected(adapter.dialog, view, getAdapterPosition(), text);
+            }
+        }
+    }
 }

+ 4 - 8
core/src/main/java/com/afollestad/materialdialogs/DialogInit.java

@@ -8,6 +8,7 @@ import android.support.annotation.LayoutRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.StyleRes;
 import android.support.annotation.UiThread;
+import android.support.v7.widget.RecyclerView;
 import android.text.InputType;
 import android.text.method.LinkMovementMethod;
 import android.text.method.PasswordTransformationMethod;
@@ -16,7 +17,6 @@ import android.view.ViewGroup;
 import android.widget.EditText;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.ListView;
 import android.widget.ProgressBar;
 import android.widget.ScrollView;
 import android.widget.TextView;
@@ -128,7 +128,7 @@ class DialogInit {
         dialog.icon = (ImageView) dialog.view.findViewById(R.id.md_icon);
         dialog.titleFrame = dialog.view.findViewById(R.id.md_titleFrame);
         dialog.content = (TextView) dialog.view.findViewById(R.id.md_content);
-        dialog.listView = (ListView) dialog.view.findViewById(R.id.md_contentListView);
+        dialog.recyclerView = (RecyclerView) dialog.view.findViewById(R.id.md_contentRecyclerView);
 
         // Button views initially used by checkIfStackingNeeded()
         dialog.positiveButton = (MDButton) dialog.view.findViewById(R.id.md_buttonDefaultPositive);
@@ -268,11 +268,7 @@ class DialogInit {
         // Setup list dialog stuff
         if (builder.listCallbackMultiChoice != null)
             dialog.selectedIndicesList = new ArrayList<>();
-        if (dialog.listView != null && (builder.items != null && builder.items.length > 0 || builder.adapter != null)) {
-            dialog.listView.setSelector(dialog.getListSelector());
-
-            // No custom adapter specified, setup the list with a MaterialDialogAdapter.
-            // Which supports regular lists and single/multi choice dialogs.
+        if (dialog.recyclerView != null) {
             if (builder.adapter == null) {
                 // Determine list type
                 if (builder.listCallbackSingleChoice != null) {
@@ -286,7 +282,7 @@ class DialogInit {
                 } else {
                     dialog.listType = MaterialDialog.ListType.REGULAR;
                 }
-                builder.adapter = new DefaultAdapter(dialog,
+                builder.adapter = new DefaultRvAdapter(dialog,
                         MaterialDialog.ListType.getLayoutForType(dialog.listType));
             } else if (builder.adapter instanceof MDAdapter) {
                 // Notify simple list adapter of the dialog it belongs to

+ 67 - 60
core/src/main/java/com/afollestad/materialdialogs/MaterialDialog.java

@@ -22,6 +22,9 @@ import android.support.annotation.Nullable;
 import android.support.annotation.StringRes;
 import android.support.annotation.UiThread;
 import android.support.v4.content.res.ResourcesCompat;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
@@ -30,13 +33,11 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
-import android.widget.AdapterView;
 import android.widget.CheckBox;
 import android.widget.EditText;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.ListAdapter;
-import android.widget.ListView;
 import android.widget.ProgressBar;
 import android.widget.RadioButton;
 import android.widget.TextView;
@@ -61,10 +62,10 @@ import java.util.Locale;
  * @author Aidan Follestad (afollestad)
  */
 public class MaterialDialog extends DialogBase implements
-        View.OnClickListener, AdapterView.OnItemClickListener {
+        View.OnClickListener, DefaultRvAdapter.InternalListCallback {
 
     protected final Builder mBuilder;
-    protected ListView listView;
+    protected RecyclerView recyclerView;
     protected ImageView icon;
     protected TextView title;
     protected View titleFrame;
@@ -104,16 +105,17 @@ public class MaterialDialog extends DialogBase implements
     }
 
     protected final void checkIfListInitScroll() {
-        if (listView == null)
+        if (recyclerView == null)
             return;
-        listView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+        recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+            @SuppressWarnings("ConstantConditions")
             @Override
             public void onGlobalLayout() {
                 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                     //noinspection deprecation
-                    listView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
+                    recyclerView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                 } else {
-                    listView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                    recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                 }
 
                 if (listType == ListType.SINGLE || listType == ListType.MULTI) {
@@ -128,17 +130,30 @@ public class MaterialDialog extends DialogBase implements
                         Collections.sort(selectedIndicesList);
                         selectedIndex = selectedIndicesList.get(0);
                     }
-                    if (listView.getLastVisiblePosition() < selectedIndex) {
-                        final int totalVisible = listView.getLastVisiblePosition() - listView.getFirstVisiblePosition();
+
+                    int lastVisiblePosition;
+                    int firstVisiblePosition;
+                    if (mBuilder.layoutManager instanceof LinearLayoutManager) {
+                        lastVisiblePosition = ((LinearLayoutManager) mBuilder.layoutManager).findLastVisibleItemPosition();
+                        firstVisiblePosition = ((LinearLayoutManager) mBuilder.layoutManager).findFirstVisibleItemPosition();
+                    } else if (mBuilder.layoutManager instanceof GridLayoutManager) {
+                        lastVisiblePosition = ((GridLayoutManager) mBuilder.layoutManager).findLastVisibleItemPosition();
+                        firstVisiblePosition = ((GridLayoutManager) mBuilder.layoutManager).findFirstVisibleItemPosition();
+                    } else {
+                        throw new IllegalStateException("Unsupported layout manager type: " + mBuilder.layoutManager.getClass().getName());
+                    }
+
+                    if (lastVisiblePosition < selectedIndex) {
+                        final int totalVisible = lastVisiblePosition - firstVisiblePosition;
                         // Scroll so that the selected index appears in the middle (vertically) of the ListView
                         int scrollIndex = selectedIndex - (totalVisible / 2);
                         if (scrollIndex < 0) scrollIndex = 0;
                         final int fScrollIndex = scrollIndex;
-                        listView.post(new Runnable() {
+                        recyclerView.post(new Runnable() {
                             @Override
                             public void run() {
-                                listView.requestFocus();
-                                listView.setSelection(fScrollIndex);
+                                recyclerView.requestFocus();
+                                recyclerView.scrollToPosition(fScrollIndex);
                             }
                         });
                     }
@@ -148,33 +163,26 @@ public class MaterialDialog extends DialogBase implements
     }
 
     /**
-     * Sets the dialog ListView's adapter and it's item click listener.
+     * Sets the dialog RecyclerView's adapter/layout manager, and it's item click listener.
      */
     protected final void invalidateList() {
-        if (listView == null)
+        if (recyclerView == null)
             return;
         else if ((mBuilder.items == null || mBuilder.items.length == 0) && mBuilder.adapter == null)
             return;
-        // Set up list with adapter
-        listView.setAdapter(mBuilder.adapter);
-        if (listType != null || mBuilder.listCallbackCustom != null)
-            listView.setOnItemClickListener(this);
+        if (mBuilder.layoutManager == null)
+            mBuilder.layoutManager = new LinearLayoutManager(getContext());
+        recyclerView.setLayoutManager(mBuilder.layoutManager);
+        recyclerView.setAdapter(mBuilder.adapter);
+        if (listType != null) {
+            ((DefaultRvAdapter) mBuilder.adapter).setCallback(this);
+        }
     }
 
     @Override
-    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+    public void onItemSelected(MaterialDialog dialog, View view, int position, CharSequence text) {
         if (!view.isEnabled()) return;
-        if (mBuilder.listCallbackCustom != null) {
-            // Custom adapter
-            CharSequence text = null;
-            if (view instanceof TextView) {
-                text = ((TextView) view).getText();
-            } else {
-                final TextView tv = (TextView) view.findViewById(android.R.id.title);
-                if (tv != null) text = tv.getText();
-            }
-            mBuilder.listCallbackCustom.onSelection(this, view, position, text);
-        } else if (listType == null || listType == ListType.REGULAR) {
+        if (listType == null || listType == ListType.REGULAR) {
             // Default adapter, non choice mode
             if (mBuilder.autoDismiss) {
                 // If auto dismiss is enabled, dismiss the dialog when a list item is selected
@@ -215,7 +223,7 @@ public class MaterialDialog extends DialogBase implements
                 final RadioButton radio = (RadioButton) view.findViewById(R.id.md_control);
                 if (!radio.isEnabled()) return;
                 boolean allowSelection = true;
-                final DefaultAdapter adapter = (DefaultAdapter) mBuilder.adapter;
+                final int oldSelected = mBuilder.selectedIndex;
 
                 if (mBuilder.autoDismiss && mBuilder.positiveText == null) {
                     // If auto dismiss is enabled, and no action button is visible to approve the selection, dismiss the dialog
@@ -226,7 +234,6 @@ public class MaterialDialog extends DialogBase implements
                     mBuilder.selectedIndex = position;
                     sendSingleChoiceCallback(view);
                 } else if (mBuilder.alwaysCallSingleChoiceCallback) {
-                    int oldSelected = mBuilder.selectedIndex;
                     // Temporarily set the new index so the callback uses the right one
                     mBuilder.selectedIndex = position;
                     // Only allow the radio button to be checked if the callback returns true
@@ -238,7 +245,7 @@ public class MaterialDialog extends DialogBase implements
                 if (allowSelection) {
                     mBuilder.selectedIndex = position;
                     radio.setChecked(true);
-                    adapter.notifyDataSetChanged();
+                    mBuilder.adapter.notifyItemChanged(oldSelected);
                 }
             }
 
@@ -265,6 +272,10 @@ public class MaterialDialog extends DialogBase implements
         return DialogUtils.resolveDrawable(getContext(), R.attr.md_list_selector);
     }
 
+    public RecyclerView getRecyclerView() {
+        return recyclerView;
+    }
+
     /* package */ Drawable getButtonSelector(DialogAction which, boolean isStacked) {
         if (isStacked) {
             if (mBuilder.btnSelectorStacked != 0)
@@ -409,7 +420,6 @@ public class MaterialDialog extends DialogBase implements
         protected ListCallback listCallback;
         protected ListCallbackSingleChoice listCallbackSingleChoice;
         protected ListCallbackMultiChoice listCallbackMultiChoice;
-        protected ListCallback listCallbackCustom;
         protected boolean alwaysCallMultiChoiceCallback = false;
         protected boolean alwaysCallSingleChoiceCallback = false;
         protected Theme theme = Theme.LIGHT;
@@ -425,7 +435,8 @@ public class MaterialDialog extends DialogBase implements
         protected Drawable icon;
         protected boolean limitIconToDefaultSize;
         protected int maxIconSize = -1;
-        protected ListAdapter adapter;
+        protected RecyclerView.Adapter<?> adapter;
+        protected RecyclerView.LayoutManager layoutManager;
         protected OnDismissListener dismissListener;
         protected OnCancelListener cancelListener;
         protected OnKeyListener keyListener;
@@ -1206,17 +1217,20 @@ public class MaterialDialog extends DialogBase implements
         }
 
         /**
-         * Sets a custom {@link android.widget.ListAdapter} for the dialog's list
+         * Sets a custom {@link android.support.v7.widget.RecyclerView.Adapter} for the dialog's list
          *
-         * @param adapter  The adapter to set to the list.
-         * @param callback The callback invoked when an item in the list is selected.
+         * @param adapter       The adapter to set to the list.
+         * @param layoutManager The layout manager to use in the RecyclerView. Pass null to use the default linear manager.
          * @return This Builder object to allow for chaining of calls to set methods
          */
-        public Builder adapter(@NonNull ListAdapter adapter, @Nullable ListCallback callback) {
+        @SuppressWarnings("ConstantConditions")
+        public Builder adapter(@NonNull RecyclerView.Adapter<?> adapter, @Nullable RecyclerView.LayoutManager layoutManager) {
             if (this.customView != null)
                 throw new IllegalStateException("You cannot set adapter() when you're using a custom view.");
+            if (layoutManager != null && !(layoutManager instanceof LinearLayoutManager) && !(layoutManager instanceof GridLayoutManager))
+                throw new IllegalStateException("You can currently only use LinearLayoutManager and GridLayoutManager with this library.");
             this.adapter = adapter;
-            this.listCallbackCustom = callback;
+            this.layoutManager = layoutManager;
             return this;
         }
 
@@ -1415,11 +1429,6 @@ public class MaterialDialog extends DialogBase implements
         return view;
     }
 
-    @Nullable
-    public final ListView getListView() {
-        return listView;
-    }
-
     @Nullable
     public final EditText getInputEditText() {
         return input;
@@ -1586,12 +1595,10 @@ public class MaterialDialog extends DialogBase implements
         if (mBuilder.adapter == null)
             throw new IllegalStateException("This MaterialDialog instance does not yet have an adapter set to it. You cannot use setItems().");
         mBuilder.items = items;
-        if (mBuilder.adapter instanceof DefaultAdapter) {
-            mBuilder.adapter = new DefaultAdapter(this, ListType.getLayoutForType(listType));
-        } else {
+        if (!(mBuilder.adapter instanceof DefaultRvAdapter)) {
             throw new IllegalStateException("When using a custom adapter, setItems() cannot be used. Set items through the adapter instead.");
         }
-        listView.setAdapter(mBuilder.adapter);
+        mBuilder.adapter.notifyDataSetChanged();
     }
 
     public final int getCurrentProgress() {
@@ -1703,8 +1710,8 @@ public class MaterialDialog extends DialogBase implements
     @UiThread
     public void setSelectedIndex(int index) {
         mBuilder.selectedIndex = index;
-        if (mBuilder.adapter != null && mBuilder.adapter instanceof DefaultAdapter) {
-            ((DefaultAdapter) mBuilder.adapter).notifyDataSetChanged();
+        if (mBuilder.adapter != null && mBuilder.adapter instanceof DefaultRvAdapter) {
+            mBuilder.adapter.notifyDataSetChanged();
         } else {
             throw new IllegalStateException("You can only use setSelectedIndex() with the default adapter implementation.");
         }
@@ -1720,8 +1727,8 @@ public class MaterialDialog extends DialogBase implements
     @UiThread
     public void setSelectedIndices(@NonNull Integer[] indices) {
         selectedIndicesList = new ArrayList<>(Arrays.asList(indices));
-        if (mBuilder.adapter != null && mBuilder.adapter instanceof DefaultAdapter) {
-            ((DefaultAdapter) mBuilder.adapter).notifyDataSetChanged();
+        if (mBuilder.adapter != null && mBuilder.adapter instanceof DefaultRvAdapter) {
+            mBuilder.adapter.notifyDataSetChanged();
         } else {
             throw new IllegalStateException("You can only use setSelectedIndices() with the default adapter implementation.");
         }
@@ -1742,10 +1749,10 @@ public class MaterialDialog extends DialogBase implements
     public void clearSelectedIndices(boolean sendCallback) {
         if (listType == null || listType != ListType.MULTI)
             throw new IllegalStateException("You can only use clearSelectedIndices() with multi choice list dialogs.");
-        if (mBuilder.adapter != null && mBuilder.adapter instanceof DefaultAdapter) {
+        if (mBuilder.adapter != null && mBuilder.adapter instanceof DefaultRvAdapter) {
             if (selectedIndicesList != null)
                 selectedIndicesList.clear();
-            ((DefaultAdapter) mBuilder.adapter).notifyDataSetChanged();
+            mBuilder.adapter.notifyDataSetChanged();
             if (sendCallback && mBuilder.listCallbackMultiChoice != null)
                 sendMultichoiceCallback();
         } else {
@@ -1768,14 +1775,14 @@ public class MaterialDialog extends DialogBase implements
     public void selectAllIndicies(boolean sendCallback) {
         if (listType == null || listType != ListType.MULTI)
             throw new IllegalStateException("You can only use selectAllIndicies() with multi choice list dialogs.");
-        if (mBuilder.adapter != null && mBuilder.adapter instanceof DefaultAdapter) {
+        if (mBuilder.adapter != null && mBuilder.adapter instanceof DefaultRvAdapter) {
             if (selectedIndicesList == null)
                 selectedIndicesList = new ArrayList<>();
-            for (int i = 0; i < mBuilder.adapter.getCount(); i++) {
+            for (int i = 0; i < mBuilder.adapter.getItemCount(); i++) {
                 if (!selectedIndicesList.contains(i))
                     selectedIndicesList.add(i);
             }
-            ((DefaultAdapter) mBuilder.adapter).notifyDataSetChanged();
+            mBuilder.adapter.notifyDataSetChanged();
             if (sendCallback && mBuilder.listCallbackMultiChoice != null)
                 sendMultichoiceCallback();
         } else {
@@ -1868,7 +1875,7 @@ public class MaterialDialog extends DialogBase implements
      * A callback used for regular list dialogs.
      */
     public interface ListCallback {
-        void onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text);
+        void onSelection(MaterialDialog dialog, View itemView, int position, CharSequence text);
     }
 
     /**

+ 1 - 0
core/src/main/res/layout-ldrtl/md_listitem_multichoice.xml

@@ -2,6 +2,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:background="?selectableItemBackground"
     android:descendantFocusability="blocksDescendants"
     android:minHeight="@dimen/md_listitem_height"
     android:orientation="horizontal"

+ 1 - 0
core/src/main/res/layout-ldrtl/md_listitem_singlechoice.xml

@@ -2,6 +2,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:background="?selectableItemBackground"
     android:descendantFocusability="blocksDescendants"
     android:minHeight="@dimen/md_listitem_height"
     android:orientation="horizontal"

+ 3 - 4
core/src/main/res/layout/md_dialog_list.xml

@@ -36,13 +36,12 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content">
 
-            <ListView
-                android:id="@+id/md_contentListView"
+            <android.support.v7.widget.RecyclerView
+                android:id="@+id/md_contentRecyclerView"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:scrollbarStyle="outsideOverlay"
-                android:divider="@null"
-                android:dividerHeight="0dp"
+                android:scrollbars="vertical"
                 android:clipToPadding="false"
                 android:paddingTop="@dimen/md_content_padding_top"
                 android:paddingBottom="@dimen/md_content_padding_bottom" />

+ 1 - 0
core/src/main/res/layout/md_listitem.xml

@@ -2,6 +2,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:background="?selectableItemBackground"
     android:gravity="center_vertical|start"
     android:minHeight="@dimen/md_listitem_height"
     android:orientation="horizontal">

+ 1 - 0
core/src/main/res/layout/md_listitem_multichoice.xml

@@ -2,6 +2,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:background="?selectableItemBackground"
     android:descendantFocusability="blocksDescendants"
     android:minHeight="@dimen/md_listitem_height"
     android:orientation="horizontal"

+ 1 - 0
core/src/main/res/layout/md_listitem_singlechoice.xml

@@ -2,6 +2,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:background="?selectableItemBackground"
     android:descendantFocusability="blocksDescendants"
     android:minHeight="@dimen/md_listitem_height"
     android:orientation="horizontal"

+ 49 - 34
sample/src/main/java/com/afollestad/materialdialogssample/ButtonItemAdapter.java

@@ -3,68 +3,83 @@ package com.afollestad.materialdialogssample;
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.support.annotation.ArrayRes;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.BaseAdapter;
 import android.widget.Button;
 import android.widget.TextView;
-import android.widget.Toast;
 
 /**
  * Simple adapter example for custom items in the dialog
  */
-class ButtonItemAdapter extends BaseAdapter implements View.OnClickListener {
+class ButtonItemAdapter extends RecyclerView.Adapter<ButtonItemAdapter.ButtonVH> {
+
+    public interface Callback {
+        void onItemClicked(int index);
+
+        void onButtonClicked(int index);
+    }
 
-    private Toast mToast;
-    private final Context mContext;
     private final CharSequence[] mItems;
+    private Callback mCallback;
 
     public ButtonItemAdapter(Context context, @ArrayRes int arrayResId) {
-        this(context, context.getResources().getTextArray(arrayResId));
+        this(context.getResources().getTextArray(arrayResId));
     }
 
-    private ButtonItemAdapter(Context context, CharSequence[] items) {
-        this.mContext = context;
+    private ButtonItemAdapter(CharSequence[] items) {
         this.mItems = items;
     }
 
-    @Override
-    public int getCount() {
-        return mItems.length;
+    public void setCallback(Callback mCallback) {
+        this.mCallback = mCallback;
     }
 
     @Override
-    public CharSequence getItem(int position) {
-        return mItems[position];
+    public ButtonVH onCreateViewHolder(ViewGroup parent, int viewType) {
+        final View view = LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.dialog_customlistitem, parent, false);
+        return new ButtonVH(view, this);
     }
 
+    @SuppressLint("SetTextI18n")
     @Override
-    public long getItemId(int position) {
-        return position;
+    public void onBindViewHolder(ButtonVH holder, int position) {
+        holder.title.setText(mItems[position] + " (" + position + ")");
+        holder.button.setTag(position);
     }
 
     @Override
-    public boolean hasStableIds() {
-        return true;
+    public int getItemCount() {
+        return mItems.length;
     }
 
-    @SuppressLint("ViewHolder")
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-        if (convertView == null)
-            convertView = View.inflate(mContext, R.layout.dialog_customlistitem, null);
-        ((TextView) convertView.findViewById(R.id.md_title)).setText(mItems[position] + " (" + position + ")");
-        Button button = (Button) convertView.findViewById(R.id.md_button);
-        button.setTag(position);
-        button.setOnClickListener(this);
-        return convertView;
-    }
+    public static class ButtonVH extends RecyclerView.ViewHolder implements View.OnClickListener {
 
-    @Override
-    public void onClick(View v) {
-        Integer index = (Integer) v.getTag();
-        if (mToast != null) mToast.cancel();
-        mToast = Toast.makeText(mContext, "Clicked button " + index, Toast.LENGTH_SHORT);
-        mToast.show();
+        final TextView title;
+        final Button button;
+        final ButtonItemAdapter adapter;
+
+        public ButtonVH(View itemView, ButtonItemAdapter adapter) {
+            super(itemView);
+            title = (TextView) itemView.findViewById(R.id.md_title);
+            button = (Button) itemView.findViewById(R.id.md_button);
+
+            this.adapter = adapter;
+            itemView.setOnClickListener(this);
+            button.setOnClickListener(this);
+        }
+
+        @Override
+        public void onClick(View view) {
+            if (adapter.mCallback == null)
+                return;
+            if (view instanceof Button) {
+                adapter.mCallback.onButtonClicked(getAdapterPosition());
+            } else {
+                adapter.mCallback.onItemClicked(getAdapterPosition());
+            }
+        }
     }
 }

+ 22 - 15
sample/src/main/java/com/afollestad/materialdialogssample/MainActivity.java

@@ -23,6 +23,7 @@ import android.text.method.PasswordTransformationMethod;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
+import android.widget.Button;
 import android.widget.CheckBox;
 import android.widget.CompoundButton;
 import android.widget.EditText;
@@ -394,7 +395,12 @@ public class MainActivity extends AppCompatActivity implements
 
     @OnClick(R.id.simpleList)
     public void showSimpleList() {
-        final MaterialSimpleListAdapter adapter = new MaterialSimpleListAdapter(this);
+        final MaterialSimpleListAdapter adapter = new MaterialSimpleListAdapter(new MaterialSimpleListAdapter.Callback() {
+            @Override
+            public void onMaterialListItemSelected(int index, MaterialSimpleListItem item) {
+                showToast(item.getContent().toString());
+            }
+        });
         adapter.add(new MaterialSimpleListItem.Builder(this)
                 .content("username@gmail.com")
                 .icon(R.drawable.ic_account_circle)
@@ -413,27 +419,28 @@ public class MainActivity extends AppCompatActivity implements
 
         new MaterialDialog.Builder(this)
                 .title(R.string.set_backup)
-                .adapter(adapter, new MaterialDialog.ListCallback() {
-                    @Override
-                    public void onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text) {
-                        MaterialSimpleListItem item = adapter.getItem(which);
-                        showToast(item.getContent().toString());
-                    }
-                })
+                .adapter(adapter, null)
                 .show();
     }
 
     @OnClick(R.id.customListItems)
     public void showCustomList() {
+        final ButtonItemAdapter adapter = new ButtonItemAdapter(this, R.array.socialNetworks);
+        adapter.setCallback(new ButtonItemAdapter.Callback() {
+            @Override
+            public void onItemClicked(int index) {
+                showToast("Item clicked: " + index);
+            }
+
+            @Override
+            public void onButtonClicked(int index) {
+                showToast("Button clicked: " + index);
+            }
+        });
+
         new MaterialDialog.Builder(this)
                 .title(R.string.socialNetworks)
-                .adapter(new ButtonItemAdapter(this, R.array.socialNetworks),
-                        new MaterialDialog.ListCallback() {
-                            @Override
-                            public void onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text) {
-                                showToast("Clicked item " + which);
-                            }
-                        })
+                .adapter(adapter, null)
                 .show();
     }
 

+ 13 - 13
sample/src/main/res/layout/dialog_customlistitem.xml

@@ -1,33 +1,33 @@
-<RelativeLayout
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:descendantFocusability="blocksDescendants"
-    xmlns:android="http://schemas.android.com/apk/res/android">
+    android:layout_height="wrap_content"
+    android:background="?selectableItemBackground"
+    android:descendantFocusability="blocksDescendants">
 
     <TextView
         android:id="@+id/md_title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textSize="@dimen/md_listitem_textsize"
-        android:layout_marginLeft="@dimen/md_listitem_margin_left"
-        android:layout_marginStart="@dimen/md_listitem_margin_left"
         android:layout_alignParentLeft="true"
         android:layout_alignParentStart="true"
+        android:layout_centerVertical="true"
+        android:layout_marginLeft="@dimen/md_listitem_margin_left"
+        android:layout_marginStart="@dimen/md_listitem_margin_left"
         android:layout_toLeftOf="@+id/md_button"
         android:layout_toStartOf="@+id/md_button"
-        android:layout_centerVertical="true" />
+        android:textSize="@dimen/md_listitem_textsize" />
 
     <Button
         android:id="@+id/md_button"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginRight="@dimen/md_dialog_frame_margin"
-        android:layout_marginEnd="@dimen/md_dialog_frame_margin"
-        android:text="@string/custom_listitem_button"
-        android:layout_alignParentRight="true"
         android:layout_alignParentEnd="true"
+        android:layout_alignParentRight="true"
         android:layout_centerVertical="true"
+        android:layout_marginBottom="4dp"
+        android:layout_marginEnd="@dimen/md_dialog_frame_margin"
+        android:layout_marginRight="@dimen/md_dialog_frame_margin"
         android:layout_marginTop="4dp"
-        android:layout_marginBottom="4dp" />
+        android:text="@string/custom_listitem_button" />
 
 </RelativeLayout>