Tuesday, November 14, 2017

Android Settings Series (part 2)

Welcome to this series post.
In this series post, we are going to create a complete Settings for a productive app.

We already published a post-
Now I am starting where I left in the previous post.

Now we need to create other 2 XML file. (In our final settings we need 3 XML file, we already have done one.)

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

    <Preference
        android:key="@string/bps_ex_fav_key"
        android:summary="@string/bps_ex_fav_summery"
        android:title="@string/bps_ex_fav_title" />

    <Preference
        android:key="@string/bps_im_fav_key"
        android:summary="@string/bps_im_fav_summery"
        android:title="@string/bps_im_fav_title" />

    <Preference
        android:key="@string/bps_ex_add_key"
        android:summary="@string/bps_ex_add_summery"
        android:title="@string/bps_ex_add_title" />

    <Preference
        android:key="@string/bps_im_add_key"
        android:summary="@string/bps_im_add_summery"
        android:title="@string/bps_im_add_title" />

</PreferenceScreen>

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

    <CheckBoxPreference
        android:defaultValue="false"
        android:key="@string/switchKey"
        android:summaryOff="@string/switchOFF"
        android:summaryOn="@string/switchON"
        android:title="@string/switchLabel"/>

</PreferenceScreen>

we need to modify activity_sttings.xml
activity_sttings.xml
<!--
<fragment
     android:layout_marginTop="?attr/actionBarSize"
     android:id="@+id/settings_fragment"
  android:name="com.blogspot.shudiptotrafder.soilscience.settings.SettingsFragment"
     android:layout_width="match_parent"
     android:layout_height="match_parent" /> -->

<RelativeLayout
    android:layout_marginTop="?attr/actionBarSize"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/settingsContainer" />

that's we finished XML coding

Now time for java code-
create two new java file like SettingsFragment.java
note: I show you how you can do that. I am going throw step by step. But Don't worry I will provide full code for other two class.

AdvacnceSettingsFragment.java
public class AdvanceSettingsFragment extends PreferenceFragmentCompat implements
        SharedPreferences.OnSharedPreferenceChangeListener {

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {

        Preference preference = findPreference(key);


        //if for re queries


        if (preference != null) {

            if (!(preference instanceof CheckBoxPreference)) {
                String value = sharedPreferences.getString(preference.getKey(), "");
                setPreferenceSummery(preference, value);
            }
        }
    }

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {

        // Add 'general' preferences, defined in the XML file
        addPreferencesFromResource(R.xml.pref_advance);

        SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences();

        PreferenceScreen preferenceScreen = getPreferenceScreen();

        int count = preferenceScreen.getPreferenceCount();


        for (int i = 0; i < count; i++) {
            Preference p = preferenceScreen.getPreference(i);
            if (!(p instanceof CheckBoxPreference)) {
                String value = sharedPreferences.getString(p.getKey(), "");
                setPreferenceSummery(p, value);
            }
        }

    }

    private void setPreferenceSummery(Preference preference, Object value) {

        String stringValue = value.toString();

        if (preference instanceof ListPreference) {
            // For list preferences, look up the correct display value in
            // the preference's 'entries' list (since they have separate labels/values).
            ListPreference listPreference = (ListPreference) preference;
            int prefIndex = listPreference.findIndexOfValue(stringValue);
            //same code in one line
            //int prefIndex = ((ListPreference) preference).findIndexOfValue(value);

            //prefIndex must be is equal or garter than zero because
            //array count as 0 to ....
            if (prefIndex >= 0) {
                listPreference.setSummary(listPreference.getEntries()[prefIndex]);
            }
        } else {
            // For other preferences, set the summary to the value's simple string representation.
            preference.setSummary(stringValue);
        }
    }

    //register and unregister on lifecycle
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getPreferenceScreen().getSharedPreferences()
                .registerOnSharedPreferenceChangeListener(this);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        getPreferenceScreen().getSharedPreferences()
                .unregisterOnSharedPreferenceChangeListener(this);
    }

}

now create two variable in this class
private static final String TAG = AdvanceSettingsFragment.class.getName();
public static final String PAGE_ID = "page_id";

now add this method
public static AdvanceSettingsFragment newInstance(String pageId) {
        AdvanceSettingsFragment f = new AdvanceSettingsFragment();
        Bundle args = new Bundle();
        args.putString(PAGE_ID, pageId);
        f.setArguments(args);
        return (f);
}

that it for this class. You can also check git. AdvanceSettingsFragment

Now time for BackupFragment.java
BackupFragment.java
public class BackupSettings extends PreferenceFragmentCompat implements
        SharedPreferences.OnSharedPreferenceChangeListener {

    private static final String TAG = AdvanceSettingsFragment.class.getName();
    public static final String PAGE_ID = "page_id";


    public static BackupSettings newInstance(String pageId) {
        BackupSettings f = new BackupSettings();
        Bundle args = new Bundle();
        args.putString(PAGE_ID, pageId);
        f.setArguments(args);
        return (f);
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {

        Preference preference = findPreference(key);


        if (preference != null) {

            if (!(preference instanceof CheckBoxPreference)) {
                String value = sharedPreferences.getString(preference.getKey(), "");
                setPreferenceSummery(preference, value);
            }
        }
    }


    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {

        // Add 'general' preferences, defined in the XML file
        addPreferencesFromResource(R.xml.pref_backup);

        SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences();

        PreferenceScreen preferenceScreen = getPreferenceScreen();

        int count = preferenceScreen.getPreferenceCount();

        for (int i = 0; i < count; i++) {
            Preference p = preferenceScreen.getPreference(i);
            if (!(p instanceof CheckBoxPreference)) {
                String value = sharedPreferences.getString(p.getKey(), "");
                setPreferenceSummery(p, value);
            }
        }

        //my all preference
        Preference exportFavourite = findPreference(getString(R.string.bps_ex_fav_key));
        Preference importFavourite = findPreference(getString(R.string.bps_im_fav_key));
        Preference exportAddWord = findPreference(getString(R.string.bps_ex_add_key));
        Preference importAddWord = findPreference(getString(R.string.bps_im_add_key));


        //export favourite
        exportFavourite.setOnPreferenceClickListener(preference -> {
        //your code
        });

        //import favourite
        importFavourite.setOnPreferenceClickListener(preference -> {
        //your code
        });

        //export added word
        exportAddWord.setOnPreferenceClickListener(preference -> {
        //you code
        });

        //import added word
        importAddWord.setOnPreferenceClickListener(preference -> {
        //your code
        });

    }



    private void setPreferenceSummery(Preference preference, Object value) {

        String stringValue = value.toString();

        if (preference instanceof ListPreference) {
           
            ListPreference listPreference = (ListPreference) preference;
            int prefIndex = listPreference.findIndexOfValue(stringValue);
            
            if (prefIndex >= 0) {
                listPreference.setSummary(listPreference.getEntries()[prefIndex]);
            }
        } else {
           
            preference.setSummary(stringValue);
        }
    }

    //register and unregister on lifecycle
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        getPreferenceScreen().getSharedPreferences()
                .registerOnSharedPreferenceChangeListener(this);
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        getPreferenceScreen().getSharedPreferences()
                .unregisterOnSharedPreferenceChangeListener(this);
    }
}

now change SettingsFragment.java (that's found in the part1)
SettingsFragment.java
private static final String TAG = SettingsFragment.class.getName();
    public static final String PAGE_ID = "page_id";

    public SettingsFragment newInstance(String pageId) {
        SettingsFragment f = new SettingsFragment();
        Bundle args = new Bundle();
        args.putString(PAGE_ID, pageId);
        f.setArguments(args);
        return f;
    }

add this method and two variable. Are you confused where you need to put this code, you can see this on git SettingsFragment

We are almost last step-
we need to make some changes on Settings Activity-
Steps-
  • implement this "PreferenceFragmentCompat.OnPreferenceStartScreenCallback"
  • add fragment manager and replace the fragment

See the code-
public class SettingsActivity extends AppCompatActivity implements
        PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
}

we need to implement this method-
@Override
    public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, PreferenceScreen preferenceScreen) {

        return false;
}

we do this later in this post.
create a new Fragment Manager and make these changes-
FragmentManager fragmentManager = getSupportFragmentManager();

Fragment fragment;

if (savedInstanceState == null) {
   FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
   fragment = new SettingsFragment().newInstance("Advanced_Setting");
   fragmentTransaction.add(R.id.settingsContainer, fragment);
   fragmentTransaction.commit();
}

Now change this method
@Override
    public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, PreferenceScreen preferenceScreen) {
        //Log.d(TAG, "callback called to attach the preference sub screen");
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        AdvanceSettingsFragment fragment = AdvanceSettingsFragment.newInstance("Advanced Settings Subscreen");
        BackupSettings backupSettings = BackupSettings.newInstance("BackUp Settings");
        Bundle args = new Bundle();
        //Defining the sub screen as new root for the  subscreen
        args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, preferenceScreen.getKey());
        fragment.setArguments(args);

        if (preferenceScreen.getKey().equalsIgnoreCase("backup")) {

            ft.replace(R.id.settingsContainer, backupSettings, preferenceScreen.getKey());

        } else {
            ft.replace(R.id.settingsContainer, fragment, preferenceScreen.getKey());
        }

        ft.addToBackStack(null);
        ft.commit();
        return true;
    }

That's it.
we finished the coding of Android Settings with the fragment. In the next parts, we do same this by using 2 activity. As we use single activity on Android App Settings with Preference Fragment Compat this post. We have already one activity and We create two new other activity and do same settings. Until then Happy coding.

Possible Error and Fix:
you can get that class cannot find. If you find this error you probably miss adding preference as in your dependencies
//for settings
compile 'com.android.support:preference-v14:26.1.0'

Or, If your app is Crashed then check the Styles.xml
add this line
<!-- must add this line or app will crash -->
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>

Still not working, please recheck the code
All Java Class:


Layout XML:


Pref XML



Once again Happy coding

About Author:

I am Shudipto Trafder,
Since 2015, I write android code. I love to implement new ideas. I use java and also kotlin. Now I am learning Flutter. I love to learn new things and also love to share. And that is the main reason to run this blog.


Let's Get Connected: Facebook Twitter Linkedin Github

No comments :