{"id":3693,"date":"2013-02-26T16:11:44","date_gmt":"2013-02-27T00:11:44","guid":{"rendered":"http:\/\/blog.xamarin.com\/?p=3693"},"modified":"2013-02-26T16:11:44","modified_gmt":"2013-02-27T00:11:44","slug":"android-tricks-flash-bars","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/xamarin\/android-tricks-flash-bars\/","title":{"rendered":"Android Tricks: Flash Bars"},"content":{"rendered":"<p>\t\t\t\t<em>The artwork and underlying idea in this blog post is based on <a href=\"http:\/\/roman.nurik.net\/\">Romain Nurik<\/a>&#8216;s work. He is a great source to follow for insight into Android application design.<\/em><\/p>\n<p>The new GMail app that comes with Android 4.0 is full of interesting UI tricks. If you have ever been on dodgy data connection, you probably have already encountered this one that I call the flash bar:<\/p>\n<p><img decoding=\"async\" src=\"\/wp-content\/uploads\/sites\/44\/2019\/04\/flash-bar-300x120.png\" alt=\"flash-bar\" width=\"300\" height=\"120\" class=\"aligncenter size-medium wp-image-3694\" \/><\/p>\n<p>There is a little text block for it at the bottom of the <a href=\"http:\/\/developer.android.com\/design\/patterns\/confirming-acknowledging.html\">Confirming &amp; Acknowledging<\/a> Android design page. This bar is used to warn of a non-fatal error in an unobstrusive manner and offer a quick way to retry the operation that failed. If the user doesn&#8217;t actually care about it, the bar will disappear after a small amount of time.<\/p>\n<p>For these cases, it efficiently replaces a normal <code>AlertDialog<\/code>&mdash;which is much more costly in term of user interaction. As such, it&#8217;s an excellent pattern to handle things like the kind of simple network errors that happen often and are generally non-lethal.<\/p>\n<p>As you can see in the screenshot, flash bars are very similar to normal <code>Toast<\/code> messages. The problem with toast messages is that they can never gain focus. Consequently, toast notifications can&#8217;t use any interactive UI elements (like buttons). That&#8217;s why the bar needs to be a custom XML layout snippet.<\/p>\n<p>First of all, download the <a href=\"\/wp-content\/uploads\/sites\/44\/2019\/04\/flashbar_resources1.zip\">following zip<\/a> containing the 9-patch images and the style definition for the flashbar. Put its content in your application&#8217;s <code>Resources<\/code> directory (merging the <code>styles.xml<\/code> file with what you already have).<\/p>\n<p>Next, you will need to tune up a bit the XML layout of the <code>Activity<\/code> that will contain a flash bar. Essentially, it should look like this:<\/p>\n<pre class=\"lang:xml decode:true\">\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;FrameLayout xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;\n\tandroid:layout_width=&quot;match_parent&quot;\n\tandroid:layout_height=&quot;match_parent&quot;&gt;\n\t&lt;LinearLayout\n\t\tandroid:orientation=&quot;vertical&quot;\n\t\tandroid:layout_width=&quot;fill_parent&quot;\n\t\tandroid:layout_height=&quot;fill_parent&quot;&gt;\n\t\t&lt;!-- Rest of your normal XML layout definition --&gt;\n\t&lt;\/LinearLayout&gt;\n\t&lt;LinearLayout\n\t\tandroid:id=&quot;@+id\/flashbar&quot;\n\t\tstyle=&quot;@style\/FlashBar&quot;&gt;\n\t\t&lt;TextView\n\t\t\tandroid:id=&quot;@+id\/flashbar_message&quot;\n\t\t\tstyle=&quot;@style\/FlashBarMessage&quot;\n\t\t\tandroid:text=&quot;Some error string&quot; \/&gt;\n\t\t&lt;Button\n\t\t\tandroid:id=&quot;@+id\/flashbar_button&quot;\n\t\t\tstyle=&quot;@style\/FlashBarButton&quot;\n\t\t\tandroid:text=&quot;Retry&quot; \/&gt;\n\t&lt;\/LinearLayout&gt;\n&lt;\/FrameLayout&gt;\n<\/pre>\n<p>The overlay trick is achieved by using multiple children in a container <code>FrameLayout<\/code> that stacks them from bottom to top on the screen. If you have an existing layout where you want to add a flash bar, the first child <code>LinearLayout<\/code> can be the one that was enclosing your layout initially.<\/p>\n<p>Now, the code manipulating that bar follows:<\/p>\n<div style=\"font-size: 11px\">\n<pre class=\"lang:csharp decode:true\">\nusing System;\nusing System.Threading.Tasks;\n\nusing Android.Animation;\nusing Android.OS;\nusing Android.Text;\nusing Android.Views;\nusing Android.Widget;\n\npublic class FlashBarController : AnimatorListenerAdapter\n{\n\tView barView;\n\tTextView messageView;\n\tViewPropertyAnimator barAnimator;\n\tHandler hideHandler = new Handler();\n\t\n\tstring message;\n\tAction hideRunnable;\n\tAction flashBarCallback;\n\t\n\tconst int DefaultHideTime = 5000;\n\t\n\tpublic FlashBarController (View flashBarView)\n\t{\n\t\tbarView = flashBarView;\n\t\tbarAnimator = barView.Animate ();\n\t\t\n\t\tmessageView = barView.FindViewById&lt;TextView&gt; (Resource.Id.flashbar_message);\n\t\tvar flashBarBtn = barView.FindViewById&lt;Button&gt; (Resource.Id.flashbar_button);\n\t\tflashBarBtn.Click += delegate {\n\t\t\tHideBar (false);\n\t\t\tif (flashBarCallback != null)\n\t\t\t\tflashBarCallback ();\n\t\t};\n\t\thideRunnable = () =&gt; HideBar(false);\n\t\t\n\t\tHideBar (true);\n\t}\n\t\n\tpublic void ShowBarUntil (Func&lt;bool&gt; flashBarCallback, bool immediate = false, string withMessage = null, int withMessageId = -1)\n\t{\n\t\tAction callback = () =&gt; {\n\t\t\tvar t = Task.Factory.StartNew (flashBarCallback);\n\t\t\tt.ContinueWith (_ =&gt; {\n\t\t\t\tif (t.Exception != null || !t.Result)\n\t\t\t\t\thideHandler.Post (() =&gt; ShowBarUntil (flashBarCallback, immediate, withMessage, withMessageId));\n\t\t\t});\n\t\t};\n\t\tShowBar (callback, immediate, withMessage, withMessageId);\n\t}\n\t\n\tpublic void ShowBar (Action flashBarCallback, bool immediate = false, string withMessage = null, int withMessageId = -1)\n\t{\n\t\tif (withMessage != null) {\n\t\t\tthis.message = withMessage;\n\t\t\tmessageView.Text = message;\n\t\t}\n\t\tif (withMessageId != -1) {\n\t\t\tthis.message = barView.Resources.GetString (withMessageId);\n\t\t\tmessageView.Text = message;\n\t\t}\n\t\t\n\t\tthis.flashBarCallback = flashBarCallback;\n\t\thideHandler.RemoveCallbacks (hideRunnable);\n\t\thideHandler.PostDelayed (hideRunnable, DefaultHideTime);\n\t\t\n\t\tbarView.Visibility = ViewStates.Visible;\n\t\tif (immediate) {\n\t\t\tbarView.Alpha = 1;\n\t\t} else {\n\t\t\tbarAnimator.Cancel();\n\t\t\tbarAnimator.Alpha (1);\n\t\t\tbarAnimator.SetDuration (barView.Resources.GetInteger (Android.Resource.Integer.ConfigShortAnimTime));\n\t\t\tbarAnimator.SetListener (null);\n\t\t}\n\t}\n\t\n\tpublic void HideBar (bool immediate = false)\n\t{\n\t\thideHandler.RemoveCallbacks (hideRunnable);\n\t\tif (immediate) {\n\t\t\tbarView.Visibility = ViewStates.Gone;\n\t\t\tbarView.Alpha = 0;\n\t\t} else {\n\t\t\tbarAnimator.Cancel();\n\t\t\tbarAnimator.Alpha (0);\n\t\t\tbarAnimator.SetDuration (barView.Resources.GetInteger (Android.Resource.Integer.ConfigShortAnimTime));\n\t\t\tbarAnimator.SetListener (this);\n\t\t}\n\t}\n\t\n\tpublic override void OnAnimationEnd (Animator animation)\n\t{\n\t\tbarView.Visibility = ViewStates.Gone;\n\t\tmessage = null;\n\t}\n}\n<\/pre>\n<\/div>\n<p>You generally instantiate this class after inflating your <code>Activity<\/code> layout by passing it the <code>LinearLayout<\/code> of the flash bar as a <code>View<\/code>, like so:<\/p>\n<pre class=\"lang:csharp decode:true\">\nvar view = inflater.Inflate (Resource.Layout.MyMainLayout, container, false);\n\/\/ ...\nvar flashBarView = view.FindViewById (Resource.Id.flashbar);\nflashBarCtrl = new FlashBarController (flashBarView);\n<\/pre>\n<p>Afterwards, you can use either <code>ShowBar<\/code> or <code>ShowBarUntil<\/code> to display the flash bar. Each of these methods accepts a boolean parameter to disable animation. You can set the text of the bar either by string id or directly by passing a <code>string<\/code> instance. The callback is the action to be called when the user tap the bar button.<\/p>\n<p>Here are two examples using the above methods:<\/p>\n<pre class=\"lang:csharp decode:true\">\n\/\/ ShowBarUntil\n\/\/ If the callback returns false or throws an exception, the bar will be shown again\nflashBarCtrl.ShowBarUntil (() =&gt; {\n\tDoNetworkCall ();\n\tDoSomeOtherStuff ();\n\treturn true;\n}, withMessageId: Resource.String.flashbar_tab_error);\n\n\/\/ ShowBar\nflashBarCtrl.ShowBar (() =&gt; RecursivelyCallTheExecutingMethod (withThe, sameParameters),\n                      withMessage: &quot;Problem while contacting server&quot;);\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>The artwork and underlying idea in this blog post is based on Romain Nurik&#8216;s work. He is a great source to follow for insight into Android application design. The new GMail app that comes with Android 4.0 is full of interesting UI tricks. If you have ever been on dodgy data connection, you probably have [&hellip;]<\/p>\n","protected":false},"author":1925,"featured_media":39167,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[5,4],"class_list":["post-3693","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-developers","tag-android","tag-xamarin-platform"],"acf":[],"blog_post_summary":"<p>The artwork and underlying idea in this blog post is based on Romain Nurik&#8216;s work. He is a great source to follow for insight into Android application design. The new GMail app that comes with Android 4.0 is full of interesting UI tricks. If you have ever been on dodgy data connection, you probably have [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/3693","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/users\/1925"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/comments?post=3693"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/3693\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media\/39167"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media?parent=3693"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/categories?post=3693"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/tags?post=3693"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}