{"id":168,"date":"2016-06-23T05:40:46","date_gmt":"2016-06-23T05:40:46","guid":{"rendered":"https:\/\/wp.local\/?p=168"},"modified":"2016-06-23T06:02:02","modified_gmt":"2016-06-23T06:02:02","slug":"multi-language-support-and-spinnerpreference-part-iii","status":"publish","type":"post","link":"https:\/\/blog.c-devtech.net\/?p=168","title":{"rendered":"Multi-Language support and SpinnerPreference &#8211; Part III"},"content":{"rendered":"<p>In <a href=\"https:\/\/wp.local\/?p=163\">Part II<\/a>, the\u00a0<a href=\"https:\/\/developer.android.com\/reference\/android\/preference\/PreferenceFragment.html\" target=\"_blank\">PreferenceFragment<\/a> and the <a href=\"https:\/\/developer.android.com\/reference\/android\/preference\/PreferenceScreen.html\" target=\"_blank\">PreferenceScreen<\/a> were discussed along with the fact that there is a limited number of built-in widgets available for configuring the settings. The one missing that I thought would be most useful for multi-language support was a <strong>SpinnerPreference<\/strong>. Language support in an app can vary over time and the spinner lends itself well to a list that can change without requiring any modifications to the user interface.<\/p>\n<p>A goal in creating the <em>SpinnerPreference<\/em> was to make it&#8217;s interface as similar as possible to the built-in widgets, such as the <a href=\"https:\/\/developer.android.com\/reference\/android\/preference\/CheckBoxPreference.html\" target=\"_blank\">CheckBoxPreference<\/a>. Because of this all the behind the scenes layout in the <em>SpinnerPreference<\/em> is done in java and not xml. In addition, the xml preference settings needed by the <a href=\"https:\/\/developer.android.com\/reference\/android\/preference\/CheckBoxPreference.html\" target=\"_blank\">CheckBoxPreference<\/a> will also be used:<\/p>\n<pre>&lt;com.cdevtech.preference.SpinnerPreference\r\n    android:key=\"pref_language\"\r\n    android:title=\"@string\/pref_change_language_title\"\r\n    android:summary=\"@string\/pref_change_language_summary\"\r\n    android:entries=\"@array\/language_entries\"\r\n    android:entryValues=\"@array\/language_values\"\r\n    android:defaultValue=\"en\"\/&gt;<\/pre>\n<pre>&lt;string-array name=\"language_entries\"&gt;\r\n    &lt;item&gt;English&lt;\/item&gt;\r\n    &lt;item&gt;Spanish&lt;\/item&gt;\r\n&lt;\/string-array&gt;\r\n\r\n&lt;string-array name=\"language_values\"&gt;\r\n    &lt;item&gt;en&lt;\/item&gt;\r\n    &lt;item&gt;es&lt;\/item&gt;\r\n&lt;\/string-array&gt;<\/pre>\n<p><em>SpinnerPreference<\/em> extends <em><strong>DialogPreference <\/strong><\/em>which has a number of methods that need to be overridden in the inherited class. In the constructor, the text for the positive and negative dialog buttons is set along with the retrieval of some of the parameters set in the preferences.xml file.<\/p>\n<pre>public SpinnerPreference(Context context, AttributeSet attrs) {\r\n    super(context, attrs);\r\n\r\n    this.context = context;\r\n\r\n    setPositiveButtonText(context.getString(\r\n            android.R.string.ok));\r\n    setNegativeButtonText(context.getString(\r\n            android.R.string.cancel));\r\n\r\n    int entryValuesId = 0;\r\n    for (int i = 0; i &lt; attrs.getAttributeCount(); i++) {\r\n        if (attrs.getAttributeName(i).\r\n                equalsIgnoreCase(\"entries\")) {\r\n            entriesId = Integer.parseInt(\r\n                    attrs.getAttributeValue(i).substring(1));\r\n        }\r\n        if (attrs.getAttributeName(i).\r\n                equalsIgnoreCase(\"entryValues\")) {\r\n            entryValuesId = Integer.parseInt(\r\n                    attrs.getAttributeValue(i).substring(1));\r\n        }\r\n    }\r\n\r\n    if (entryValuesId != 0) {\r\n        String[] entryValues = context.getResources().\r\n                getStringArray(entryValuesId);\r\n\r\n        \/\/ Convert String Array To ArrayList for indexOf capability\r\n        entryValuesList = Arrays.asList(entryValues);\r\n    }\r\n}<\/pre>\n<p>As mentioned, instead of using <em>setDialogLayoutResource<\/em> in the constructor to specify the layout, <strong>onCreateDialogView is overrriden<\/strong> and the <em>View <\/em>to display in the dialog is generated there using the values <em>context<\/em>, <em>entriesId<\/em> which were set in the constructor.<\/p>\n<pre>@override\r\nprotected View onCreateDialogView() {\r\n    LinearLayout layout = new LinearLayout(context);\r\n    layout.setOrientation(LinearLayout.VERTICAL);\r\n    layout.setLayoutParams(new LinearLayout.LayoutParams(\r\n            LinearLayout.LayoutParams.MATCH_PARENT,\r\n            LinearLayout.LayoutParams.MATCH_PARENT));\r\n    layout.setPadding(10, 10, 10, 10);\r\n\r\n    spinner = new AppCompatSpinner(context);\r\n    spinner.setLayoutParams(new LinearLayout.LayoutParams(\r\n            LinearLayout.LayoutParams.MATCH_PARENT,\r\n            LinearLayout.LayoutParams.MATCH_PARENT));\r\n    spinner.setPadding(10, 10, 10, 10);\r\n\r\n    if (entriesId != 0) {\r\n        ArrayAdapter&lt;String&gt; spinnerArrayAdapter =\r\n          new ArrayAdapter&lt;String&gt;(\r\n                  context,\r\n                  android.R.layout.simple_spinner_dropdown_item,\r\n                  context.getResources().getStringArray(entriesId));\r\n        spinner.setAdapter(spinnerArrayAdapter);\r\n    }\r\n\r\n    layout.addView(spinner);\r\n\r\n    return layout;\r\n}<\/pre>\n<p><strong>Override onBindDialogView<\/strong> to bind data to the content views:<\/p>\n<pre>@Override\r\nprotected void onBindDialogView(View view) {\r\n    super.onBindDialogView(view);\r\n\r\n    \/\/ Set default\/current\/selected value if set\r\n    if (value != null) {\r\n        spinner.setSelection(value);\r\n    }\r\n}<\/pre>\n<p><strong>Override onDialogClosed<\/strong> which is called when the dialog is closed. If the positive button was clicked then persist the data (save in <em>SharedPreferences <\/em>by calling <em>persistString<\/em>:<\/p>\n<pre>@Override\r\nprotected void onDialogClosed(boolean positiveResult) {\r\n    try {\r\n        if (positiveResult) {\r\n            value = spinner.getSelectedItemPosition();\r\n\r\n            String entryValue = entryValuesList.get(value);\r\n            if (callChangeListener(entryValue)) {\r\n                persistString(entryValue);\r\n            }\r\n        }\r\n    } catch (Exception e) {\r\n        e.printStackTrace();\r\n    }\r\n}<\/pre>\n<p><strong>Override onSetInitialValue<\/strong> to<em> s<\/em>et the initial value of the preference. It is called when the preference object is added to the screen. If <em>restorePersistedValue <\/em>is true, the preference value should be restored from the <em>SharedPreferences <\/em>else the preference value should be set to the <em>defaultValue\u00a0<\/em> passed and it should also be persisted (saved).<em><br \/>\n<\/em><\/p>\n<p><em>restorePersistedValue<\/em> will generally be false when you&#8217;ve specified <strong>android:defaultValue<\/strong> that calls <em>onGetDefaultValue<\/em> (check below) and that in turn returns a value which is passed as the <em>defaultValue<\/em> to <em>onSetInitialValue<\/em>.<\/p>\n<pre>@Override\r\nprotected void onSetInitialValue(boolean restorePersistedValue, \r\n                                 Object defaultValue) {\r\n    try {\r\n        if (restorePersistedValue) {\r\n            if (defaultValue == null) {\r\n                value = entryValuesList.indexOf(\r\n                        getPersistedString(entryValuesList.get(0)));\r\n            } else {\r\n                value = entryValuesList.indexOf(\r\n                        getPersistedString((String) defaultValue));\r\n            }\r\n        } else {\r\n            value = entryValuesList.indexOf((String) defaultValue);\r\n            persistString((String) defaultValue);\r\n        }\r\n    } catch (Exception e) {\r\n        e.printStackTrace();\r\n    }\r\n}<\/pre>\n<p><strong>Override onGetDefaultValue <\/strong>which is called when you set <strong>android:defaultValue<\/strong> . Just in case the value is undefined, you can return DEFAULT_VALUE so that it gets passed to <em>onSetInitialValue<\/em> that gets saved in <em>SharedPreferences<\/em>.<\/p>\n<pre>@Override\r\nprotected Object onGetDefaultValue(TypedArray a, int index) {\r\n    return a.getString(index);\r\n}<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>In Part II, the\u00a0PreferenceFragment and the PreferenceScreen were discussed along with the fact that there is a limited number of built-in widgets available for configuring the settings. The one missing that I thought would be most useful for multi-language support &hellip; <a href=\"https:\/\/blog.c-devtech.net\/?p=168\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_hide_featured_image":false,"_jetpack_featured_image_duplicate_dismissed":0,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[8],"tags":[],"class_list":["post-168","post","type-post","status-publish","format-standard","hentry","category-android-app-develoment"],"jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p1IEK3-2I","jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/blog.c-devtech.net\/index.php?rest_route=\/wp\/v2\/posts\/168","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.c-devtech.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.c-devtech.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.c-devtech.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.c-devtech.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=168"}],"version-history":[{"count":6,"href":"https:\/\/blog.c-devtech.net\/index.php?rest_route=\/wp\/v2\/posts\/168\/revisions"}],"predecessor-version":[{"id":179,"href":"https:\/\/blog.c-devtech.net\/index.php?rest_route=\/wp\/v2\/posts\/168\/revisions\/179"}],"wp:attachment":[{"href":"https:\/\/blog.c-devtech.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=168"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.c-devtech.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=168"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.c-devtech.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=168"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}