Toolbar is hidden in nested PreferenceScreen
S

4

17

I use PreferenceFragment in ActionBarActivity from support-v7 library.
In the Activity I have Toolbar. Everything goes okay, until I open a nested PreferenceScreen.
In the opened screen the Toolbar is hidden.

Maybe somebody know a workaround for this issue?

Preferences xml-file:

<?xml version="1.0" encoding="utf-8"?>
    <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >

    <PreferenceCategory android:title="Main category" >

        <EditTextPreference
            android:defaultValue="defaultValue"
            android:key="key_global_setting"
            android:title="Global title" />        

    </PreferenceCategory>

    <PreferenceCategory android:title="Nested screens" >        

        <PreferenceScreen
            android:persistent="false"
            android:title="@string/settings_facility_title" >

        <CheckBoxPreference
            android:defaultValue="false"
            android:key="nested_screen_1_1"
            android:title="Nested screen 1.1 check box" />

        <CheckBoxPreference
            android:defaultValue="true"
            android:key="nested_screen_1_2"
            android:title="Nested screen 1.2 check box" />
        </PreferenceScreen>

        <PreferenceScreen
            android:persistent="false"
            android:title="@string/settings_menu_screen_title" >

         <CheckBoxPreference
            android:defaultValue="true"
            android:key="nested_screen2"
            android:title="Nested screen 2 check box" />
        </PreferenceScreen>        

    </PreferenceCategory>    

</PreferenceScreen> 

Activity layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"    
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".SettingsScreen" >

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        style="@style/Toolbar" />

    <FrameLayout
        android:id="@+id/contentSettings"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>
Skiff answered 9/1, 2015 at 14:4 Comment(1)
https://mcmap.net/q/175725/-no-actionbar-in-preferenceactivity-after-upgrade-to-support-library-v21 This answer has a perfect solution for Support LibraryJael
S
27

I found the solution on my own. I used a small work-around of all this nested PreferenceScreen's. I simply made a separation to different xml-preference files, created an additional Fragment which extends PreferenceFragment and there I show an appropriate nested preference screen.
Maybe somebody would found this useful.

Github sources link.

Some code examples below:

main_preferences.xml

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >

    <PreferenceCategory android:title="Main category" >

        <EditTextPreference
            android:defaultValue="defaultValue"
            android:key="key_global_setting"
            android:title="Global title" />

    </PreferenceCategory>

    <PreferenceCategory android:title="Nested screens" >

        <Preference
            android:key="NESTED_KEY1"
            android:persistent="false"
            android:title="Nested screen #1" />

        <Preference
            android:key="NESTED_KEY2"
            android:persistent="false"
            android:title="Nested screen #2" />

    </PreferenceCategory>

</PreferenceScreen> 

nested_screen1_preferences.xml

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
    android:title="Nested screen #1" >

    <CheckBoxPreference
        android:defaultValue="false"
        android:key="nested_screen_1_1"
        android:title="Nested screen 1.1 check box" />

    <CheckBoxPreference
        android:defaultValue="true"
        android:key="nested_screen_1_2"
        android:title="Nested screen 1.2 check box" />
</PreferenceScreen>

nested_screen2_preferences.xml

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
    android:title="Nested screen #2">

    <CheckBoxPreference
        android:defaultValue="true"
        android:key="nested_screen2"
        android:title="Nested screen 2 check box" />
</PreferenceScreen>

SettingsActivity.java

public class SettingsActivity extends ActionBarActivity implements MyPreferenceFragment.Callback {

    private static final String TAG_NESTED = "TAG_NESTED";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_settings);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        if (toolbar != null) {
            setSupportActionBar(toolbar);
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        }

        if (savedInstanceState == null) {
            getFragmentManager().beginTransaction()
                    .add(R.id.contentSettings, new MyPreferenceFragment())
                    .commit();
        }
    }

    @Override
    public void onBackPressed() {
        // this if statement is necessary to navigate through nested and main fragments
        if (getFragmentManager().getBackStackEntryCount() == 0) {
            super.onBackPressed();
        } else {
            getFragmentManager().popBackStack();
        }
    }

    @Override
    public void onNestedPreferenceSelected(int key) {
        getFragmentManager().beginTransaction().replace(R.id.contentSettings, NestedPreferenceFragment.newInstance(key), TAG_NESTED).addToBackStack(TAG_NESTED).commit();
    }    
}

MyPreferenceFragment.java

// The main preference fragment class
public class MyPreferenceFragment extends PreferenceFragment implements Preference.OnPreferenceClickListener {

    private Callback mCallback;

    private static final String KEY_1 = "NESTED_KEY1";
    private static final String KEY_2 = "NESTED_KEY2";

    @Override
    public void onAttach(Activity activity) {

        super.onAttach(activity);

        if (activity instanceof Callback) {
            mCallback = (Callback) activity;
        } else {
            throw new IllegalStateException("Owner must implement Callback interface");
        }
    }
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.main_preferences);

        // add listeners for non-default actions
        Preference preference = findPreference(KEY_1);
        preference.setOnPreferenceClickListener(this);

        preference = findPreference(KEY_2);
        preference.setOnPreferenceClickListener(this);
    }

    @Override
    public boolean onPreferenceClick(Preference preference) {
        // here you should use the same keys as you used in the xml-file
        if (preference.getKey().equals(KEY_1)) {
            mCallback.onNestedPreferenceSelected(NestedPreferenceFragment.NESTED_SCREEN_1_KEY);
        }

        if (preference.getKey().equals(KEY_2)) {
            mCallback.onNestedPreferenceSelected(NestedPreferenceFragment.NESTED_SCREEN_2_KEY);
        }

        return false;
    }

    public interface Callback {
        public void onNestedPreferenceSelected(int key);
    }
}

NestedPreferencesFragment.java

public class NestedPreferenceFragment extends PreferenceFragment {

    public static final int NESTED_SCREEN_1_KEY = 1;
    public static final int NESTED_SCREEN_2_KEY = 2;

    private static final String TAG_KEY = "NESTED_KEY";

    public static NestedPreferenceFragment newInstance(int key) {
        NestedPreferenceFragment fragment = new NestedPreferenceFragment();
        // supply arguments to bundle.
        Bundle args = new Bundle();
        args.putInt(TAG_KEY, key);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        checkPreferenceResource();
    }

    private void checkPreferenceResource() {
        int key = getArguments().getInt(TAG_KEY);
        // Load the preferences from an XML resource
        switch (key) {
            case NESTED_SCREEN_1_KEY:
                addPreferencesFromResource(R.xml.nested_screen1_preferences);
                break;

            case NESTED_SCREEN_2_KEY:
                addPreferencesFromResource(R.xml.nested_screen2_preferences);
                break;

            default:
                break;
        }
    }

}
Skiff answered 14/1, 2015 at 11:21 Comment(4)
I was checking in this issue recently. Your solution is nice but you can only use 1 level of nesting, right? Any ideas of extending this to use any number of nested preference screens? I saw a workaround of making the Toolbar as a custom preference which it works ok but it seems very hacky to me.Applaud
Yes, with current solution you can have only 1 level of nesting. Anyway, my solution is based on manipulation with fragment's back-stack. I think it is very possible to add extra fragments into current back stack and operate the logic to create as many nested prefs as you wish.Skiff
This does not seem to work for me, all the preference elements are showing in the same screen.Cohette
Just a little question, how can I animate the Nested Fragment with a Slide(Gravity.TOP) animation?Vergeboard
U
7

Here comes my solution, which is inspired by the original answer but not that complicated. Maybe it'll help someone...

layout/settings.xml:

    <RelativeLayout 
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <include
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            layout="@layout/toolbar" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/content"
            android:layout_below="@+id/toolbar"/>

    </RelativeLayout>

Classes:

public class SettingsActivity extends ActionBarActivity {
  @Override
  protected void onCreate( Bundle savedInstanceState ) {
    setContentView( R.layout.settings );
    super.onCreate( savedInstanceState ); 
    initializeSupportActionBar();
    getFragmentManager().beginTransaction().replace( R.id.content, new MainFragment() ).commit();
  }

  @Override
  public void onBackPressed() {
    if( !getFragmentManager().popBackStackImmediate() ) super.onBackPressed();
  }
}

public class MainFragment extends PreferenceFragment {

  public MainFragment() {}

  @Override
  public void onCreate( Bundle savedInstanceState ) {
    super.onCreate( savedInstanceState );
    addPreferencesFromResource( R.xml.pref_main );
    // "nested" is the <Preference android:key="nested" android:persistent="false"/>`
    findPreference( "nested" ).setOnPreferenceClickListener( new OnPreferenceClickListener() {
      @Override public boolean onPreferenceClick( Preference preference ) {
        getFragmentManager().beginTransaction().replace( R.id.content, new NestedFragment() ).addToBackStack( NestedFragment.class.getSimpleName() ).commit();
        return true;
      }
    } );
  }

public class NestedFragment extends PreferenceFragment {
  ...
}

I tested it on 4.3 and 5.0.2 and no limitation on nesting levels applies

Urga answered 11/8, 2015 at 14:24 Comment(0)
C
0

In my solution you only need one AppCompatActivity and one PreferenceFragement, but several XML files, each having only one level of PreferenceScreens.

XML file list

  • top level PreferenceScreen
  • second level PreferenceScreen 0
  • second level PreferenceScreen 1
  • second level PreferenceScreen 2
  • ...

This code is for one sub-level (for simplicity and to get the idea), but you can easily extend it to have arbitrary sub-levels of PreferenceScreens.

SettingsFragment.java

public class SettingsFragment extends PreferenceFragment
{
    private int xmlId;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        xmlId = R.xml.preferences;
        addPreferencesFromResource(xmlId);
    }

    public void changePrefScreen(int xmlId, int titleId)
    {
        this.xmlId = xmlId;
        ((AppCompatActivity)getActivity()).getSupportActionBar().setTitle(getActivity().getResources().getString(titleId));
        getPreferenceScreen().removeAll();
        addPreferencesFromResource(xmlId);
    }

    // will be called by SettingsActivity (Host Activity)
    public void onUpButton()
    {
        if(xmlId == R.xml.preferences) // in top-level
        {
            // Switch to MainActivity
            Intent intent = new Intent(getActivity(), MainActivity.class);
            startActivity(intent);
        }
        else // in sub-level
        {
            changePrefScreen(R.xml.preferences, R.string.settings);
        }
    }

    @Override
    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)
    {
        String key = preference.getKey();

        //
        // Top level PreferenceScreen
        //
        if(key.equals("top_key_0"))
        {
            changePrefScreen(R.xml.download_preference_screen, R.string.download_database); // descend into second level
        }

        // ...

        //
        // Second level PreferenceScreens
        //
        if (key.equals("second_level_key_0"))
        {
           // do something...
        }

        // ...
     }

SettingsActivity.java

public class SettingsActivity extends AppCompatActivity
{
    SettingsFragment settingsFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        settingsFragment = new SettingsFragment();

        // Display the fragment as the main content.
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, settingsFragment)
                .commit();
    }

    //
    // Handle what happens on up button
    //
    @Override
    public boolean onOptionsItemSelected(MenuItem item) 
    {
        switch (item.getItemId()) 
        {
            case android.R.id.home:
                settingsFragment.onUpButton();
                return true;
        }
        return true;
    }

    // ...
}

Technically it should work for all Android versions for which the PreferenceFragment is available.

Crystlecs answered 5/9, 2016 at 11:39 Comment(0)
C
0

As the issue comes from the part that you are still in the same activity/fragment and the nested pref screen is just a dialog you can do the following:

  • You can set preference click listener
  • Get the root view from the dialog: (PreferenceScreen)preference).getDialog().getWindow() .getDecorView().getRootView());

  • Recursively search until find a stub view (there is one, unfortunately I do not know the android.R.id.xxxxx) and set what layout you need as title which will look like the toolbar(You can inflate toolbar):

    private Toolbar toolbar;
    
        public void findViewStub(ViewGroup viewGroup) {
        int childCount = viewGroup.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = viewGroup.getChildAt(i);
            if( childView instanceof ViewStub){
                ((ViewStub)childView).setLayoutResource(R.layout.your_title_layout);
               toolbar =  ((ViewStub)childView).inflate();
            }
            if (childView instanceof ViewGroup) {
                findViewStub((ViewGroup) childView);
            }
        }
    
      }
    
    toolbar.setNavigationIcon();
    toolbar.setNavigationOnClickListener();
    toolbar.setTitle();
    
  • In the layout you can put only a toolbar. And set the back icon. Register for click on it and having reference to the fragment, on click you can dismiss the dialog. You have set title and etc.

Charron answered 24/3, 2017 at 15:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.