Drawn
Drawn

Reputation: 445

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?

enter image description here

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>

Upvotes: 9

Views: 1263

Answers (4)

Benedict Lang
Benedict Lang

Reputation: 175

Had maybe pretty much the same issue. In my detail/subPreferences fragment I just put the preference referenceing into the onCreateView method:

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    setPreferencesFromResource(R.xml.root_preferences, FRAGMENT_TAG);
    super.onViewCreated(view, savedInstanceState);
}

@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
    //setPreferencesFromResource(R.xml.root_preferences, FRAGMENT_TAG);
    Log.d(TAG, "onCreatePreferences of the sub screen " + rootKey);
}

Upvotes: 0

Drawn
Drawn

Reputation: 445

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;
}

Upvotes: 1

PN10
PN10

Reputation: 1958

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

Upvotes: 2

Matthew Shearer
Matthew Shearer

Reputation: 2795

try changing your if statement from

(!mFragMngr.popBackStackImmediate(fragName, 0)
                && mFragMngr.findFragmentByTag(fragName) == null)

to

(mFragMngr.findFragmentByTag(fragName) == null)

Upvotes: 1

Related Questions