Browse Source

The ability to NOT wrap custom views in a ScrollView. Needs to be tested before 0.5.0 can be released.

Aidan Follestad 10 years ago
parent
commit
3313ee5995

+ 14 - 15
README.md

@@ -6,6 +6,12 @@ The code you see below is also found in the sample project. You can download a A
 
 ### What's New
 
+###### Version 0.5.0
+
+> 1. The ability to choose whether or not custom views are placed inside of a ScrollView (the second parameter of `customView()` in the `Builder`).
+> 2. Various padding improvements.
+> 3. Small bug fixes.
+
 ###### Version 0.4.8 – 0.4.9
 
 > 1. Improvements for padding in list dialogs.
@@ -14,29 +20,22 @@ The code you see below is also found in the sample project. You can download a A
 > 4. Pull request from [hzsweers](https://github.com/hzsweers): https://github.com/afollestad/material-dialogs/pull/146
 > 5. List items use pure black or white text depending on theme by default, rather than the former gray-ish color.
 
-###### Version 0.4.7
+###### Version 0.4.6 – 0.4.7
 
-> 1. Yet more fixes thanks to a pull request from [hzsweers](https://github.com/hzsweers), see the [pull request here](https://github.com/afollestad/material-dialogs/pull/124).
+> 1. Yet more fixes thanks to a pull request from [hzsweers](https://github.com/hzsweers).
 > 2. Note that the 3 variations of the action callbacks are deprecated and replaced with the single `ButtonCallback` interface.
 > 3. A fix for action button text styling on Lollipop, thanks [plusCubed](https://github.com/plusCubed)!
 > 4. Other fixes and improvements.
+> 5. The ability to force the action buttons to be stacked (see the [Misc.](#misc) section).
 
-###### Version 0.4.6
-
-> 1. Some fixes thanks to a pull request from [hzsweers](https://github.com/hzsweers), see the [pull request here](https://github.com/afollestad/material-dialogs/pull/124).
-> 2. The ability to force the action buttons to be stacked (see the [Misc.](#misc) section).
-
-###### Version 0.4.5
+###### Version 0.4.4 – 0.4.5
 
 > 1. Crash fix for Huawei devices
 > 2. Removed some unnecessary logging.
 > 3. New methods in `MaterialDialogCompat.Builder`
 > 4. Other crash fixes and improvements.
-
-###### Version 0.4.4
-
-> 1. Memory management improvements for Typefaces (thanks [Kevin Barry](https://github.com/teslacoil) of Nova Launcher!)
-> 2. Added `dismiss`, `cancel`, and `show` listener methods to the `Builder`.
+> 5. Memory management improvements for Typefaces (thanks [Kevin Barry](https://github.com/teslacoil) of Nova Launcher!)
+> 6. Added `dismiss`, `cancel`, and `show` listener methods to the `Builder`.
 
 ###### Version 0.4.1 – 0.4.3
 
@@ -51,7 +50,7 @@ The code you see below is also found in the sample project. You can download a A
 
 > 1. Bug fixes and improvements throughout
 > 2. Action button selectors have rounded corners
-> 3. More global theming capabilities. Override the accent color used for action buttons, titles, and content from your Activity theme. See the [Global Theming](#global-theming) section below.
+> 3. Global theming capabilities. Override the accent color used for action buttons, titles, and content from your Activity theme. See the [Global Theming](#global-theming) section below.
 
 ---
 
@@ -61,7 +60,7 @@ Easily reference the library in your Android projects using this dependency in y
 
 ```Groovy
 dependencies {
-    compile 'com.afollestad:material-dialogs:0.4.9'
+    compile 'com.afollestad:material-dialogs:0.5.0'
 }
 ```
 

+ 2 - 2
library/build.gradle

@@ -9,7 +9,7 @@ android {
         minSdkVersion 8
         targetSdkVersion 21
         versionCode 1
-        versionName "0.4.9"
+        versionName "0.5.0"
     }
     lintOptions {
         abortOnError false
@@ -26,7 +26,7 @@ publish {
     userOrg = 'drummer-aidan'
     groupId = 'com.afollestad'
     artifactId = 'material-dialogs'
-    version = '0.4.9'
+    version = '0.5.0'
     description = 'A library for implementing Material design styled dialogs across all versions of Android.'
     website = 'https://github.com/afollestad/material-dialogs'
     issueTracker = "${website}/issues"

+ 262 - 139
library/src/main/java/com/afollestad/materialdialogs/MaterialDialog.java

@@ -6,7 +6,6 @@ import android.content.DialogInterface;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.database.DataSetObserver;
 import android.graphics.Color;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
@@ -24,10 +23,13 @@ import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.webkit.WebView;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.CheckBox;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ListAdapter;
@@ -38,8 +40,6 @@ import android.widget.ScrollView;
 import android.widget.TextView;
 
 import com.afollestad.materialdialogs.base.DialogBase;
-import com.afollestad.materialdialogs.views.MeasureCallbackListView;
-import com.afollestad.materialdialogs.views.MeasureCallbackScrollView;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -48,22 +48,21 @@ import java.util.List;
 /**
  * @author Aidan Follestad (afollestad)
  */
-public class MaterialDialog extends DialogBase implements View.OnClickListener, MeasureCallbackScrollView.Callback, MeasureCallbackListView.Callback {
+public class MaterialDialog extends DialogBase implements View.OnClickListener {
 
     View view;
     ListView listView;
-
     ImageView icon;
     TextView title;
     View titleFrame;
     Builder mBuilder;
+    FrameLayout customViewFrame;
 
     Button positiveButton;
     Button neutralButton;
     Button negativeButton;
     boolean isStacked;
     final int defaultItemColor;
-    boolean mMeasuredScrollView;
     ListType listType;
     List<Integer> selectedIndicesList;
 
@@ -135,23 +134,50 @@ public class MaterialDialog extends DialogBase implements View.OnClickListener,
         }
 
         if (mBuilder.customView != null) {
-            title = (TextView) view.findViewById(R.id.titleCustomView);
-            icon = (ImageView) view.findViewById(R.id.iconCustomView);
-            titleFrame = view.findViewById(R.id.titleFrameCustomView);
             invalidateCustomViewAssociations();
-            ((LinearLayout) view.findViewById(R.id.customViewFrame)).addView(mBuilder.customView);
+            FrameLayout frame = (FrameLayout) view.findViewById(R.id.customViewFrame);
+            customViewFrame = frame;
+            View innerView = mBuilder.customView;
+
+            if (mBuilder.customViewScroll) {
+                /* Apply the frame padding to the content, this allows the ScrollView to draw it's
+                   overscroll glow without clipping */
+                Resources r = getContext().getResources();
+                int frameMargin = r.getDimensionPixelSize(R.dimen.md_dialog_frame_margin);
+                innerView.setPadding(frameMargin, 0, frameMargin, 0);
+
+                ScrollView sv = new ScrollView(getContext());
+                int paddingTop;
+                int paddingBottom;
+                if (titleFrame.getVisibility() != View.GONE)
+                    paddingTop = r.getDimensionPixelSize(R.dimen.md_content_vertical_padding);
+                else
+                    paddingTop = r.getDimensionPixelSize(R.dimen.md_dialog_frame_margin);
+
+                if (hasActionButtons())
+                    paddingBottom = r.getDimensionPixelSize(R.dimen.md_content_vertical_padding);
+                else
+                    paddingBottom = r.getDimensionPixelSize(R.dimen.md_dialog_frame_margin);
+
+                sv.setPadding(0, paddingTop, 0, paddingBottom);
+                sv.setClipToPadding(false);
+                sv.addView(innerView,
+                        new ScrollView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                                ViewGroup.LayoutParams.WRAP_CONTENT));
+
+                innerView = sv;
+            }
+
+            frame.addView(innerView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                    ViewGroup.LayoutParams.WRAP_CONTENT));
         } else {
             invalidateCustomViewAssociations();
         }
 
         boolean adapterProvided = mBuilder.adapter != null;
         if (mBuilder.items != null && mBuilder.items.length > 0 || adapterProvided) {
-            title = (TextView) view.findViewById(R.id.titleCustomView);
-            icon = (ImageView) view.findViewById(R.id.iconCustomView);
-            titleFrame = view.findViewById(R.id.titleFrameCustomView);
             listView = (ListView) view.findViewById(R.id.contentListView);
             listView.setSelector(DialogUtils.resolveDrawable(getContext(), R.attr.md_selector));
-            ((MeasureCallbackListView) listView).setCallback(this);
 
             if (!adapterProvided) {
                 // Determine list type
@@ -170,19 +196,6 @@ public class MaterialDialog extends DialogBase implements View.OnClickListener,
                 mBuilder.adapter = new MaterialDialogAdapter(mBuilder.context,
                         ListType.getLayoutForType(listType), R.id.title, mBuilder.items);
             }
-
-            mBuilder.adapter.registerDataSetObserver(new DataSetObserver() {
-                @Override
-                public void onChanged() {
-                    super.onChanged();
-                    listView.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            invalidateCustomViewAssociations();
-                        }
-                    });
-                }
-            });
         }
 
         if (builder.icon != null) {
@@ -198,11 +211,8 @@ public class MaterialDialog extends DialogBase implements View.OnClickListener,
             }
         }
 
-        // Title is set after it's determined whether to use first title or custom view title
         if (builder.title == null || builder.title.toString().trim().length() == 0) {
             titleFrame.setVisibility(View.GONE);
-            if (mBuilder.customView == null)
-                view.findViewById(R.id.titleFrameCustomView).setVisibility(View.GONE);
         } else {
             title.setText(builder.title);
             setTypeface(title, mBuilder.mediumFont);
@@ -215,7 +225,7 @@ public class MaterialDialog extends DialogBase implements View.OnClickListener,
             if (builder.titleAlignment == Alignment.CENTER) {
                 title.setGravity(Gravity.CENTER_HORIZONTAL);
             } else if (builder.titleAlignment == Alignment.END) {
-                title.setGravity(Gravity.END);
+                title.setGravity(Gravity.RIGHT);
             }
         }
 
@@ -229,10 +239,21 @@ public class MaterialDialog extends DialogBase implements View.OnClickListener,
             setOnDismissListener(builder.dismissListener);
         }
 
+        updateFramePadding();
         invalidateActions();
         setOnShowListenerInternal();
         setViewInternal(view);
 
+        view.getViewTreeObserver().addOnGlobalLayoutListener(
+                new ViewTreeObserver.OnGlobalLayoutListener() {
+                    @Override
+                    public void onGlobalLayout() {
+                        if (view.getMeasuredWidth() > 0) {
+                            invalidateCustomViewAssociations();
+                        }
+                    }
+                });
+
         if (builder.theme == Theme.LIGHT && Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
             setInverseBackgroundForced(true);
             title.setTextColor(Color.BLACK);
@@ -247,77 +268,94 @@ public class MaterialDialog extends DialogBase implements View.OnClickListener,
         invalidateCustomViewAssociations();
     }
 
+    /**
+     * To account for scrolling content and overscroll glows, the frame padding/margins sometimes
+     * must be set on inner views. This is dependent on the visibility of the title bar and action
+     * buttons. This method determines where the padding or margins are needed and applies them.
+     */
+    private void updateFramePadding() {
+        Resources r = getContext().getResources();
+        int frameMargin = r.getDimensionPixelSize(R.dimen.md_dialog_frame_margin);
+
+        View contentScrollView = view.findViewById(R.id.contentScrollView);
+        int paddingTop = contentScrollView.getPaddingTop();
+        int paddingBottom = contentScrollView.getPaddingBottom();
+
+        if (!hasActionButtons())
+            paddingBottom = frameMargin;
+        if (titleFrame.getVisibility() == View.GONE)
+            paddingTop = frameMargin;
+
+        contentScrollView.setPadding(contentScrollView.getPaddingLeft(), paddingTop,
+                contentScrollView.getPaddingRight(), paddingBottom);
+
+        if (listView != null) {
+            final int titleMarginBottom = (int) mBuilder.context.getResources().getDimension(R.dimen.md_title_frame_margin_bottom_list);
+            setVerticalMargins(titleFrame, -1, titleMarginBottom);
+
+//            final int dialogFramePadding = (int) mBuilder.context.getResources().getDimension(R.dimen.md_dialog_frame_margin);
+//            paddingTop = titleFrame.getVisibility() != View.GONE ? listView.getPaddingTop() :
+//                    dialogFramePadding;
+//            paddingBottom = hasActionButtons() ? listView.getPaddingBottom() :
+//                    dialogFramePadding;
+//            listView.setPadding(listView.getPaddingLeft(), paddingTop,
+//                    listView.getPaddingRight(), paddingBottom);
+        }
+    }
+
     /**
      * Invalidates visibility of views for the presence of a custom view or list content
      */
     private void invalidateCustomViewAssociations() {
-        if (mBuilder.customView != null || (mBuilder.items != null && mBuilder.items.length > 0) || mBuilder.adapter != null) {
-            view.findViewById(R.id.mainFrame).setVisibility(View.GONE);
-            view.findViewById(R.id.customViewScrollParent).setVisibility(View.VISIBLE);
-            if (!mMeasuredScrollView && listView == null) {
-                // Wait until it's measured
-                ((MeasureCallbackScrollView) view.findViewById(R.id.customViewScroll)).setCallback(this);
-                return;
-            }
-            if (canCustomViewScroll()) {
-                view.findViewById(R.id.customViewDivider).setVisibility(View.VISIBLE);
-                view.findViewById(R.id.customViewDivider).setBackgroundColor(DialogUtils.resolveColor(getContext(), R.attr.md_divider));
-                setMargin(view.findViewById(R.id.buttonStackedFrame), -1, 0, -1, -1);
-                setMargin(view.findViewById(R.id.buttonDefaultFrame), -1, 0, -1, -1);
-                if (mBuilder.items != null && mBuilder.items.length > 0) {
-                    View customFrame = view.findViewById(R.id.customViewFrame);
-                    Resources r = getContext().getResources();
-                    int bottomPadding = view.findViewById(R.id.titleCustomView).getVisibility() == View.VISIBLE ?
-                            (int) r.getDimension(R.dimen.md_main_frame_margin) : (int) r.getDimension(R.dimen.md_dialog_frame_margin);
-                    customFrame.setPadding(customFrame.getPaddingLeft(), customFrame.getPaddingTop(),
-                            customFrame.getPaddingRight(), bottomPadding);
-                }
-            } else {
-                view.findViewById(R.id.customViewDivider).setVisibility(View.GONE);
-                final int bottomMargin = (int) getContext().getResources().getDimension(R.dimen.md_button_padding_frame_bottom);
-                setMargin(view.findViewById(R.id.buttonStackedFrame), -1, bottomMargin, -1, -1);
-                setMargin(view.findViewById(R.id.buttonDefaultFrame), -1, bottomMargin, -1, -1);
-            }
+        if (view.getMeasuredWidth() == 0) {
+            return;
+        }
+        View contentScrollView = view.findViewById(R.id.contentScrollView);
+        if (mBuilder.customView != null) {
+            contentScrollView.setVisibility(View.GONE);
+            customViewFrame.setVisibility(View.VISIBLE);
+            boolean topScroll = canViewOrChildScroll(customViewFrame.getChildAt(0), false);
+            boolean bottomScroll = canViewOrChildScroll(customViewFrame.getChildAt(0), true);
+            setDividerVisibility(topScroll, bottomScroll);
+        } else if ((mBuilder.items != null && mBuilder.items.length > 0) || mBuilder.adapter != null) {
+            contentScrollView.setVisibility(View.GONE);
+            boolean canScroll = canListViewScroll();
+            setDividerVisibility(canScroll, canScroll);
         } else {
-            view.findViewById(R.id.mainFrame).setVisibility(View.VISIBLE);
-            view.findViewById(R.id.customViewScrollParent).setVisibility(View.GONE);
-            view.findViewById(R.id.customViewDivider).setVisibility(View.GONE);
-            if (!mMeasuredScrollView) {
-                // Wait until it's measured
-                ((MeasureCallbackScrollView) view.findViewById(R.id.contentScrollView)).setCallback(this);
-                return;
-            }
-            if (canContentScroll()) {
-                view.findViewById(R.id.customViewDivider).setVisibility(View.VISIBLE);
-                view.findViewById(R.id.customViewDivider).setBackgroundColor(DialogUtils.resolveColor(getContext(), R.attr.md_divider));
-                setMargin(view.findViewById(R.id.mainFrame), -1, 0, -1, -1);
-                setMargin(view.findViewById(R.id.buttonStackedFrame), -1, 0, -1, -1);
-                setMargin(view.findViewById(R.id.buttonDefaultFrame), -1, 0, -1, -1);
-                final int conPadding = (int) getContext().getResources().getDimension(R.dimen.md_main_frame_margin);
-                View con = view.findViewById(R.id.content);
-                con.setPadding(con.getPaddingLeft(), 0, con.getPaddingRight(), conPadding);
-            } else {
-                View con = view.findViewById(R.id.content);
-                con.setPadding(con.getPaddingLeft(), 0, con.getPaddingRight(), 0);
-            }
+            contentScrollView.setVisibility(View.VISIBLE);
+            boolean canScroll = canContentScroll();
+            setDividerVisibility(canScroll, canScroll);
         }
     }
 
     /**
-     * Invalidates the radio buttons in the single choice mode list so that only the radio button that
-     * was previous selected is checked.
+     * Set the visibility of the bottom divider and adjusts the layout margin,
+     * when the divider is visible the button bar bottom margin (8dp from
+     * http://www.google.com/design/spec/components/dialogs.html#dialogs-specs )
+     * is removed as it makes the button bar look off balanced with different amounts of padding
+     * above and below the divider.
      */
-    private void invalidateSingleChoice(int newSelection) {
-        newSelection++;
-        final LinearLayout list = (LinearLayout) view.findViewById(R.id.customViewFrame);
-        for (int i = 1; i < list.getChildCount(); i++) {
-            View v = list.getChildAt(i);
-            @SuppressLint("WrongViewCast")
-            RadioButton rb = (RadioButton) v.findViewById(R.id.control);
-            if (newSelection != i) {
-                rb.setChecked(false);
-                rb.clearFocus();
-            }
+    private void setDividerVisibility(boolean topVisible, boolean bottomVisible) {
+        View titleBarDivider = view.findViewById(R.id.titleBarDivider);
+        if (topVisible) {
+            titleBarDivider.setVisibility(View.VISIBLE);
+            titleBarDivider.setBackgroundColor(DialogUtils.resolveColor(getContext(), R.attr.md_divider));
+        } else {
+            titleBarDivider.setVisibility(View.GONE);
+        }
+
+        View buttonBarDivider = view.findViewById(R.id.buttonBarDivider);
+        if (bottomVisible) {
+            buttonBarDivider.setVisibility(View.VISIBLE);
+            buttonBarDivider.setBackgroundColor(DialogUtils.resolveColor(getContext(), R.attr.md_divider));
+            setVerticalMargins(view.findViewById(R.id.buttonStackedFrame), 0, 0);
+            setVerticalMargins(view.findViewById(R.id.buttonDefaultFrame), 0, 0);
+        } else {
+            Resources r = getContext().getResources();
+            buttonBarDivider.setVisibility(View.GONE);
+            final int bottomMargin = r.getDimensionPixelSize(R.dimen.md_button_frame_vertical_padding);
+            setVerticalMargins(view.findViewById(R.id.buttonStackedFrame), bottomMargin, bottomMargin);
+            setVerticalMargins(view.findViewById(R.id.buttonDefaultFrame), bottomMargin, bottomMargin);
         }
     }
 
@@ -330,13 +368,10 @@ public class MaterialDialog extends DialogBase implements View.OnClickListener,
 
         // Hide content
         view.findViewById(R.id.contentScrollView).setVisibility(View.GONE);
-
-        // Show custom frame container but hide the scrollview
-        view.findViewById(R.id.customViewScrollParent).setVisibility(View.VISIBLE);
-        view.findViewById(R.id.customViewScroll).setVisibility(View.GONE);
+        view.findViewById(R.id.customViewFrame).setVisibility(View.GONE);
 
         // Set up list with adapter
-        LinearLayout listViewContainer = (LinearLayout) view.findViewById(R.id.list_view_container);
+        FrameLayout listViewContainer = (FrameLayout) view.findViewById(R.id.contentListViewFrame);
         listViewContainer.setVisibility(View.VISIBLE);
         listView.setAdapter(mBuilder.adapter);
 
@@ -370,40 +405,91 @@ public class MaterialDialog extends DialogBase implements View.OnClickListener,
             });
         }
 
-        final int dialogFramePadding = (int) mBuilder.context.getResources().getDimension(R.dimen.md_dialog_frame_margin);
-        if (titleFrame.getVisibility() == View.VISIBLE || icon.getVisibility() == View.VISIBLE) {
-            int bottomPadding = (int) getContext().getResources().getDimension(R.dimen.md_title_margin_plainlist);
-            setMargin(titleFrame, dialogFramePadding, bottomPadding, dialogFramePadding, dialogFramePadding);
-            ((ViewGroup) titleFrame.getParent()).removeView(titleFrame);
-            listViewContainer.addView(titleFrame, 0);
+    }
+
+    /**
+     * Find the view touching the bottom of this ViewGroup. Non visible children are ignored,
+     * however getChildDrawingOrder is not taking into account for simplicity and because it behaves
+     * inconsistently across platform versions.
+     *
+     * @return View touching the bottom of this viewgroup or null
+     */
+    @Nullable
+    private static View getBottomView(ViewGroup viewGroup) {
+        View bottomView = null;
+        for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
+            View child = viewGroup.getChildAt(i);
+            if (child.getVisibility() == View.VISIBLE && child.getBottom() == viewGroup.getBottom()) {
+                bottomView = child;
+                break;
+            }
+        }
+        return bottomView;
+    }
+
+    @Nullable
+    private static View getTopView(ViewGroup viewGroup) {
+        View topView = null;
+        for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
+            View child = viewGroup.getChildAt(i);
+            if (child.getVisibility() == View.VISIBLE && child.getTop() == viewGroup.getTop()) {
+                topView = child;
+                break;
+            }
+        }
+        return topView;
+    }
+
+    private static boolean canViewOrChildScroll(View view, boolean atBottom) {
+        if (view == null || !(view instanceof ViewGroup)) {
+            return false;
+        }
+            /* Is the bottom view something that scrolls? */
+        if (view instanceof ScrollView) {
+            ScrollView sv = (ScrollView) view;
+            if (sv.getChildCount() == 0)
+                return false;
+            final int childHeight = sv.getChildAt(0).getMeasuredHeight();
+            return sv.getMeasuredHeight() < childHeight;
+        } else if (view instanceof AdapterView) {
+            return canAdapterViewScroll((AdapterView) view);
+        } else if (view instanceof WebView) {
+            return canWebViewScroll((WebView) view);
+//          } TODO else if RecyclerView {
         } else {
-            listView.setPadding(listView.getPaddingLeft(), 0,
-                    listView.getPaddingRight(), listView.getPaddingBottom());
+            if (atBottom) {
+                return canViewOrChildScroll(getBottomView((ViewGroup) view), true);
+            } else {
+                return canViewOrChildScroll(getTopView((ViewGroup) view), false);
+            }
         }
     }
 
-    private int calculateMaxButtonWidth() {
-        /**
-         * Max button width = (DialogWidth - Side margins) / [Number of buttons]
-         * From: http://www.google.com/design/spec/components/dialogs.html#dialogs-specs
-         */
-        final int dialogWidth = getWindow().getDecorView().getMeasuredWidth();
-        final int margins = (int) getContext().getResources().getDimension(R.dimen.md_button_padding_frame_side);
-        return (dialogWidth - 2 * margins) / numberOfActionButtons();
+
+    private static boolean canWebViewScroll(WebView view) {
+        return view.getMeasuredHeight() > view.getContentHeight();
     }
 
-    /**
-     * Detects whether or not the custom view or list content can be scrolled.
-     */
-    private boolean canCustomViewScroll() {
-        if (listView != null) {
-            return listView.getLastVisiblePosition() != -1 && listView.getLastVisiblePosition() < (listView.getCount() - 1);
+    private static boolean canAdapterViewScroll(AdapterView lv) {
+        /* Force it to layout it's children */
+        if (lv.getLastVisiblePosition() == -1)
+            return false;
+        /* We scroll if the last item is not visible */
+        boolean lastItemVisible = lv.getLastVisiblePosition() == lv.getCount() - 1;
+
+        if (lastItemVisible) {
+            /* or the last item's bottom is beyond our own bottom */
+            return lv.getChildAt(lv.getChildCount() - 1).getBottom() >
+                    lv.getHeight() - lv.getPaddingBottom();
         }
-        final ScrollView scrollView = (ScrollView) view.findViewById(R.id.customViewScroll);
-        final int childHeight = view.findViewById(R.id.customViewFrame).getMeasuredHeight();
-        return scrollView.getMeasuredHeight() < childHeight;
+        return true;
     }
 
+    private boolean canListViewScroll() {
+        return canAdapterViewScroll(listView);
+    }
+
+
     /**
      * Detects whether or not the content TextView can be scrolled.
      */
@@ -413,6 +499,34 @@ public class MaterialDialog extends DialogBase implements View.OnClickListener,
         return scrollView.getMeasuredHeight() < childHeight;
     }
 
+    /**
+     * Invalidates the radio buttons in the single choice mode list so that only the radio button that
+     * was previous selected is checked.
+     */
+    private void invalidateSingleChoice(int newSelection) {
+        newSelection++;
+        final LinearLayout list = (LinearLayout) view.findViewById(R.id.customViewFrame);
+        for (int i = 1; i < list.getChildCount(); i++) {
+            View v = list.getChildAt(i);
+            @SuppressLint("WrongViewCast")
+            RadioButton rb = (RadioButton) v.findViewById(R.id.control);
+            if (newSelection != i) {
+                rb.setChecked(false);
+                rb.clearFocus();
+            }
+        }
+    }
+
+    private int calculateMaxButtonWidth() {
+        /**
+         * Max button width = (DialogWidth - Side margins) / [Number of buttons]
+         * From: http://www.google.com/design/spec/components/dialogs.html#dialogs-specs
+         */
+        final int dialogWidth = getWindow().getDecorView().getMeasuredWidth();
+        final int margins = (int) getContext().getResources().getDimension(R.dimen.md_button_padding_frame_side);
+        return (dialogWidth - 2 * margins) / numberOfActionButtons();
+    }
+
     /**
      * Measures the action button's and their text to decide whether or not the button should be stacked.
      */
@@ -594,19 +708,6 @@ public class MaterialDialog extends DialogBase implements View.OnClickListener,
         }
     }
 
-    @Override
-    public void onMeasureScroll(ScrollView view) {
-        if (view.getMeasuredWidth() > 0) {
-            mMeasuredScrollView = true;
-            invalidateCustomViewAssociations();
-        }
-    }
-
-    @Override
-    public void onMeasureList(ListView view) {
-        invalidateCustomViewAssociations();
-    }
-
     /**
      * The class used to construct a MaterialDialog.
      */
@@ -645,6 +746,7 @@ public class MaterialDialog extends DialogBase implements View.OnClickListener,
         OnCancelListener cancelListener;
         OnShowListener showListener;
         boolean forceStacking;
+        boolean customViewScroll;
 
         public Builder(@NonNull Context context) {
             this.context = context;
@@ -845,14 +947,38 @@ public class MaterialDialog extends DialogBase implements View.OnClickListener,
             return this;
         }
 
+        /**
+         * Use {@link #customView(int, boolean)} instead.
+         *
+         * @param layoutRes
+         * @return
+         */
+        @Deprecated
         public Builder customView(@LayoutRes int layoutRes) {
+            return customView(layoutRes, true);
+        }
+
+        public Builder customView(@LayoutRes int layoutRes, boolean wrapInScrollView) {
             LayoutInflater li = LayoutInflater.from(this.context);
             customView(li.inflate(layoutRes, null));
+            this.customViewScroll = wrapInScrollView;
             return this;
         }
 
+        /**
+         * Use {@link #customView(android.view.View, boolean)} instead.
+         *
+         * @param view
+         * @return
+         */
+        @Deprecated
         public Builder customView(View view) {
+            return customView(view, true);
+        }
+
+        public Builder customView(View view, boolean wrapInScrollView) {
             this.customView = view;
+            this.customViewScroll = wrapInScrollView;
             return this;
         }
 
@@ -1276,15 +1402,12 @@ public class MaterialDialog extends DialogBase implements View.OnClickListener,
     public static abstract class ButtonCallback {
 
         public void onPositive(MaterialDialog dialog) {
-
         }
 
         public void onNegative(MaterialDialog dialog) {
-
         }
 
         public void onNeutral(MaterialDialog dialog) {
-
         }
 
         public ButtonCallback() {
@@ -1316,4 +1439,4 @@ public class MaterialDialog extends DialogBase implements View.OnClickListener,
             return super.toString();
         }
     }
-}
+}

+ 12 - 11
library/src/main/java/com/afollestad/materialdialogs/base/DialogBase.java

@@ -26,18 +26,19 @@ public class DialogBase extends AlertDialog implements DialogInterface.OnShowLis
         super(context);
     }
 
-    protected static void setMargin(View view, int top, int bottom, int left, int right) {
-        setMargin(view, top, bottom, left, right, -1);
-    }
-
-    private static void setMargin(View view, int top, int bottom, int left, int right, int height) {
+    protected void setVerticalMargins(View view, int topMargin, int bottomMargin) {
         ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
-        if (top > -1) params.topMargin = top;
-        if (bottom > -1) params.bottomMargin = bottom;
-        if (left > -1) params.leftMargin = left;
-        if (right > -1) params.rightMargin = right;
-        if (height > -1) params.height = height;
-        view.setLayoutParams(params);
+        boolean changed = false;
+        if (topMargin > -1 && params.topMargin != topMargin) {
+            params.topMargin = topMargin;
+            changed = true;
+        }
+        if (bottomMargin > -1 && params.bottomMargin != bottomMargin) {
+            params.bottomMargin = bottomMargin;
+            changed = true;
+        }
+        if (changed)
+            view.setLayoutParams(params);
     }
 
     /**

+ 0 - 40
library/src/main/java/com/afollestad/materialdialogs/views/MeasureCallbackListView.java

@@ -1,40 +0,0 @@
-package com.afollestad.materialdialogs.views;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.ListView;
-
-/**
- * @author Aidan Follestad (afollestad)
- */
-public class MeasureCallbackListView extends ListView {
-
-    public MeasureCallbackListView(Context context) {
-        super(context);
-    }
-
-    public MeasureCallbackListView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public MeasureCallbackListView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public static interface Callback {
-        void onMeasureList(ListView view);
-    }
-
-    private Callback mCallback;
-
-    public void setCallback(Callback mCallback) {
-        this.mCallback = mCallback;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        if (mCallback != null)
-            mCallback.onMeasureList(this);
-    }
-}

+ 0 - 40
library/src/main/java/com/afollestad/materialdialogs/views/MeasureCallbackScrollView.java

@@ -1,40 +0,0 @@
-package com.afollestad.materialdialogs.views;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.ScrollView;
-
-/**
- * @author Aidan Follestad (afollestad)
- */
-public class MeasureCallbackScrollView extends ScrollView {
-
-    public MeasureCallbackScrollView(Context context) {
-        super(context);
-    }
-
-    public MeasureCallbackScrollView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public MeasureCallbackScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public static interface Callback {
-        void onMeasureScroll(ScrollView view);
-    }
-
-    private Callback mCallback;
-
-    public void setCallback(Callback mCallback) {
-        this.mCallback = mCallback;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        if (mCallback != null)
-            mCallback.onMeasureScroll(this);
-    }
-}

+ 36 - 80
library/src/main/res/layout/md_dialog.xml

@@ -10,12 +10,6 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/md_dialog_frame_margin"
-        android:layout_marginBottom="@dimen/md_main_frame_margin"
-        android:layout_marginLeft="@dimen/md_dialog_frame_margin"
-        android:layout_marginStart="@dimen/md_dialog_frame_margin"
-        android:layout_marginRight="@dimen/md_dialog_frame_margin"
-        android:layout_marginEnd="@dimen/md_dialog_frame_margin"
         android:layout_weight="1">
 
         <LinearLayout
@@ -24,16 +18,17 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:gravity="center_vertical"
-            android:layoutDirection="locale"
-            android:layout_marginBottom="@dimen/md_main_frame_margin">
+            android:layout_marginLeft="@dimen/md_dialog_frame_margin"
+            android:layout_marginTop="@dimen/md_dialog_frame_margin"
+            android:layout_marginRight="@dimen/md_dialog_frame_margin"
+            android:layout_marginBottom="@dimen/md_title_frame_margin_bottom">
 
             <ImageView
                 android:id="@+id/icon"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:scaleType="fitXY"
-                android:layout_marginRight="@dimen/md_icon_margin"
-                android:layout_marginEnd="@dimen/md_icon_margin" />
+                android:layout_marginRight="@dimen/md_icon_margin" />
 
             <TextView
                 android:id="@+id/title"
@@ -43,94 +38,58 @@
                 tools:text="Title" />
         </LinearLayout>
 
-        <com.afollestad.materialdialogs.views.MeasureCallbackScrollView
+        <View
+            android:id="@+id/titleBarDivider"
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:layout_marginBottom="-1dp"
+            android:visibility="gone" />
+
+        <ScrollView
             android:id="@+id/contentScrollView"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:clipToPadding="false">
+            android:visibility="gone">
 
             <TextView
                 android:id="@+id/content"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:textSize="@dimen/md_content_textsize"
+                android:paddingLeft="@dimen/md_dialog_frame_margin"
+                android:paddingRight="@dimen/md_dialog_frame_margin"
                 tools:text="Content" />
-        </com.afollestad.materialdialogs.views.MeasureCallbackScrollView>
-    </LinearLayout>
-
-    <LinearLayout
-        android:orientation="vertical"
-        android:id="@+id/customViewScrollParent"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        android:visibility="gone">
-
-        <com.afollestad.materialdialogs.views.MeasureCallbackScrollView
-            android:id="@+id/customViewScroll"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:clipToPadding="false">
-
-            <LinearLayout
-                android:id="@+id/customViewFrame"
-                android:orientation="vertical"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:paddingTop="@dimen/md_dialog_frame_margin"
-                android:paddingBottom="@dimen/md_dialog_frame_margin"
-                android:layout_marginLeft="@dimen/md_dialog_frame_margin"
-                android:layout_marginStart="@dimen/md_dialog_frame_margin"
-                android:layout_marginRight="@dimen/md_dialog_frame_margin">
-
-                <LinearLayout
-                    android:id="@+id/titleFrameCustomView"
-                    android:orientation="horizontal"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:gravity="center_vertical"
-                    android:layoutDirection="locale"
-                    android:layout_marginBottom="@dimen/md_main_frame_margin">
-
-                    <ImageView
-                        android:id="@+id/iconCustomView"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:scaleType="fitXY"
-                        android:layout_marginRight="@dimen/md_icon_margin"
-                        android:layout_marginEnd="@dimen/md_icon_margin" />
-
-                    <TextView
-                        android:id="@+id/titleCustomView"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:textSize="@dimen/md_title_textsize"
-                        tools:text="Title" />
-                </LinearLayout>
-            </LinearLayout>
-        </com.afollestad.materialdialogs.views.MeasureCallbackScrollView>
+        </ScrollView>
 
-        <LinearLayout
-            android:id="@+id/list_view_container"
+        <FrameLayout
+            android:id="@+id/contentListViewFrame"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="vertical"
             android:visibility="gone">
 
-            <com.afollestad.materialdialogs.views.MeasureCallbackListView
+            <ListView
                 android:id="@+id/contentListView"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
+                android:paddingTop="@dimen/md_content_vertical_padding"
+                android:paddingBottom="@dimen/md_content_vertical_padding"
                 android:scrollbarStyle="outsideOverlay"
+                android:clipToPadding="false"
                 android:divider="@null"
                 android:dividerHeight="0dp" />
-        </LinearLayout>
+        </FrameLayout>
+
+        <FrameLayout
+            android:id="@+id/customViewFrame"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
     </LinearLayout>
 
     <View
-        android:id="@+id/customViewDivider"
+        android:id="@+id/buttonBarDivider"
         android:layout_width="match_parent"
         android:layout_height="1dp"
+        android:layout_marginTop="-1dp"
         android:visibility="gone" />
 
     <RelativeLayout
@@ -138,16 +97,13 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginRight="@dimen/md_button_padding_frame_side"
-        android:layout_marginEnd="@dimen/md_button_padding_frame_side"
-        android:layout_marginBottom="@dimen/md_button_padding_frame_bottom">
+        android:layout_marginBottom="@dimen/md_button_frame_vertical_padding">
 
         <Button
             android:id="@+id/buttonDefaultNeutral"
             style="@style/MD_ActionButton"
             android:layout_alignParentLeft="true"
-            android:layout_alignParentStart="true"
-            android:layout_marginLeft="@dimen/md_neutral_button_margin"
-            android:layout_marginStart="@dimen/md_neutral_button_margin" />
+            android:layout_marginLeft="@dimen/md_neutral_button_margin" />
         <!-- toLeftOf rule added from invalidateActions() -->
         <Button
             android:id="@+id/buttonDefaultNegative"
@@ -156,8 +112,7 @@
         <Button
             android:id="@+id/buttonDefaultPositive"
             style="@style/MD_ActionButton"
-            android:layout_alignParentRight="true"
-            android:layout_alignParentEnd="true" />
+            android:layout_alignParentRight="true" />
     </RelativeLayout>
 
     <LinearLayout
@@ -165,7 +120,8 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/md_button_padding_frame_bottom">
+        android:layout_marginTop="@dimen/md_button_frame_vertical_padding"
+        android:layout_marginBottom="@dimen/md_button_frame_vertical_padding">
 
         <Button
             android:id="@+id/buttonStackedPositive"

+ 13 - 14
library/src/main/res/values/dimens.xml

@@ -1,38 +1,37 @@
-<?xml version="1.0" encoding="utf-8"?>
 <resources>
-
     <!-- See http://www.google.com/design/spec/components/dialogs.html#dialogs-specs -->
-
+    <!-- Margin around the dialog, excluding the button bar -->
     <dimen name="md_dialog_frame_margin">24dp</dimen>
-    <dimen name="md_main_frame_margin">16dp</dimen>
-    <dimen name="md_title_margin_plainlist">8dp</dimen>
-
+    <dimen name="md_title_frame_margin_bottom">16dp</dimen>
+    <dimen name="md_title_frame_margin_bottom_list">4dp</dimen>
     <!-- 64dp + 8dp (left and right insets) -->
     <dimen name="md_button_min_width">72dp</dimen>
-
     <!-- Above and below buttons, 36+12=48 for the height of the button frame -->
     <dimen name="md_button_inset_vertical">6dp</dimen>
     <dimen name="md_button_inset_horizontal">4dp</dimen>
     <dimen name="md_button_padding_horizontal">8dp</dimen>
     <dimen name="md_button_padding_vertical">4dp</dimen>
-
     <dimen name="md_button_padding_horizontal_internalexternal">32dp</dimen>
-
     <!-- 16dp - 4dp (inset) -->
     <dimen name="md_button_padding_frame_side">12dp</dimen>
     <dimen name="md_neutral_button_margin">12dp</dimen>
-
-    <dimen name="md_button_padding_frame_bottom">8dp</dimen>
+    <!-- Applied to content scrollview/listview and customview, the spec calls for 16dp between the
+             bottom of the content and the top of the button bar, we split this between the two for
+             ease of layouts and adjustments. Likewise the spec implies 16dp between the title and
+             content and we split that as well.
+
+             Splitting makes it easier to have consistent padding against the dividers.
+             -->
+    <dimen name="md_content_vertical_padding">8dp</dimen>
+    <dimen name="md_button_frame_vertical_padding">8dp</dimen>
     <dimen name="md_button_height">48dp</dimen>
-
     <dimen name="md_title_textsize">20sp</dimen>
     <dimen name="md_content_textsize">18sp</dimen>
     <dimen name="md_button_textsize">14sp</dimen>
     <dimen name="md_listitem_textsize">18sp</dimen>
-    <dimen name="md_listitem_height">52dp</dimen>
+    <dimen name="md_listitem_height">48dp</dimen>
     <dimen name="md_listitem_control_margin">16dp</dimen>
     <dimen name="md_icon_margin">16dp</dimen>
     <dimen name="md_listitem_margin_left">24dp</dimen>
     <dimen name="md_action_corner_radius">2dp</dimen>
-
 </resources>

+ 2 - 2
sample/build.gradle

@@ -8,8 +8,8 @@ android {
         applicationId "com.afollestad.materialdialogssample"
         minSdkVersion 10
         targetSdkVersion 21
-        versionCode 55
-        versionName "0.4.9"
+        versionCode 56
+        versionName "0.5.0"
     }
     lintOptions {
         abortOnError false

BIN
sample/sample.apk


+ 48 - 0
sample/src/main/assets/webview.html

@@ -0,0 +1,48 @@
+<html>
+<head>
+    <title>HTML Title</title>
+</head>
+<body>
+
+<p>This is an <b>example</b> <i>HTML file</i> loaded from assets.</p>
+
+<p>Key changes in this release:
+<ul>
+    <li>Feature A</li>
+    <li>Feature B</li>
+    <li>Feature C</li>
+    <li>Feature D</li>
+    <li>Feature E</li>
+    <li>Bugfix A</li>
+    <li>Bugfix B</li>
+    <li>Bugfix C</li>
+    <li>Bugfix D</li>
+    <li>Bugfix E</li>
+    <li>Bugfix F</li>
+    <li>Bugfix G</li>
+    <li>Bugfix H</li>
+    <li>Bugfix I</li>
+    <li>Bugfix J</li>
+    <li>Bugfix K</li>
+    <li>Bugfix L</li>
+    <li>Bugfix M</li>
+    <li>Bugfix N</li>
+    <li>Bugfix O</li>
+    <li>Bugfix P</li>
+    <li>Bugfix Q</li>
+    <li>Bugfix R</li>
+    <li>Bugfix S</li>
+    <li>Bugfix T</li>
+    <li>Bugfix U</li>
+    <li>Bugfix V</li>
+    <li>Bugfix W</li>
+    <li>Bugfix X</li>
+    <li>Bugfix Y</li>
+    <li>Bugfix Z</li>
+</ul>
+
+<p>This is an example of a dialog that does not automatically wrap its custom view in a ScrollView,
+    since a WebView is already a ScrollView.</p>
+
+</body>
+</html>

+ 20 - 1
sample/src/main/java/com/afollestad/materialdialogssample/MainActivity.java

@@ -11,6 +11,7 @@ import android.text.method.PasswordTransformationMethod;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
+import android.webkit.WebView;
 import android.widget.AdapterView;
 import android.widget.CheckBox;
 import android.widget.CompoundButton;
@@ -140,6 +141,13 @@ public class MainActivity extends ActionBarActivity implements FolderSelectorDia
             }
         });
 
+        findViewById(R.id.customView_noScroll).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showCustomWebView();
+            }
+        });
+
         findViewById(R.id.themed).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
@@ -382,7 +390,7 @@ public class MainActivity extends ActionBarActivity implements FolderSelectorDia
     private void showCustomView() {
         MaterialDialog dialog = new MaterialDialog.Builder(this)
                 .title(R.string.googleWifi)
-                .customView(R.layout.dialog_customview)
+                .customView(R.layout.dialog_customview, true)
                 .positiveText(R.string.connect)
                 .negativeText(android.R.string.cancel)
                 .callback(new MaterialDialog.ButtonCallback() {
@@ -426,6 +434,17 @@ public class MainActivity extends ActionBarActivity implements FolderSelectorDia
         positiveAction.setEnabled(false); // disabled by default
     }
 
+    private void showCustomWebView() {
+        MaterialDialog dialog = new MaterialDialog.Builder(this)
+                .title(R.string.changelog)
+                .customView(R.layout.dialog_webview, false)
+                .positiveText(android.R.string.ok)
+                .build();
+        WebView webView = (WebView) dialog.getCustomView().findViewById(R.id.webview);
+        webView.loadUrl("file:///android_asset/webview.html");
+        dialog.show();
+    }
+
     private void showThemed() {
         new MaterialDialog.Builder(this)
                 .title(R.string.useGoogleLocationServices)

+ 13 - 6
sample/src/main/res/layout/activity_main.xml

@@ -94,24 +94,24 @@
             android:layout_marginTop="@dimen/sample_button_spacing" />
 
         <Button
-            android:id="@+id/complex"
+            android:id="@+id/customListItems"
             android:layout_width="match_parent"
             android:layout_height="56dp"
-            android:text="@string/complex"
+            android:text="@string/custom_adapter"
             android:layout_marginTop="@dimen/sample_button_spacing" />
 
         <Button
-            android:id="@+id/customListItems"
+            android:id="@+id/customView"
             android:layout_width="match_parent"
             android:layout_height="56dp"
-            android:text="@string/custom_adapter"
+            android:text="@string/customView"
             android:layout_marginTop="@dimen/sample_button_spacing" />
 
         <Button
-            android:id="@+id/customView"
+            android:id="@+id/customView_noScroll"
             android:layout_width="match_parent"
             android:layout_height="56dp"
-            android:text="@string/customView"
+            android:text="@string/customView_noScroll"
             android:layout_marginTop="@dimen/sample_button_spacing" />
 
         <Button
@@ -128,6 +128,13 @@
             android:text="@string/showCancelDismissCallbacks"
             android:layout_marginTop="@dimen/sample_button_spacing" />
 
+        <Button
+            android:id="@+id/complex"
+            android:layout_width="match_parent"
+            android:layout_height="56dp"
+            android:text="@string/complex"
+            android:layout_marginTop="@dimen/sample_button_spacing" />
+
         <Button
             android:id="@+id/folder_chooser"
             android:layout_width="match_parent"

+ 16 - 0
sample/src/main/res/layout/dialog_customview.xml

@@ -19,6 +19,22 @@
         android:textColor="#000000"
         android:text="@string/excellent" />
 
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="@dimen/customTitle"
+        android:textColor="#80000000"
+        android:layout_marginTop="18dp"
+        android:text="@string/ssid" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="@dimen/customEntry"
+        android:layout_marginTop="4dp"
+        android:textColor="#000000"
+        android:text="@string/ssidLit" />
+
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"

+ 5 - 0
sample/src/main/res/layout/dialog_webview.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<WebView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/webview"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content" />

+ 4 - 0
sample/src/main/res/values/strings.xml

@@ -53,6 +53,10 @@
     <string name="basic_list_notitle">Basic List (No Title)</string>
     <string name="folder_chooser">Folder Chooser</string>
     <string name="showCancelDismissCallbacks">Show/Cancel/Dismiss Callbacks</string>
+    <string name="changelog">Changelog</string>
+    <string name="customView_noScroll">Custom View (No ScrollView)</string>
+    <string name="ssid">SSID</string>
+    <string name="ssidLit">Material Dialogs</string>
 
     <string-array name="socialNetworks">
         <item>Twitter</item>