Preference Screen Custom Layout only works with second click
I have a preference screen using a 'custom layout':
android:layout="@layout/currencycodes"
This issue is it fails to bring up the layout on the first attempt. As you see from the animated gif below, I have to retreat and try again a second time for the layout to come up. Now why is that?
preferences.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:key="application_preferences">
<PreferenceCategory>
<PreferenceScreen
android:key="url_settings"
android:title="@string/url_settings"
android:summary="@string/summary_url_settings">
</PreferenceScreen>
<PreferenceScreen
android:key="currency_codes"
android:title="@string/left_handed"
android:summary="@string/summary_left_handed">
<Preference
android:key="currency_exchanges"
android:layout="@layout/currencycodes"/>
</PreferenceScreen>
<EditTextPreference
android:key="url_exchange_rate"
android:title="@string/url_exchange_rate_settings"
android:summary="@string/summary_url_exchange_rate"
android:enabled = "false"/>
</PreferenceCategory>
</PreferenceScreen>
What follows is the custom layout used. It contains a GridView.
currencycodes.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true"
android:focusableInTouchMode="true"
tools:context=".MainActivity">
<GridView
android:layout_width="328dp"
android:layout_height="479dp"
android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent" android:layout_marginStart="16dp"
app:layout_constraintLeft_toLeftOf="parent" android:layout_marginEnd="16dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent" android:layout_marginBottom="16dp"
android:id="@+id/grdExchanges" android:scrollbars="horizontal|vertical"
android:smoothScrollbar="true" tools:textAlignment="center"
android:verticalScrollbarPosition="defaultPosition"/>
</android.support.constraint.ConstraintLayout>
Solution 1:
Well first of all you need to understand how PreferenceScreen Works??
From [Android Documentation][1]:
When it appears inside another preference hierarchy, it is shown and serves as the gateway to another screen of preferences (either by showing another screen of preferences as a Dialog or via a startActivity(android.content.Intent) from the getIntent()). The children of this PreferenceScreen are NOT shown in the screen that this PreferenceScreen is shown in. Instead, a separate screen will be shown when this preference is clicked.
In Simple Words,
The preference framework handles showing these other screens from the preference hierarchy. This PreferenceScreen tag serves as a screen break (similar to page break in word processing).Like for other preference types, we assign a key here so it is able to save and restore its instance state.
In your case ,what actually happening is "application_preferences" will be used as the root of the hierarchy and given to a PreferenceActivity.The first screen will show preferences "Url_Setting" and "Currency Codes". The "Currency Codes" is the "second_preferencescreen" and when clicked will show another screen of preferences which is "Currency Exchange" (as it preferences as children of the "second_preferencescreen" tag i.e. Currency Codes).
So ,That why its not showing on first click....but on second click hope you got how preference Screen works...
If you still have any doubts...let me know... https://developer.android.com/reference/android/preference/PreferenceScreen.html
Solution 2:
try changing your if
statement from
(!mFragMngr.popBackStackImmediate(fragName, 0)
&& mFragMngr.findFragmentByTag(fragName) == null)
to
(mFragMngr.findFragmentByTag(fragName) == null)
Solution 3:
I came up with a workaround. I noted when inflating a layout in LayoutInflater.java, a static hashmap is used to collect view objects:
private static final HashMap<String, Constructor<? extends View>> sConstructorMap
What's happening is that the first click calls the LayoutInflater, but that first click only adds the gridview view in the static hashmap. You'll see that, with the first click, the command, sConstructorMap.get(name)
, returns only null:
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
Constructor<? extends View> constructor = sConstructorMap.get(name);
Class<? extends View> clazz = null;
try {
if (constructor == null) {
In the LayoutInflater, the variable, mPrivateFactory, is in fact your Main Activity. It is there my gridview would be initialized in mPrivateFactory.onCreateView(). However, with the first click, mPrivateFactory.onCreateView() returns null because the static hashmap doesn't have the gridview's view....yet. It's further below in the command line, onCreateView(parent, name, attrs);
where it is finally added:
if (view == null && mPrivateFactory != null) {
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs); <-- ADD TO STATIC HASHMAP
} else {
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
So until I came up with a more elegant solution, I merely call the method, onCreateView(parent)
, twice the first time that particular preference is clicked on. The first call adds the gridview to the static hashmap, the second call then instantiates the layout view (again) but, this time, initializes the gridview in my own Activity's own onCreateView method, mPrivateFactory.onCreateView()
:
protected View onCreateView(ViewGroup parent) {
View layout = super.onCreateView(parent);
if(firstCall){
firstCall = false;
layout = super.onCreateView(parent);
}
return layout;
}