111

Is there any way to add a button to the bottom of preferences screen and make them work correct when scrolling?

vlaku
  • 1,534
  • 2
  • 14
  • 27

12 Answers12

251

There is another solution for customizing the appearance of the preferences.

Design a normal XML layout with buttons or whatever you want to add to the standard preferences. Include a ListView in your layout and give it the ID @android:id/list.

Let's say we call the layout file res/layout/main.xml. It could look something like this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">
    <Button android:text="This is a button on top of all preferences."
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    <ListView android:id="@android:id/list"
              android:layout_width="match_parent"
              android:layout_height="wrap_content" />
</LinearLayout>

In your PreferenceActivity, add these two lines to your onCreate:

addPreferencesFromResource(R.xml.preferences);
setContentView(R.layout.main);

The ListView in your layout will then be replaced by the preferences defined the usual way in res/xml/preferences.xml.

Kevin Adesara
  • 3,830
  • 1
  • 17
  • 18
Max
  • 2,574
  • 1
  • 14
  • 3
  • Look at @stealthcopter's answer below to get the buttons below. – Robby Pond Mar 04 '11 at 22:06
  • 10
    Does not work when button is placed below the list view. Does not work when preferences activity is using the dialog theme. – Greg Ennis Nov 06 '11 at 17:30
  • 11
    The latest documentation on using PreferenceActivity at http://developer.android.com/reference/android/preference/PreferenceActivity.html mentions that for all new development PreferenceFragment is the preferred way to implement. It seems that the given solution wont work in such a scenario. – Pankaj Jul 26 '12 at 07:20
  • thankx man, such a easy solution , but took my long time to add View to Preference Screen – Naba Jan 04 '13 at 13:35
  • 4
    It still doesn't scroll with the list. – Sudarshan Bhat Jan 07 '13 at 11:56
  • 7
    I'm a bit baffled by how many votes this answer has since it doesn't actually answer the original question. It doesn't scroll with the list, and it doesn't work when the button is placed at the bottom of the view, as mentioned in comments above. – howettl Jan 21 '13 at 23:21
  • Very clearly described and worked perfectly the first time, thanks! Why people want to hide buttons IN a ListView is beyond me--listen, buttons should be OUTSIDE of a ListView, so that they are *viewable*! – SMBiggs Sep 30 '13 at 16:10
  • 1
    @howettl just change fill_parent to wrap_content in ListView layout_height – Martin Ch Mar 16 '14 at 15:47
  • How would you then programatically access the button / textview? I want to set the text fo the textview. – Zapnologica Nov 06 '14 at 10:46
  • This doesn't seem to be working in Marshmellow. Anyone else seeing this? – Justin Nov 02 '15 at 16:36
  • Updated code is here https://developer.android.com/reference/android/preference/PreferenceActivity.html as Default Shared Preference Setting. – Teekam Jun 07 '17 at 07:09
56

I know this is a bit late, but I just found a solution i like better than Max's praised solution.

You can simply add a footer (or if you like the button to be on top, a header) to the PreferenceActivity's ListView like so:

public class MyActivity extends PreferenceActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);
        ListView v = getListView();
        v.addFooterView(new Button(this));
    }
}

I hope this helps someone.

jpihl
  • 7,941
  • 3
  • 37
  • 50
  • 2
    At least this feels better as Max's solution as it doesn't rely on element id's. Let's see how it behaves in future versions of Android... – Daniel F Sep 16 '12 at 21:27
  • @DanielF. don't cross the bridge till you get to it ;) – jpihl Sep 17 '12 at 06:27
  • 2
    I can't get the `ListView` with PreferenceFragment:( – Michał Klimczak Nov 01 '12 at 18:17
  • @MichałK I'm not at all sure, and I can't try right now as I haven't got the SDK installed, but you could maybe do it like this: ListView v = ((PreferenceActivity)getActivity()).getListView(); – jpihl Nov 01 '12 at 20:23
  • 1
    No, it wouldn't work that way (because `PreferenceFragment` has its own list), but I resolved it by just creating a `Preference` and setting an `Intent` on it. There was no need to create a button (at least in my case) – Michał Klimczak Nov 01 '12 at 20:56
  • 3
    Doesn't work with the PreferenceFragmentCompat, since getListView will actually return a RecyclerView – Mauker Mar 28 '20 at 17:40
11

This example below will render a button at the bottom of the page (in case anybody is still interested).

In case of a LinearLayout you could also apply weights; this is needed because the Listview is set to *fill_parent*. I usually do this by adding *android:layout_weight* 's:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:orientation="vertical">
    <ListView android:id="@android:id/list"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent" android:layout_weight="10"/>
    <Button android:text="This is a button on top of all preferences."
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" android:layout_weight="1"/>
</LinearLayout>

The explanation below isn't propbably 100% but it will help you understand...

+-- View Port (linear layout)
| +-- List View (this is where the preferences will go)
| |
| |
| +--
+--
  +--
  | Button (which was pushed out of view by the fillparent of ListView
  +--

You could also say, because the Button has no weight; the button is rendered at 0dp height.

Now with the layout_weigths added it will lett the button render inview

+-- View Port (linear layout)
| +-- List View (this is where the preferences will go)
| |
| |
| +--
| +--
| | Button (which was pushed out of view by the fillparent of ListView
| +--
+--
Ronnie
  • 852
  • 12
  • 25
7

Actually, there is a solution. Here is a code, i hope, this will be useful for anyone. It looks like 3 options and 2 buttons in the bottom of the screen, independent of screen resolution (was targeted to 240 as lowest)

package com.myapplication.gui;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.view.Display;
import android.view.Gravity;
import android.view.WindowManager;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ScrollView;
import com.myproject.general.HeightListView;

import com.myapplication.R;

public class FilterActivity extends PreferenceActivity {

    private LinearLayout rootView; 
    private LinearLayout buttonView; 
    private Button buttonDone;
    private Button buttonRevert;
    private ListView preferenceView; 
    private LinearLayout gradientView;
    private ScrollView scrollRoot;

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

        Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); 
        int height = display.getHeight();
        int width = height > 240 ? display.getWidth() : display.getWidth() - 4;

        scrollRoot = new ScrollView(this);
        scrollRoot.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));

        rootView = new LinearLayout(this); 
        rootView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); 
        rootView.setOrientation(LinearLayout.VERTICAL);

        buttonView = new LinearLayout(this); 
        buttonView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
        buttonView.setOrientation(LinearLayout.HORIZONTAL);
        buttonView.setGravity(Gravity.BOTTOM);

        gradientView = new LinearLayout(this);
        gradientView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
        gradientView.setOrientation(LinearLayout.HORIZONTAL);
        gradientView.setBackgroundResource(R.drawable.gradient);
        gradientView.setPadding(0, 5, 0, 0);
        gradientView.setBackgroundResource(R.drawable.gradient);

        buttonDone = new Button(this); 
        buttonDone.setText(R.string.filterButton_Done); 
        buttonDone.setLayoutParams(new LayoutParams(width/2, LayoutParams.WRAP_CONTENT));
        gradientView.addView(buttonDone);

        buttonRevert = new Button(this); 
        buttonRevert.setText(R.string.filterButton_Revert);
        buttonRevert.setLayoutParams(new LayoutParams(width/2, LayoutParams.WRAP_CONTENT));
        gradientView.addView(buttonRevert);

        buttonView.addView(gradientView);

        preferenceView = new HeightListView(this); 
        preferenceView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); 
        preferenceView.setId(android.R.id.list); 

        PreferenceScreen screen = createPreferenceHierarchy(); 
        screen.bind(preferenceView); 
        preferenceView.setAdapter(screen.getRootAdapter()); 
        rootView.addView(preferenceView);
        rootView.addView(buttonView);

        if (height > 240) {
            this.setContentView(rootView);
        }
        else {
            scrollRoot.addView(rootView);
            this.setContentView(scrollRoot);
        }

        setPreferenceScreen(screen); 
    } 

    private PreferenceScreen createPreferenceHierarchy() {        
        PreferenceScreen root = getPreferenceManager().createPreferenceScreen(this);

        PreferenceScreen pref1 = getPreferenceManager().createPreferenceScreen(this);
        pref1.setKey("pref1");
        pref1.setTitle("Title");
        pref1.setSummary("Summary");
        root.addPreference(pref1); 

        PreferenceScreen pref2 = getPreferenceManager().createPreferenceScreen(this);
        pref2.setKey("pref2");
        pref2.setTitle("Title");
        pref2.setSummary("Summary");
        root.addPreference(pref2); 

        PreferenceScreen pref3 = getPreferenceManager().createPreferenceScreen(this);
        pref3.setKey("pref3");
        pref3.setTitle("Title");
        pref3.setSummary("Summary");
        root.addPreference(pref3); 

        return root; 
    } 
}
glenneroo
  • 1,908
  • 5
  • 30
  • 49
vlaku
  • 1,534
  • 2
  • 14
  • 27
4

You just need to use PreferenceFragment inside general Activity and add the button into activity layout.

public class SettingActivity extends Activity {

    UserProfileViewModel userProfileViewModel = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_setting);
        getFragmentManager().beginTransaction()
                .replace(R.id.content, new SettingsFragment())
                .commit();

    }

    private class SettingsFragment extends PreferenceFragment {
        public SettingsFragment() {
        }

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

            // Load the preferences from an XML resource
            addPreferencesFromResource(R.xml.pref_main);

        }
    }
}

SettingActivity.java

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

    <FrameLayout
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/buttonSave"/>

    <Button
        android:id="@+id/buttonSave"
        android:text="Save"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />
</RelativeLayout>

activity_setting

enter image description here

  • I followed this suggestion, and it does display the button at the bottom of the view, but it's superimposed on top of the preferences list and cannot be clicked (i.e. clicking the button only activates the preference item underneath). – iaindownie Mar 06 '19 at 17:22
  • The button onClickListener is not fired using this solution – rdehuyss May 21 '19 at 08:30
  • @rdehuyss it because you need to add onClickListener to `buttonSave` :) – Albert Aleksieiev May 22 '19 at 04:07
2

It is also possible to add Action buttons to the action bar for an android standard approach.

public class PrefActivity extends PreferenceActivity{

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

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
      // Inflate the menu items for use in the action bar
      MenuInflater inflater = getMenuInflater();
      inflater.inflate(R.menu.preference_header_menu, menu);
      return super.onCreateOptionsMenu(menu);
  }

}


    <?xml version="1.0" encoding="utf-8"?>
       <menu xmlns:android="http://schemas.android.com/apk/res/android">
       <item android:id="@+id/action_add"
           android:icon="@drawable/ic_menu_add_dark"
           android:title="@string/menu_action_add_title"
           android:showAsAction="always"  />

   </menu>
redevill
  • 341
  • 1
  • 3
  • 9
1
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="@dimens/listview_height" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="This is a button on top of all preferences." />
</RelativeLayout>

I reference @Ronnie, use RelativeLayout and set a height for layout_height of listview, and then set the button's layout_alignParentBottom = "true", It can render a button at the bottom of PreferenceScreen; then use the way of @Max. it works for my needs.

Mejonzhan
  • 2,374
  • 1
  • 20
  • 30
  • This is a nice solution, but you forgot to set the listview above the button. currently, items of the listview could be behind the button, which is not recommended since if it's the last item, it will never be visible and therefore clickable. – android developer Apr 26 '13 at 09:00
  • oh, yes, you are right, I forgot this case, because my listview only 5 items, thanks. – Mejonzhan Apr 26 '13 at 09:22
  • please update your answer, so others won't have this behavior. – android developer Apr 26 '13 at 09:43
  • I think you also forgot some attributes and the end-tag of the relativeLayout . – android developer Apr 26 '13 at 10:09
  • In fact, setting the right listview_height can solve the problem you said. – Mejonzhan Apr 27 '13 at 16:29
  • no it won't. since it's a constant value, yet the size of the list can be as large as you wish, the last item that should be visible will in fact be (at least partially) hidden by the button. it's really easy to fix your code though. – android developer Apr 27 '13 at 19:17
1

The following is a simple solution to add a clickable button to your preference screen. This is made easy because the preferences already reserve the space in the android:widgetLayout and the button can pass clicks with android:onClick.

First create a button.xml with the content

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
    android:text="BUTTON"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/button"
    android:onClick="onButtonClick"/>
</LinearLayout>

Now in your preferences.xml, add the preference

<Preference
    android:key="button"
    android:title="Title"
    android:summary="Summary"
    android:widgetLayout="@layout/button" />

Your PreferenceActivity now only has to contain a onButtonClick member

public class MainActivity extends PreferenceActivity {

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

    addPreferencesFromResource(R.xml.main_preferences);


}

public void onButtonClick(View v) {
    Log.d("Button", "Yeah, button was clicked");
}
}
ppareit
  • 423
  • 5
  • 5
1

preferences.xml:

    <Preference
        android:key="clearAllData"
        android:title="@string/settings_clear_all_data">
    </Preference>

SettingsFragment.java:

public class SettingsFragment extends PreferenceFragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.settings);

        Preference clearAllData = (Preference) findPreference("clearAllData");

        // setup buttons
        final Context context = getActivity();
        clearAllData.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {

            @Override
            public boolean onPreferenceClick(Preference preference) {
                ...
            }
    }

}
Dmitry
  • 14,306
  • 23
  • 105
  • 189
1

This would be what the code looks like in the activity at the ronny's example. My intent was to put an menu in the bottom side of the screen.

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.prefs);
    addPreferencesFromResource(R.xml.prefs);

   /* LayoutInflater CX = getLayoutInflater();
    CX.inflate(R.layout.main,null);*/
    // TODO Auto-generated method stub
}
user507410
  • 512
  • 4
  • 12
0

Custom view in Preference Activity this will help to add custom view in PreferenceActivity in Android.

Create main.xml, the only necessary view is a ListView, with id: android:id="@android:id/list".

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="fill_parent"
        android:layout_height="fill_parent" android:weightSum="1">
        <ListView 
            android:id="@android:id/list" 
            android:layout_weight="1"
            android:layout_width="fill_parent"
                android:layout_height="0dp">
        </ListView>
        <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

Create CustomPreferenceActivity.java

public class CustomPreferenceActivity extends PreferenceActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.main);
                addPreferencesFromResource(R.xml.settings);

                //setup any other views that you have
                TextView textView = (TextView) findViewById(R.id.textView);
textView.setText("View Added");
        }
}
Alexander Farber
  • 21,519
  • 75
  • 241
  • 416
0

I found all of the above answers to be un-usable as any layouts I created to 'wrap' the PreferenceScreen container inside custom layouts (then adding a button below the ListView) didn't actually work.

They only overlaid the custom layout on top of the preferences list (floating), and clicking (e.g.) a new custom button would only invoke the preference underneath the button.

However, I found this solution which works a treat for adding a button below the preferences list container, when using PreferenceFragment.

iaindownie
  • 1,046
  • 12
  • 28