{"id":671,"date":"2010-12-08T11:45:48","date_gmt":"2010-12-08T16:45:48","guid":{"rendered":"http:\/\/www.outofwhatbox.com\/blog\/?p=671"},"modified":"2010-12-08T12:06:40","modified_gmt":"2010-12-08T17:06:40","slug":"android-spinners-simpleadapter-and-maybe-viewbinder","status":"publish","type":"post","link":"https:\/\/www.outofwhatbox.com\/blog\/2010\/12\/android-spinners-simpleadapter-and-maybe-viewbinder\/","title":{"rendered":"Android: Spinners, SimpleAdapter, and (maybe) ViewBinder"},"content":{"rendered":"<p>The question came up on <a href=\"http:\/\/stackoverflow.com\/questions\/4383913\">StackOverflow<\/a> yesterday: Can a Spinner be configured to use a SimpleAdapter (and if so, how?) The user who asked the question, <a href=\"http:\/\/stackoverflow.com\/users\/533635\/chromium\">Chromium<\/a>, ran into a couple of problems; the last of these was an IllegalStateException after clicking on the Spinner to make a selection.<\/p>\n<p>A bit of searching turned up <a href=\"http:\/\/code.google.com\/p\/android\/issues\/detail?id=7251\">this issue<\/a> about using a Spinner in combination with a SimpleAdapter to display a CheckedTextView; it seems that a ViewBinder must be set on the SimpleAdapter for that combination to work. Although it&#8217;s not the same problem, it still made me wonder if the SimpleAdapter \/ Spinner combination might need a ViewBinder for displaying TextView, too.<\/p>\n<p>I wasn&#8217;t able to reproduce the IllegalStateException when I ran a sample program on AndroidOS 2.2, but I thought I&#8217;d try it on 1.5 in the emulator. Sure enough, I hit the same problem that Chromium reported. Apparently, whatever issue this is has been fixed by 2.2, and perhaps in earlier releases. (I haven&#8217;t tried other releases besides 2.2 and 1.5.)<\/p>\n<p>And I also found that ViewBinder once again comes to the rescue, preventing the IllegalStateException from being thrown.<\/p>\n<p>Like some other interfaces in Android (I&#8217;m looking at you, <a href=\"http:\/\/developer.android.com\/reference\/android\/widget\/SimpleCursorAdapter.CursorToStringConverter.html\">CursorToStringConverter<\/a>. You too, <a href=\"http:\/\/developer.android.com\/reference\/android\/widget\/FilterQueryProvider.html\">FilterQueryProvider<\/a>), <a href=\"http:\/\/developer.android.com\/reference\/android\/widget\/SimpleAdapter.ViewBinder.html\">ViewBinder&#8217;<\/a>s bark is worse than its bite. In other words, the name may be a bit intimidating, but the implementation is a breeze.<\/p>\n<div class=\"oowbbtw\">Well, it <strong>would<\/strong> be a breeze, perhaps, if the documentation were clearer. The only method that you need to implement, <a href=\"http:\/\/developer.android.com\/reference\/android\/widget\/SimpleAdapter.ViewBinder.html#setViewValue(android.view.View,%20java.lang.Object,%20java.lang.String)\">setViewValue<\/a>, receives three parameters: <code>View view, Object data, String textRepresentation<\/code>. But the documentation doesn&#8217;t tell us what these parameters really <em>mean<\/em> in a given context. I started by creating an empty setViewValue method, and set a breakpoint there. Inside the breakpoint, I verified that <code>view<\/code> is the TextView for displaying a single row in the Spinner; <code>data<\/code> is the String value in the Map for this row in the Spinner (&#8220;Red&#8221;, &#8220;Orange&#8221;, etc.); and textRepresentation is, well, the text representation of <code>data<\/code>, which in this case is that same String.<\/div>\n<p>Here&#8217;s a sample program, adapted from <a href=\"http:\/\/stackoverflow.com\/questions\/4383913\">Chromium&#8217;s example<\/a>, that adds a ViewBinder to a SimpleAdapter. This works on AndroidOS 1.5 and 2.2 (and, presumably, intermediate versions as well.)<\/p>\n<pre class=\"brush: java; title: HelloSpinner; notranslate\" title=\"HelloSpinner\">\r\npackage org.oowb.HelloSpinner;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.HashMap;\r\nimport java.util.List;\r\nimport java.util.Map;\r\n\r\nimport android.app.Activity;\r\nimport android.os.Bundle;\r\nimport android.view.View;\r\nimport android.widget.AdapterView;\r\nimport android.widget.SimpleAdapter;\r\nimport android.widget.Spinner;\r\nimport android.widget.TextView;\r\nimport android.widget.Toast;\r\nimport android.widget.AdapterView.OnItemSelectedListener;\r\n\r\n\/**\r\n *  Displays a list of colors in a Spinner.\r\n *\/\r\npublic class HelloSpinner extends Activity {\r\n    @Override\r\n    public void onCreate(Bundle savedInstanceState) {\r\n        super.onCreate(savedInstanceState);\r\n        setContentView(R.layout.main);\r\n\r\n        \/\/ The key to use for reading the color from the Map\r\n        final String[] from = new String[] { &quot;color&quot; };\r\n\r\n        \/\/ The type of View to use for displaying the color name.\r\n        \/\/ android.R.id.text1 is a standard resource for displaying text.\r\n        final int[] to = new int[] { android.R.id.text1  };\r\n\r\n        \/\/ Create the List of strings for the spinner to display. Each string\r\n        \/\/ is embedded within a Map, using &quot;color&quot; as the key.\r\n        final List&lt;Map&lt;String, String&gt;&gt; data = \r\n            new ArrayList&lt;Map&lt;String, String&gt;&gt;();\r\n        final String[] colors = getResources().getStringArray(R.array.colors);\r\n\r\n        for (int i = 0; i &lt; colors.length; i++) {\r\n            data.add(addData(colors[i]));\r\n        }\r\n\r\n        final SimpleAdapter simpleAdapter = \r\n            new SimpleAdapter(this, data, \r\n                    android.R.layout.simple_spinner_item, from, to);\r\n        simpleAdapter.setDropDownViewResource(\r\n                android.R.layout.simple_spinner_dropdown_item);\r\n\r\n        \/\/ Add a ViewBinder to display a color name in a TextView within the\r\n        \/\/ Spinner. (This isn't needed in AndroidOS 2.2. In earlier releases,\r\n        \/\/ when we're displaying text data within a Spinner, and no ViewBinder\r\n        \/\/ is set in the SimpleAdapter, an IllegalStateException is thrown.)\r\n        SimpleAdapter.ViewBinder viewBinder = new SimpleAdapter.ViewBinder() {\r\n            \r\n            public boolean setViewValue(View view, Object data,\r\n                    String textRepresentation) {\r\n                \/\/ We configured the SimpleAdapter to create TextViews (see\r\n                \/\/ the 'to' array, above), so this cast should be safe:\r\n                TextView textView = (TextView) view;\r\n                textView.setText(textRepresentation);\r\n                return true;\r\n            }\r\n        };\r\n        simpleAdapter.setViewBinder(viewBinder);\r\n\r\n        final Spinner spinner = (Spinner) findViewById(R.id.spinner);\r\n        spinner.setAdapter(simpleAdapter);\r\n\r\n        \/\/ Add an OnItemSelectedListener to display the selected Color\r\n        spinner.setOnItemSelectedListener(new OnItemSelectedListener() {\r\n            @Override\r\n            public void onItemSelected(AdapterView&lt;?&gt; parent, View view,\r\n                    int position, long id) {\r\n                \/\/ Get the color name out of the Map\r\n                final Map&lt;String, String&gt; data = \r\n                    (Map&lt;String, String&gt;) parent.getItemAtPosition(position);\r\n                final String text = &quot;Selected Color:-  &quot; + data.get(&quot;color&quot;);\r\n\r\n                Toast.makeText(parent.getContext(), text, \r\n                        Toast.LENGTH_LONG).show();\r\n            }\r\n\r\n            @Override\r\n            public void onNothingSelected(AdapterView&lt;?&gt; arg0) {\r\n                \/\/ Do nothing\r\n            }\r\n        });\r\n    }\r\n\r\n    \/**\r\n     * Convert the String that's passed in into a Map, with\r\n     * &quot;color&quot; as the key, and the String as the value.\r\n     * @param    colorName  The color to be inserted into a new Map\r\n     * @return   the new Map\r\n     *\/\r\n    private Map&lt;String, String&gt; addData(String colorName) {\r\n        Map&lt;String, String&gt; map = new HashMap&lt;String, String&gt;();\r\n        map.put(&quot;color&quot;, colorName);\r\n        return map;\r\n    }\r\n}<\/pre>\n<p>To complete the example, here are the layout (<code>main.xml<\/code>) and values files (<code>arrays.xml<\/code> and <code>strings.xml<\/code>):<\/p>\n<pre class=\"brush: xml; title: main; notranslate\" title=\"main\">\r\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\r\n&lt;LinearLayout xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;\r\n    android:orientation=&quot;vertical&quot;\r\n    android:padding=&quot;10dip&quot;\r\n    android:layout_width=&quot;fill_parent&quot;\r\n    android:layout_height=&quot;wrap_content&quot;&gt;\r\n\r\n    &lt;Spinner \r\n        android:id=&quot;@+id\/spinner&quot;\r\n        android:layout_width=&quot;fill_parent&quot;\r\n        android:layout_height=&quot;wrap_content&quot;\r\n        android:drawSelectorOnTop=&quot;true&quot;\r\n        android:prompt=&quot;@string\/prompt&quot;\r\n    \/&gt;\r\n&lt;\/LinearLayout&gt;\r\n<\/pre>\n<pre class=\"brush: xml; title: arrays; notranslate\" title=\"arrays\">\r\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;\r\n&lt;resources&gt;\r\n    &lt;string-array name=&quot;colors&quot;&gt;\r\n        &lt;item&gt;Red&lt;\/item&gt;\r\n        &lt;item&gt;Orange&lt;\/item&gt;\r\n        &lt;item&gt;Yellow&lt;\/item&gt;\r\n        &lt;item&gt;Green&lt;\/item&gt;\r\n        &lt;item&gt;Blue&lt;\/item&gt;\r\n        &lt;item&gt;Purple&lt;\/item&gt;\r\n        &lt;item&gt;Violet&lt;\/item&gt;\r\n        &lt;item&gt;Octarine&lt;\/item&gt;\r\n    &lt;\/string-array&gt;\r\n&lt;\/resources&gt;\r\n<\/pre>\n<pre class=\"brush: xml; title: strings; notranslate\" title=\"strings\">\r\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\r\n&lt;resources&gt;\r\n    &lt;string name=&quot;app_name&quot;&gt;Color Chooser&lt;\/string&gt;\r\n    &lt;string name=&quot;prompt&quot;&gt;Choose a color:&lt;\/string&gt;\r\n&lt;\/resources&gt;\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>On older versions of AndroidOS, there&#8217;s a bug that causes the combination of SimpleAdapter and Spinner to fail when displaying text data. There&#8217;s also a workaround: Add a ViewBinder to the SimpleAdapter. This is simpler than it sounds.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[33],"tags":[51,38,37,39],"_links":{"self":[{"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/posts\/671"}],"collection":[{"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/comments?post=671"}],"version-history":[{"count":7,"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/posts\/671\/revisions"}],"predecessor-version":[{"id":678,"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/posts\/671\/revisions\/678"}],"wp:attachment":[{"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/media?parent=671"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/categories?post=671"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.outofwhatbox.com\/blog\/wp-json\/wp\/v2\/tags?post=671"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}