3

The PreferenceScreen isn't good enough for me, since I've to add items to a Spinner. Those items need to come from a data list.

I've got a custom ArrayAdapter that returns the name of the item, and when I click it. It returns the data that is contained within the item.

I want to use that same ArrayAdapter in a ListPreference (that's the spinner in the PreferenceScreen) but the ListPreference doesn't allow me to use a Adapter.

So, I want to recreate the look of the PreferenceScreen (with the PreferenceCategory's) without the use of the actual PreferenceScreen (and PreferenceCategory's)

Is this possible with a library? I haven't found one.

Thanks,

Tim

tim687
  • 2,256
  • 2
  • 17
  • 28
  • `Is this possible with a library?` Well, it is possible **without**. But you have to recreate the layout and the undelying logic and use the SharedPreferences programmatically. – Phantômaxx Mar 31 '15 at 08:45
  • @DerGolem That's no problem, but I'm searching for somebody who already has recreated the layout. Or the library – tim687 Mar 31 '15 at 09:08
  • I did. **Without** any library. A kickstart: http://stackoverflow.com/a/6194194/2649012 – Phantômaxx Mar 31 '15 at 09:11
  • @DerGolem That's funny, I've just added the same piece of code. Can you tell me how did you find the @+android:id/summary and @+android:id/widget_frame in the layout? It throws errors at me here – tim687 Mar 31 '15 at 09:20
  • @DerGolem Can you post some code? That will be very useful to all the visitors that will visit this question later on. Thanks! – tim687 Mar 31 '15 at 09:35
  • It's long. Allow me some time to compose it all in an answer... – Phantômaxx Mar 31 '15 at 09:43

2 Answers2

1

I tried to collect my first method - I hope I didn't forget to include some parts (aapart color definitions or statelist drawables, which is a trivial task to make your own)

Customizing the standard Preferences

/res/xml/prefs.xml

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

    <!-- ... -->

    <PreferenceCategory android:title="@string/pref_vibrate_cat">
        <CheckBoxPreference
            android:persistent="true"
            android:key="vibrate"
            android:title="@string/pref_vibrate_title"
            android:summary="@string/pref_vibrate_summ"
            android:defaultValue="true"
            android:layout="@layout/prefs"
        />
    </PreferenceCategory>

    <!-- ... -->

    <!-- Just to show how to use a custom preference (you must have the corresponding java Class in your project) -->
    <PreferenceCategory android:title="@string/pref_tts_cat">
        <com.dergolem.abc.CLS_Prefs_Multi
            android:persistent="true"
            android:key="tts"
            android:title="@string/pref_tts_title"
            android:summary="@string/nothing"
            android:dialogTitle="@string/pref_tts_dlg"
            android:dialogIcon="@android:drawable/sym_action_chat"
            android:entries="@array/prefs_tts_titles"
            android:entryValues="@array/prefs_tts_values"
            android:defaultValue="@array/prefs_tts_defaults"
            android:layout="@layout/prefs"
            android:widgetLayout="@layout/arr_dn"
        />
    </PreferenceCategory>

    <!-- ... -->

</PreferenceScreen>

/res/layout/prefs.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- Layout for a visually child-like Preference in a PreferenceActivity. -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:baselineAligned="false"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?android:attr/listPreferredItemHeight"
    android:gravity="center_vertical"
    android:paddingStart="16dp"
    android:paddingEnd="?android:attr/scrollbarSize"
    >
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:minWidth="16dp"
        android:gravity="center"
        android:orientation="horizontal"
        >
        <ImageView
            android:id="@+android:id/icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
        />
    </LinearLayout>
    <RelativeLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        android:layout_weight="1"
        >
        <TextView
            android:id="@+android:id/displayTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:textStyle="bold"
            android:ellipsize="marquee"
            android:fadingEdge="horizontal"
        />
        <TextView
            android:id="@+android:id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:textStyle="bold"
            android:ellipsize="marquee"
            android:fadingEdge="horizontal"
        />
        <TextView
            android:id="@+android:id/summary"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@android:id/title"
            android:layout_alignStart="@android:id/title"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:textColor="?android:attr/textColorSecondary"
            android:shadowColor="@color/white"
            android:shadowDx="1"
            android:shadowDy="1"
            android:shadowRadius="1"
            android:maxLines="4"
        />
    </RelativeLayout>
    <!-- Preference should place its actual preference widget here. -->
    <LinearLayout
        android:id="@+android:id/widget_frame"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:minWidth="48dp"
        android:gravity="center"
        android:orientation="vertical"
    />
</LinearLayout>

/src/ACT_Prefs

package com.dergolem.abc;

/* ---------------------------------- Imports ------------------------------- */

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
import android.widget.ListView;

public final class ACT_Prefs // NO_UCD (use default)
extends PreferenceActivity
implements OnSharedPreferenceChangeListener
{
    /* ------------------------------ Objects ------------------------------- */

    private Context ctx = null;

    /* ----------------------------- Overrides ------------------------------ */

    // Reload the Activity on rotation.
    @Override
    public final void onConfigurationChanged(final Configuration cfg)
    {
        super.onConfigurationChanged(cfg);
        reStart();
    }
    /*
    Load the Preference Activity if the API LEvel is less than 11 or else load
    the PreferenceFragment.
    Needed workaround, since unfortunately Google didn't include the
    PreferenceFragment in the support library
    */
    @Override
    public final void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        ctx = getApplicationContext();

        if (Build.VERSION.SDK_INT < 11)
        {
            createPreference_Activity();
        }
        else
        {
            createPreference_Fragment();
        }
    }
    @Override
    protected void onPause()
    {
        // Unregister OnSharedPreferenceChangeListener
        PreferenceManager.getDefaultSharedPreferences(ctx).
        unregisterOnSharedPreferenceChangeListener(this);

        // Call the base method
        super.onPause();
    }
    @Override
    protected void onResume()
    {
        // Register OnSharedPreferenceChangeListener
        PreferenceManager.getDefaultSharedPreferences(ctx).
        registerOnSharedPreferenceChangeListener(this);

        // Fire the base method
        super.onResume();
    }

    /* ------------------------------ Methods ------------------------------- */

    @SuppressWarnings("deprecation")
    private final void createPreference_Activity()
    {
        // Set the Activity layout
        addPreferencesFromResource(R.xml.prefs);

        // Get the PreferenceScreen ListView
        final ListView lvw = getListView();

        // Set the horizontal separator
        lvw.setDivider(getResources().getDrawable(R.drawable.list_divider));
        lvw.setDividerHeight((1));

        // Set the statelist selector
        lvw.setSelector(R.drawable.list_item_colors);

        // Remove the top and bottom fadings
        lvw.setVerticalFadingEdgeEnabled(false);
    }
    @SuppressLint("NewApi")
    private final void createPreference_Fragment()
    {
        // Create the fragment.
        getFragmentManager().beginTransaction().replace
            (android.R.id.content, new FRG_Prefs()).commit();
        getFragmentManager().executePendingTransactions();
    }
}

/src/FRG_Prefs

package com.dergolem.abc;

/* ---------------------------------- Imports ------------------------------- */

import android.annotation.SuppressLint;
import android.graphics.PixelFormat;
import android.preference.PreferenceFragment;
import android.view.View;
import android.widget.ListView;

@SuppressLint("NewApi")
public final class FRG_Prefs
extends PreferenceFragment
{
    /* ----------------------------- Overrides ------------------------------ */

    @Override
    public final void onResume()
    {
        super.onResume();
        addPreferencesFromResource(R.xml.prefs);

        init();
    }
    @Override
    public final void onStop()
    {
        super.onStop();
        // Kill the prefence screen, so that it won't be recreated DUPLICATE.
        // HORRIBLE, but it's the only way to avoid the PreferenceScreen copycat.
        getActivity().finish();
    }

    /* ------------------------------ Methods ------------------------------- */

    private final void init()
    {
        final View v = getView();

        v.setPadding(paddingSize, 0, paddingSize, 0);

        // Get the PreferenceScreen ListView
        final ListView lvw = (ListView) v.findViewById(android.R.id.list);

        // Set the horizontal separator
        lvw.setDivider(getResources().getDrawable(R.drawable.list_divider));
        lvw.setDividerHeight((1));

        // Set the state selector
        lvw.setSelector(R.drawable.list_item_colors);

        // Remove top and bottom fading
        lvw.setVerticalFadingEdgeEnabled(false);
    }
}

To show my Preferences:

startActivity(new Intent(ctx, ACT_Prefs.class));

ctx is defined as

Context ctx = getApplicationContext();

since I use it a lot, I define it once and for all.

[EDIT]

By request, I could add a method to make a Fake PreferenceScreen.

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
  • I need to put the views I want to display in my PreferenceScreen in the layout/prefs.xml? – tim687 Mar 31 '15 at 10:11
  • No, in the `/res/xml/prefs.xml`, like the CheckBoxPreference. The `/res/layout/prefs.xml` is the template for those preference Views. – Phantômaxx Mar 31 '15 at 10:13
  • How can I add a Spinner to the settings layout? – tim687 Mar 31 '15 at 10:14
  • I also use some **non standard** preferences. Such as Multiselection ListViews (non standard for lower API Levels), DatePickers, TimePickers, ListViews with icons, ... – Phantômaxx Mar 31 '15 at 10:17
  • How do you add those to your layout? – tim687 Mar 31 '15 at 10:18
  • Edited the PreferenceScreen xml to show you how to include a custom Preference Class (You need to have that Class in your project). – Phantômaxx Mar 31 '15 at 10:24
  • And the CLS_Prefs_Multi can be any view I like? Must it extend the Preference class? – tim687 Mar 31 '15 at 10:30
  • NO. It's a **custom** class. It's a java file to provide some different functionality. In this particular case it's a ListView which allows you to select / deselect multiple options. – Phantômaxx Mar 31 '15 at 10:32
  • Can you please update your answer with the code of CLS_Prefs_Multi? I've never used any custom views in my PreferenceScreen or PreferenceActivity – tim687 Mar 31 '15 at 10:34
  • Here is an implementation: http://misha.beshkin.lv/android-multiselectlistpreference-for-sdk-11/ by Misha Beshkin. He modified the same original from Krysztof Suzynski I modified to my needs. – Phantômaxx Mar 31 '15 at 10:45
  • 1
    Thanks! I'll take a look at it and save the code above as a snippet to use it when needed in a next project! – tim687 Mar 31 '15 at 10:46
  • Same goes when you want to use different non standard preferences: Dates, Times, Numbers, ListViews with Icons... just search, and you will find **nearly** everything. Even how to launch an Activity from the Preferences! – Phantômaxx Mar 31 '15 at 10:49
1

The answer above is to difficult to implement, so I've designed my own version.

The layout xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:ads="http://schemas.android.com/apk/res-auto"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:minHeight="?android:attr/listPreferredItemHeight"
    android:gravity="center_vertical">

<include layout="@layout/toolbar"/> <!-- This is a custom toolbar (or actionbar), and not necessary -->

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_below="@+id/toolbar"
        android:paddingRight="?android:attr/scrollbarSize">

        <ScrollView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_below="@layout/toolbar"
            android:id="@+id/scrollView" >
            <LinearLayout
                android:orientation="vertical"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textAppearance="?android:attr/textAppearanceSmall"
                    android:text="@string/category_battery"
                    android:id="@+id/category_misc"
                    android:layout_marginLeft="@dimen/activity_settings_header_margin" />

                <ImageView
                    android:layout_width="fill_parent"
                    android:layout_height="2dp"
                    android:id="@+id/divider"
                    android:layout_marginLeft="@dimen/activity_settings_margin"
                    android:layout_below="@+id/category_misc"
                    android:contentDescription="divider"
                    android:scaleType="matrix"
                    android:background="@android:drawable/divider_horizontal_bright"
                    android:src="@android:drawable/divider_horizontal_bright" />

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical"
                    android:padding="@dimen/activity_settings_margin">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:id="@+id/textView"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:id="@+id/textView"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:id="@+id/textView"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:id="@+id/textView"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:id="@+id/textView"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:id="@+id/textView"/>

                </LinearLayout>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@string/category_calibration"
                    android:textAppearance="?android:attr/textAppearanceSmall"
                    android:id="@+id/category_subjects"
                    android:layout_marginLeft="@dimen/activity_settings_header_margin"
                    android:layout_below="@+id/batteryChargeState" />

                <ImageView
                    android:layout_width="fill_parent"
                    android:layout_height="2dp"
                    android:id="@+id/divider2"
                    android:layout_marginLeft="@dimen/activity_settings_margin"
                    android:layout_below="@+id/category_subjects"
                    android:contentDescription="divider"
                    android:scaleType="matrix"
                    android:background="@android:drawable/divider_horizontal_bright"
                    android:src="@android:drawable/divider_horizontal_bright" />



                <LinearLayout android:layout_width="match_parent"
                    android:layout_below="@+id/category_subjects"
                    android:layout_centerVertical="true"
                    android:layout_height="match_parent"
                    android:padding="@dimen/activity_settings_margin"
                    android:orientation="vertical"
                    android:id="@+id/nextLayout">


                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:id="@+id/textView"/>


                </LinearLayout>

            </LinearLayout>

        </ScrollView>
    </RelativeLayout>


</RelativeLayout>

Toolbar xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/toolbar"
    app:theme="@style/ThemeOverlay.AppCompat.ActionBar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?attr/actionBarSize"
    android:background="?attr/colorPrimary"/>

Dimens xml:

<resources>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>

    <dimen name="activity_settings_margin">24dp</dimen>
    <dimen name="activity_settings_header_margin">18dp</dimen>
</resources>

Colors xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="orange">#FDA432</color>
    <color name="orange_dark">#ffd17731</color>

</resources>

Just use the your way to store the Preferences. I've created a custom preference class that contains private keys so I can't post the code here without breaking it.

The advantage of using a custom layout like this is that you can add your own toolbar with this line as the first element of the first RelativeLayout.

To use the custom toolbar use this piece of code in your onCreate()

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

                mTitle = mDrawerTitle = getTitle(); // This is for the title when you use a drawer

                mToolbar = (Toolbar) findViewById(R.id.toolbar); // This finds the toolbar you've specified using the <include> in the xml

                setSupportActionBar(mToolbar); // This sets the toolbar to be used

                mToolbar.setBackgroundColor(getResources().getColor(R.color.orange)); // This sets the color of the toolbar

                if (Build.VERSION.SDK_INT >= 21) {
                    getWindow().setStatusBarColor(getResources().getColor(R.color.orange_dark)); // This sets the color of the navigation bar to a darker orange as used for the toolbar, only when this is supported!
                }

                mToolbar.setNavigationIcon(R.mipmap.ic_launcher); // This makes the icon clickable, to open and close a drawer if you have one
    }
tim687
  • 2,256
  • 2
  • 17
  • 28