From 665dc1ccf487e1ae2bca7857a00c268b1b61f18d Mon Sep 17 00:00:00 2001 From: Emily Lynam Date: Mon, 8 Apr 2019 19:11:34 +0000 Subject: [PATCH] Merged PR 251892: Snackbar: Bug fixes and polish for icon variant - Fix a bug so that we can use `Snackbar`s in `RecyclerView`s with `findSuitableParent` method borrowed from android (discovered this while working on `ListItemView`) - Add single line Snackbar with icon variant to the demo and fix bugs so that the icon and text are centered properly - Give a default duration (per design and pm request) - Note which duration is used in the demo button text. | Single-line with icon | Single-line with icon and action | | ------------- |:-------------:| |![Screenshot_1554738893.png](https://onedrive.visualstudio.com/4dcbf0bc-c3cd-49c8-a7c3-ec1924691d9b/_apis/git/repositories/32fa6338-45ea-42a0-aca0-484938e1962a/pullRequests/251892/attachments/Screenshot_1554738893.png) | ![Screenshot_1554738897.png](https://onedrive.visualstudio.com/4dcbf0bc-c3cd-49c8-a7c3-ec1924691d9b/_apis/git/repositories/32fa6338-45ea-42a0-aca0-484938e1962a/pullRequests/251892/attachments/Screenshot_1554738897.png) | | Multi-line with icon | Multi-line with icon and action | |![Screenshot_1554738900.png](https://onedrive.visualstudio.com/4dcbf0bc-c3cd-49c8-a7c3-ec1924691d9b/_apis/git/repositories/32fa6338-45ea-42a0-aca0-484938e1962a/pullRequests/251892/attachments/Screenshot_1554738900.png) | ![Screenshot_1554738905.png](https://onedrive.visualstudio.com/4dcbf0bc-c3cd-49c8-a7c3-ec1924691d9b/_apis/git/repositories/32fa6338-45ea-42a0-aca0-484938e1962a/pullRequests/251892/attachments/Screenshot_1554738905.png) | Related work items: #706313 --- .../demos/SnackbarActivity.kt | 105 +++++++++++------- .../src/main/res/drawable/demo_divider.xml | 2 +- .../{ms_ic_birthday.xml => ic_birthday.xml} | 0 .../src/main/res/drawable/ic_done_white.xml | 18 +++ .../src/main/res/layout/activity_snackbar.xml | 28 ++++- .../src/main/res/values/strings.xml | 16 ++- .../officeuifabric/snackbar/Snackbar.kt | 44 +++++++- .../src/main/res/layout/view_snackbar.xml | 1 - OfficeUIFabric/src/main/res/values/dimens.xml | 1 + 9 files changed, 163 insertions(+), 52 deletions(-) rename OfficeUIFabric.Demo/src/main/res/drawable/{ms_ic_birthday.xml => ic_birthday.xml} (100%) create mode 100644 OfficeUIFabric.Demo/src/main/res/drawable/ic_done_white.xml diff --git a/OfficeUIFabric.Demo/src/main/java/com/microsoft/officeuifabricdemo/demos/SnackbarActivity.kt b/OfficeUIFabric.Demo/src/main/java/com/microsoft/officeuifabricdemo/demos/SnackbarActivity.kt index 05873f7..39b53fc 100644 --- a/OfficeUIFabric.Demo/src/main/java/com/microsoft/officeuifabricdemo/demos/SnackbarActivity.kt +++ b/OfficeUIFabric.Demo/src/main/java/com/microsoft/officeuifabricdemo/demos/SnackbarActivity.kt @@ -21,56 +21,79 @@ class SnackbarActivity : DemoActivity(), View.OnClickListener { super.onCreate(savedInstanceState) btn_snackbar_single_line.setOnClickListener(this) - btn_snackbar_multi_line.setOnClickListener(this) - btn_snackbar_announcement.setOnClickListener(this) - btn_snackbar_multi_line_action.setOnClickListener(this) - btn_snackbar_multi_line_long_action.setOnClickListener(this) + btn_snackbar_single_line_icon.setOnClickListener(this) btn_snackbar_single_line_action.setOnClickListener(this) + btn_snackbar_single_line_action_icon.setOnClickListener(this) + btn_snackbar_multi_line.setOnClickListener(this) + btn_snackbar_multi_line_icon.setOnClickListener(this) + btn_snackbar_multi_line_action.setOnClickListener(this) + btn_snackbar_multi_line_action_icon.setOnClickListener(this) + btn_snackbar_multi_line_long_action.setOnClickListener(this) + btn_snackbar_announcement.setOnClickListener(this) } override fun onClick(v: View) { when (v.id) { - R.id.btn_snackbar_single_line -> Snackbar.make(root_view, getString(R.string.snackbar_single_line), Snackbar.LENGTH_SHORT).show() + R.id.btn_snackbar_single_line -> + Snackbar.make(root_view, getString(R.string.snackbar_single_line)).show() - R.id.btn_snackbar_multi_line -> Snackbar.make(root_view, getString(R.string.snackbar_multi_line), Snackbar.LENGTH_LONG).show() + R.id.btn_snackbar_single_line_icon -> + Snackbar.make(root_view, getString(R.string.snackbar_single_line)) + .setIcon(R.drawable.ic_done_white) + .show() - R.id.btn_snackbar_single_line_action -> { - val snackbar = Snackbar.make(root_view, getString(R.string.snackbar_single_line), Snackbar.LENGTH_SHORT) - snackbar.setAction(getString(R.string.snackbar_action), View.OnClickListener { - // handle click here - }) - snackbar.show() - } + R.id.btn_snackbar_single_line_action -> + Snackbar.make(root_view, getString(R.string.snackbar_single_line)) + .setAction(getString(R.string.snackbar_action), View.OnClickListener { + // handle click here + }) + .show() - R.id.btn_snackbar_multi_line_action -> { - val snackbar = Snackbar.make(root_view, getString(R.string.snackbar_multi_line), Snackbar.LENGTH_INDEFINITE) - snackbar.setAction(getString(R.string.snackbar_action), View.OnClickListener { - // handle click here - }) - snackbar.show() - } + R.id.btn_snackbar_single_line_action_icon -> + Snackbar.make(root_view, getString(R.string.snackbar_single_line)) + .setIcon(R.drawable.ic_done_white) + .setAction(getString(R.string.snackbar_action), View.OnClickListener { + // handle click here + }) + .show() - R.id.btn_snackbar_multi_line_long_action -> { - val snackbar = Snackbar.make(root_view, getString(R.string.snackbar_multi_line), Snackbar.LENGTH_INDEFINITE) - snackbar.setAction(getString(R.string.snackbar_action_long), View.OnClickListener { - // handle click here - }) - snackbar.show() - } + R.id.btn_snackbar_multi_line -> + Snackbar.make(root_view, getString(R.string.snackbar_multi_line), Snackbar.LENGTH_LONG).show() - R.id.btn_snackbar_announcement -> { - val snackbar = Snackbar.make( - root_view, - getString(R.string.snackbar_announcement), - Snackbar.LENGTH_INDEFINITE, - Snackbar.Style.ANNOUNCEMENT - ) - snackbar.setIcon(R.drawable.ms_ic_birthday) - snackbar.setAction(getString(R.string.snackbar_action), View.OnClickListener { - // handle click here - }) - snackbar.show() - } + R.id.btn_snackbar_multi_line_icon -> + Snackbar.make(root_view, getString(R.string.snackbar_multi_line), Snackbar.LENGTH_LONG) + .setIcon(R.drawable.ic_done_white) + .show() + + R.id.btn_snackbar_multi_line_action -> + Snackbar.make(root_view, getString(R.string.snackbar_multi_line), Snackbar.LENGTH_INDEFINITE) + .setAction(getString(R.string.snackbar_action), View.OnClickListener { + // handle click here + }) + .show() + + R.id.btn_snackbar_multi_line_action_icon -> + Snackbar.make(root_view, getString(R.string.snackbar_multi_line)) + .setIcon(R.drawable.ic_done_white) + .setAction(getString(R.string.snackbar_action), View.OnClickListener { + // handle click here + }) + .show() + + R.id.btn_snackbar_multi_line_long_action -> + Snackbar.make(root_view, getString(R.string.snackbar_multi_line)) + .setAction(getString(R.string.snackbar_action_long), View.OnClickListener { + // handle click here + }) + .show() + + R.id.btn_snackbar_announcement -> + Snackbar.make(root_view, getString(R.string.snackbar_announcement), style = Snackbar.Style.ANNOUNCEMENT) + .setIcon(R.drawable.ic_birthday) + .setAction(getString(R.string.snackbar_action), View.OnClickListener { + // handle click here + }) + .show() } } -} +} \ No newline at end of file diff --git a/OfficeUIFabric.Demo/src/main/res/drawable/demo_divider.xml b/OfficeUIFabric.Demo/src/main/res/drawable/demo_divider.xml index 5aeae56..d7fc775 100644 --- a/OfficeUIFabric.Demo/src/main/res/drawable/demo_divider.xml +++ b/OfficeUIFabric.Demo/src/main/res/drawable/demo_divider.xml @@ -5,6 +5,6 @@ --> - + \ No newline at end of file diff --git a/OfficeUIFabric.Demo/src/main/res/drawable/ms_ic_birthday.xml b/OfficeUIFabric.Demo/src/main/res/drawable/ic_birthday.xml similarity index 100% rename from OfficeUIFabric.Demo/src/main/res/drawable/ms_ic_birthday.xml rename to OfficeUIFabric.Demo/src/main/res/drawable/ic_birthday.xml diff --git a/OfficeUIFabric.Demo/src/main/res/drawable/ic_done_white.xml b/OfficeUIFabric.Demo/src/main/res/drawable/ic_done_white.xml new file mode 100644 index 0000000..0b78cb1 --- /dev/null +++ b/OfficeUIFabric.Demo/src/main/res/drawable/ic_done_white.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/OfficeUIFabric.Demo/src/main/res/layout/activity_snackbar.xml b/OfficeUIFabric.Demo/src/main/res/layout/activity_snackbar.xml index 65b64eb..94ca184 100644 --- a/OfficeUIFabric.Demo/src/main/res/layout/activity_snackbar.xml +++ b/OfficeUIFabric.Demo/src/main/res/layout/activity_snackbar.xml @@ -19,10 +19,10 @@ android:text="@string/snackbar_button_single_line" /> + android:text="@string/snackbar_button_single_line_icon" /> + + + + + + + + Action Long Text Action This is an announcement snackbar. It’s used for communicating new features. - Single-line - Multi-line - Single-line with action - Multi-line with action - Multi-line with long action text - Announcement style + Single-line, short + Single-line with icon, short + Single-line with action, short + Single-line with action and icon, short + Multi-line, long + Multi-line with icon, long + Multi-line with action, indefinite + Multi-line with action and icon, short + Multi-line with long action text, short + Announcement style, short This is a multi-line snackbar. Max lines are set to two, the rest of the text will truncate. Lorem ipsum dolor sit amet consectetur adipiscing elit. Single-line snackbar diff --git a/OfficeUIFabric/src/main/java/com/microsoft/officeuifabric/snackbar/Snackbar.kt b/OfficeUIFabric/src/main/java/com/microsoft/officeuifabric/snackbar/Snackbar.kt index ac5a74f..51f806b 100644 --- a/OfficeUIFabric/src/main/java/com/microsoft/officeuifabric/snackbar/Snackbar.kt +++ b/OfficeUIFabric/src/main/java/com/microsoft/officeuifabric/snackbar/Snackbar.kt @@ -7,11 +7,13 @@ package com.microsoft.officeuifabric.snackbar import android.support.annotation.DrawableRes import android.support.design.widget.BaseTransientBottomBar +import android.support.design.widget.CoordinatorLayout import android.support.v4.content.ContextCompat import android.support.v7.widget.AppCompatButton import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.FrameLayout import android.widget.ImageView import android.widget.RelativeLayout import android.widget.TextView @@ -31,7 +33,9 @@ class Snackbar : BaseTransientBottomBar { const val LENGTH_SHORT: Int = BaseTransientBottomBar.LENGTH_SHORT const val LENGTH_LONG: Int = BaseTransientBottomBar.LENGTH_LONG - fun make(parent: ViewGroup, text: CharSequence, duration: Int, style: Style = Style.REGULAR): Snackbar { + fun make(view: View, text: CharSequence, duration: Int = LENGTH_SHORT, style: Style = Style.REGULAR): Snackbar { + val parent = findSuitableParent(view) ?: + throw IllegalArgumentException("No suitable parent found from the given view. Please provide a valid view.") val content = LayoutInflater.from(parent.context).inflate(R.layout.view_snackbar, parent, false) val snackbar = Snackbar(parent, content, ContentViewCallback(content)) snackbar.duration = duration @@ -39,6 +43,36 @@ class Snackbar : BaseTransientBottomBar { snackbar.setText(text) return snackbar } + + /** + * This is adapted from android.support.design.widget.snackbar + * It ensures we can use Snackbars in complex ViewGroups like RecyclerView. + */ + private fun findSuitableParent(view: View): ViewGroup? { + var currentView: View? = view + var fallbackParent: ViewGroup? = null + + do { + if (currentView is CoordinatorLayout) + // We've found a CoordinatorLayout, use it + return currentView + + if (currentView is FrameLayout) + if (currentView.id == android.R.id.content) + // If we've hit the decor content view, then we didn't find a CoL in the + // hierarchy, so use it. + return currentView + else + // It's not the content view but we'll use it as our fallback + fallbackParent = currentView + + // Else, we will loop and crawl up the view hierarchy and try to find a parent + currentView = currentView?.parent as? View + } while (currentView != null) + + // If we reach here then we didn't find a CoL or a suitable content view so we'll fallback + return fallbackParent + } } /** @@ -101,14 +135,20 @@ class Snackbar : BaseTransientBottomBar { updateBackground() layoutTextAndActionButton() + val iconImageViewLayoutParams = iconImageView.layoutParams as RelativeLayout.LayoutParams when (style) { Style.REGULAR -> { actionButtonView.setTextColor(ContextCompat.getColor(context, R.color.uifabric_snackbar_action_text)) + iconImageViewLayoutParams.topMargin = context.resources.getDimension(R.dimen.uifabric_snackbar_icon_inset_regular).toInt() + iconImageViewLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL) } Style.ANNOUNCEMENT -> { actionButtonView.setTextColor(ContextCompat.getColor(context, R.color.uifabric_snackbar_action_text_announcement)) + iconImageViewLayoutParams.topMargin = context.resources.getDimension(R.dimen.uifabric_snackbar_content_inset).toInt() + iconImageViewLayoutParams.removeRule(RelativeLayout.CENTER_VERTICAL) } } + iconImageView.layoutParams = iconImageViewLayoutParams } private fun layoutTextAndActionButton() { @@ -120,12 +160,14 @@ class Snackbar : BaseTransientBottomBar { if (textWidth > context.resources.getDimension(R.dimen.uifabric_snackbar_action_text_wrapping_width) || style == Style.ANNOUNCEMENT) { // Action button moves to the bottom of the root view textLayoutParams.removeRule(RelativeLayout.START_OF) + textLayoutParams.removeRule(RelativeLayout.CENTER_VERTICAL) textLayoutParams.marginEnd = inset buttonLayoutParams.addRule(RelativeLayout.BELOW, snackbar_text) actionButtonView.setPaddingRelative(inset, inset, inset, inset) } else { // Action button moves to the end of the text view textLayoutParams.addRule(RelativeLayout.START_OF, snackbar_action) + textLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL) textLayoutParams.bottomMargin = inset if (actionButtonView.text.isNullOrEmpty()) textLayoutParams.marginEnd = inset diff --git a/OfficeUIFabric/src/main/res/layout/view_snackbar.xml b/OfficeUIFabric/src/main/res/layout/view_snackbar.xml index 9027ed3..be74380 100644 --- a/OfficeUIFabric/src/main/res/layout/view_snackbar.xml +++ b/OfficeUIFabric/src/main/res/layout/view_snackbar.xml @@ -16,7 +16,6 @@ android:layout_height="@dimen/uifabric_snackbar_announcement_icon_size" android:layout_gravity="center_vertical|start" android:layout_marginStart="@dimen/uifabric_snackbar_content_inset" - android:layout_marginTop="@dimen/uifabric_snackbar_content_inset" android:visibility="gone" /> 8dp 4dp @dimen/uifabric_content_inset + 12dp 176dp