Aug 132011
 

Attention: This version of the SeekBar is obsolete, check out the new version instead

While working on my Android implementation of the Dislexicon, I realized I would need a SeekBar preference to adjust the text-to-speech speed. I found a couple of SeekBar prefs online, but none of them fit my needs. Specifically, I wanted it to:

  1. Appear on the main preference screen, instead of a separate window accessed via a button
  2. Fill the entire width of the screen
  3. Allow a minimum value other than zero

Here’s what I ended up with, as it appears in Dislexicon:

SeekBar Preference


You’ll need two files, plus a preferences section. They are all inline below, but you can download them here:

File Description
preferences.xml Example of adding a config to your preferences.
SeekBarPreference.java The java class.
seek_bar_preference.xml The XML layout of the preference. This should go in your res/layout directory.

 

Here’s an example of configuring it in your preferences.xml file. Most of the parameters work exactly the same as a standard preference, but there are two new parameters:

  1. min: Minimum value to use for the slider. The default is zero
  2. units: The characters to put to the right of the value, to indicate the units (eg. %, deg, .oz)

preferences.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>

<PreferenceScreen
       xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:robobunny="http://robobunny.com"
       android:key="preference_screen">
       
    <PreferenceCategory android:title="Speech">
        <com.robobunny.SeekBarPreference
            android:key="speechRate"
                android:title="Speech speed"
                android:summary="Adjust reading speed"
                android:defaultValue="80"
                android:max="200"
                robobunny:min="1"
            robobunny:unitsLeft=""
                robobunny:unitsRight="%"
            />
    </PreferenceCategory>
   
</PreferenceScreen>

The java class itself.
SeekBarPreference.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
package com.robobunny;

import android.content.Context;
import android.content.res.TypedArray;
import android.preference.Preference;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

public class SeekBarPreference extends Preference implements OnSeekBarChangeListener {
   
    private final String TAG = getClass().getName();
   
    private static final String ANDROIDNS="http://schemas.android.com/apk/res/android";
    private static final String ROBOBUNNYNS="http://robobunny.com";
    private static final int DEFAULT_VALUE = 50;
   
    private int mMaxValue      = 100;
    private int mMinValue      = 0;
    private int mInterval      = 1;
    private int mCurrentValue;
    private String mUnitsLeft  = "";
    private String mUnitsRight = "";
    private SeekBar mSeekBar;
   
    private TextView mStatusText;

    public SeekBarPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPreference(context, attrs);
    }

    public SeekBarPreference(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initPreference(context, attrs);
    }

    private void initPreference(Context context, AttributeSet attrs) {
        setValuesFromXml(attrs);
        mSeekBar = new SeekBar(context, attrs);
        mSeekBar.setMax(mMaxValue - mMinValue);
        mSeekBar.setOnSeekBarChangeListener(this);
    }
   
    private void setValuesFromXml(AttributeSet attrs) {
        mMaxValue = attrs.getAttributeIntValue(ANDROIDNS, "max", 100);
        mMinValue = attrs.getAttributeIntValue(ROBOBUNNYNS, "min", 0);
       
        mUnitsLeft = getAttributeStringValue(attrs, ROBOBUNNYNS, "unitsLeft", "");
        String units = getAttributeStringValue(attrs, ROBOBUNNYNS, "units", "");
        mUnitsRight = getAttributeStringValue(attrs, ROBOBUNNYNS, "unitsRight", units);
       
        try {
            String newInterval = attrs.getAttributeValue(ROBOBUNNYNS, "interval");
            if(newInterval != null)
                mInterval = Integer.parseInt(newInterval);
        }
        catch(Exception e) {
            Log.e(TAG, "Invalid interval value", e);
        }
       
    }
   
    private String getAttributeStringValue(AttributeSet attrs, String namespace, String name, String defaultValue) {
        String value = attrs.getAttributeValue(namespace, name);
        if(value == null)
            value = defaultValue;
       
        return value;
    }
   
    @Override
    protected View onCreateView(ViewGroup parent){
       
        RelativeLayout layout =  null;
       
        try {
            LayoutInflater mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);

            layout = (RelativeLayout)mInflater.inflate(R.layout.seek_bar_preference, parent, false);
        }
        catch(Exception e)
        {
            Log.e(TAG, "Error creating seek bar preference", e);
        }

        return layout;
       
    }
   
    @Override
    public void onBindView(View view) {
        super.onBindView(view);

        try
        {
            // move our seekbar to the new view we've been given
            ViewParent oldContainer = mSeekBar.getParent();
            ViewGroup newContainer = (ViewGroup) view.findViewById(R.id.seekBarPrefBarContainer);
           
            if (oldContainer != newContainer) {
                // remove the seekbar from the old view
                if (oldContainer != null) {
                    ((ViewGroup) oldContainer).removeView(mSeekBar);
                }
                // remove the existing seekbar (there may not be one) and add ours
                newContainer.removeAllViews();
                newContainer.addView(mSeekBar, ViewGroup.LayoutParams.FILL_PARENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT);
            }
        }
        catch(Exception ex) {
            Log.e(TAG, "Error binding view: " + ex.toString());
        }

        updateView(view);
    }
   
    /**
     * Update a SeekBarPreference view with our current state
     * @param view
     */

    protected void updateView(View view) {

        try {
            RelativeLayout layout = (RelativeLayout)view;

            mStatusText = (TextView)layout.findViewById(R.id.seekBarPrefValue);
            mStatusText.setText(String.valueOf(mCurrentValue));
            mStatusText.setMinimumWidth(30);
           
            mSeekBar.setProgress(mCurrentValue - mMinValue);

            TextView unitsRight = (TextView)layout.findViewById(R.id.seekBarPrefUnitsRight);
            unitsRight.setText(mUnitsRight);
           
            TextView unitsLeft = (TextView)layout.findViewById(R.id.seekBarPrefUnitsLeft);
            unitsLeft.setText(mUnitsLeft);
           
        }
        catch(Exception e) {
            Log.e(TAG, "Error updating seek bar preference", e);
        }
       
    }
   
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        int newValue = progress + mMinValue;
       
        if(newValue > mMaxValue)
            newValue = mMaxValue;
        else if(newValue < mMinValue)
            newValue = mMinValue;
        else if(mInterval != 1 && newValue % mInterval != 0)
            newValue = Math.round(((float)newValue)/mInterval)*mInterval;  
       
        // change rejected, revert to the previous value
        if(!callChangeListener(newValue)){
            seekBar.setProgress(mCurrentValue - mMinValue);
            return;
        }

        // change accepted, store it
        mCurrentValue = newValue;
        mStatusText.setText(String.valueOf(newValue));
        persistInt(newValue);

    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {}

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        notifyChanged();
    }


    @Override
    protected Object onGetDefaultValue(TypedArray ta, int index){
       
        int defaultValue = ta.getInt(index, DEFAULT_VALUE);
        return defaultValue;
       
    }

    @Override
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {

        if(restoreValue) {
            mCurrentValue = getPersistedInt(mCurrentValue);
        }
        else {
            int temp = 0;
            try {
                temp = (Integer)defaultValue;
            }
            catch(Exception ex) {
                Log.e(TAG, "Invalid default value: " + defaultValue.toString());
            }
           
            persistInt(temp);
            mCurrentValue = temp;
        }
       
    }
   
}

The preference layout file. You can use this to tweek what the preference looks like.
seek_bar_preference.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?xml version="1.0" encoding="UTF-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/widget_frame"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="15dp"
    android:paddingTop="5dp"
    android:paddingRight="10dp"
    android:paddingBottom="5dp"
    >
   
    <TextView android:id="@android:id/title"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:textSize="22dp"
            android:typeface="sans"
            android:textStyle="normal"
            android:textColor="#ffffff"
    ></TextView>
   
    <TextView android:id="@android:id/summary"
            android:layout_alignParentLeft="true"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_below="@android:id/title"
    ></TextView>

    <TextView android:id="@+id/seekBarPrefUnitsRight"
            android:layout_alignParentRight="true"
            android:layout_below="@android:id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
    ></TextView>
   
    <TextView android:id="@+id/seekBarPrefValue"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toLeftOf="@id/seekBarPrefUnitsRight"
            android:layout_below="@android:id/title"
            android:gravity="right"
    ></TextView>

    <TextView android:id="@+id/seekBarPrefUnitsLeft"
            android:layout_below="@android:id/title"
            android:layout_toLeftOf="@id/seekBarPrefValue"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
    ></TextView>
   
    <LinearLayout android:id="@+id/seekBarPrefBarContainer"
            android:layout_alignParentLeft="true"
            android:layout_alignParentBottom="true"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_below="@android:id/summary">
    </LinearLayout>
   
</RelativeLayout>

  82 Responses to “Android SeekBar preference”

  1. Great work ! Very nice and useful.
    However I noticed that calling ‘notifyChanged();’ in ‘onProgressChanged’ prevents the proper tracking of the thumb. I suggest to update mStatusText only in ‘onProgressChanged’, and to actually change the preference value in ‘onStopTrackingTouch’. This will fix the tracking problem, and also will be a bit more efficient.

  2. FYI, setting a min value doesn’t seem to work. I have a value I want to go from 32 to 255 and the slider position moves incorrectly/erratically.

    • You’re right, there was a bug in the java code. The code that set the initial progress bar position was wrong, so the first time you moved it you’d get a seemingly random new value. I didn’t notice it because my min value was 1. I’ve updated the code. Thanks for the bug report, I hope the code is useful for you!

  3. There are bug in this control when number of seekbars is more than one on single preference activity screen. Can your fix it?

    • It’s fixed now. I wasn’t handling the onBindView call correctly, so the views were getting switched around. I’ve tried to follow the method used by the built-in EditTextPreference, so hopefully it will behave itself. I made a couple of other small tweaks to make it work in a more “standard” fashion.

  4. Thank you very much. it’s very useful.
    However, I don’t know what should I do tracking of the thumb using dpad(left-right).
    Could you inform to me?

    • I assumed you could just listen for them on the preference, but something seems to be grabbing keystrokes before they get there. I’ll post an update if I figure out how to do it.

      • I’m also trying to control the progress bar with dpad (left-right). I already changed SeekBarPreference.java to add an OnKeyListener but onKey callback is not called!

        Any clue?!?

        BTW, thanks.. useful plugin

  5. Great! thank you very much. it’s good.

  6. I am not sure if it works, cause if I use sharedpreferance with the same key in another place in my application it does not give value set within PreferanceActivity. Also if I set value outside PreferanceActivity with shared rpeference editor it doesn’t change value within SeekBarPreference.

    • Hmm, I’m not sure why that would be. I’m fetching the value in my app without any problems, but I’m not setting it outside the PreferenceActivity. Let me know if you find the issue.

  7. Thanks a lot for sharing you class!

  8. Thanks for writing this up in such detail — I learned a lot from it. I made a similar SeekBar in one of my projects and found that it ran fine in the android virtual device launched from Eclipse, but when I actually ran it on my phone, the app would crash as soon as I pressed the menu item that started up the Preferences screen. I fixed this issue by inverting the order of two lines in the initPreference method of the SeekBarPreference class. Here’s the order that worked for me:

    mSeekBar.setMax(mMaxValue – mMinValue);
    mSeekBar.setOnSeekBarChangeListener(this);

    • Thanks for the info! I haven’t seen that happen, but it probably depends on what your listener is doing. I’ve swapped the order as you suggested, since it should be safer.

  9. Found two issues with this great code, thank you for that btw…

    I’m using this in a preference activity, and in the androidmanifest.xml I declare a theme for the preference activity . This causes the seek bar to not display. Anyone else encounter this?

    Also, another issue is the text and summary are not matching the default device theme colors, would be nice to have.

    Thanks again!
    JS

    • activity android:theme=”@android:style/Theme.Dialog

      - Sorry that got culled out I guess, had to remove the opening less-than sign.

    • Hi, this is a little old but have you find a way to solve that problem ? I have the same and i don’t know how to do.
      Thank you
      Antoine

  10. I have some kind problems with the seek bar, when touch on thumb to change value it disappears, when I release my finger it appears….
    Also seek bar is “GONE”, when I have more then one element in preference category..
    How to solve this?

  11. Excellent code!
    I found it while trying to get rid of my Lint Warnings for “unused” custom attributes.
    Fell into a nasty little trap with the namespace because of an article on stackoverflow.com recommending

    1
    xmlns:app="http://schemas.android.com/apk/res/com.mycompany.projectname"

    as a namespace in Java file and corresponding XML file, which results in an immediately crash. While

    1
    xmlns:app="http://com.mycompany.projectname"

    works fine.
    Any ideas how this namespace tying Java code and XML stuff together really works?

    Bests,
    Tobias

  12. I find this code doesn’t work well in diffirent version of Android. If works well in 2.3 and 4.0, But for 2.2.2 and 3.0 it doesn’t work well. How to solve this?

  13. How to coop with an ‘android:dependancy’? The title stays white, althought the rest of the custom vies turns grey.

    • There are definitely problems with theming. I don’t know enough about Android UI theming to tell what’s going wrong, and I don’t have an actual Android device to test with (I just developed on the emulator). I’ve seen it behave differently on different devices depending on the phone vendor’s modifications.

      If anyone can offer any tips, I’d appreciate it.

  14. Hi Kirk,

    Very good job on your Android tweak.
    What I would like to point out to Android experts is that, some of us are not familiar with Android.
    So, very clear “how to insert code” tutorials would be good. If you have the time, ofc.

  15. nice tutorials, do you tell me how to build android application game tank step by step please, now i’m last semester in my study.please help me.

  16. Hi Kirk,

    I’m trying to use your seek bar preference component on a very simple preference list app.

    The seek bar appears in a separate dialog Box and the value is never taken into account in the shared preference file… What am i doing wrong ??

    It seems impossible to debug the component either, i put a breakpoint in every single method and execution never stops (i’m using an emulator).

    i had a compilation problem also due to the Override key words that were placed before the overridden interface methods…

    Thanks in advance

  17. Really a very good work, very valuable, improved a lot avoid to using the arrays.

  18. Very useful. Thanks!

  19. Thanks so much for doing this but I am having some issue getting the SeekBar to show up. I can see the title, summary and value text but no seekbar. Is there something obvious I might be missing?

    Thanks!

    • I’m not sure what would cause that. Are you getting any errors in your log? The seek bar gets added to the view on line 106 of SeekBarPreference.java. The first thing I’d try is setting a breakpoint there and stepping through to see if something is going wrong.

      • Yeah it’s definitely hitting it, and no, no errors. I tried adding it to a framelayout instead, but nothing. HierarchyViewer is not showing any views below the LinearLayout container (nor the Framelayout when I tried that).

        Have you tried this with a PreferenceFragment? I’m just trying to figure out what I might be missing.

  20. Good work and thanks for sharing. However, if the seekbar preference is disabled, its value can still be changed. To prevent this form happening, simply override setEnabled() in SeekBarPreference.java:

    @Override
    public void setEnabled(boolean enabled) {
    super.setEnabled(enabled);
    mSeekBar.setEnabled(enabled);
    }

    Best regards,
    Dieter

  21. Great work…Thanks a lot for sharing.:). Is the attached code contains all the bugfixes and improvements made by you. If not, where can I find the latest code.

  22. why do i have to create a class for seekbar , i do not understand why can’t i just get the value
    i need a full tutorial about this . and this is my question . i hope someone can answer
    Here

  23. This looks really cool, but after an hour or so of trying to compile it, I can’t figure out one line. In the preferences.xml, the 2nd line below for SeekBarPreference:

    1
    2
                <PreferenceCategory android:title="Temp" >
                    <com.robobunny.SeekBarPreference

    Always fails with ‘error: Error parsing XML: unbound prefix’. Changing it to match my project name (like com.company.appname ) doesn’t fix it. I’ve tried many variations (and project cleans in-between), without success such as:

    1
    2
    3
            <SeekBarPreference
    and
            <com.company.appname.SeekBarPreference

    Other than the 3 files, is there some additional project modifications necessary? I’d prefer not to have to name the project robobunny! Any suggestions would be appreciated, as I’d love to make this work.

    • There’s another name that needs to match between the code and the XML: the xmlns. I think that might be the problem you’re seeing.

      The line in the XML that reads xmlns:robobunny=”http://robobunny.com” must match the string in the code:
      private static final String ROBOBUNNYNS=”http://robobunny.com”;
      (Obviously you can rename ROBOBUNNYNS if you do so throughout the code). Also, if you rename xmlns:robobunny to something else, you’ll need to also rename all the robobunny: strings in the XML. I think that’s probably what is wrong in your case from the error you’re getting.

      Let me know if that doesn’t fix your problem.

  24. When using the android:dependency field the only thing that gets greyed out is the summary. The Seekbar is also still useable.

    • Add this and your seekbars will respond to android:dependency:

      @Override
      public void onDependencyChanged(Preference dependency, boolean disableDependent)
      {
      super.onDependencyChanged(dependency, disableDependent);

      // /see if it has been initialized
      if (this.layout != null)
      {
      this.mSeekBar.setEnabled(!disableDependent);
      this.mStatusText.setEnabled(!disableDependent);
      }
      }

      • Also add this in the onBindView(View view) method above updateView(View) to disable the seekbar when it first loads, otherwise you have to toggle the denpendency

        if(!this.layout.isEnabled() && this.layout != null){
        this.mSeekBar.setEnabled(false);
        }

      • Hi, the “this.layout” gives me an error, how can i work this out? thx

        • Hi,

          Thanks for the modifications they work perfectly.

          I have however done the code a little different and this may help you ttkttk:

          The on dependency changed

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          @Override
              public void onDependencyChanged(Preference dependency, boolean disableDependent) {
                  super.onDependencyChanged(dependency, disableDependent);
                 
                  //Disable movement of seek bar when dependency is false
                  if (mSeekBar != null)
                  {
                      mSeekBar.setEnabled(!disableDependent);
                  }
              }

          And the on bind view method:

          1
          2
          3
          4
          5
          6
          7
          //if dependency is false from the beginning, disable the seek bar
                  if (view != null && !view.isEnabled())
                  {
                      mSeekBar.setEnabled(false);
                  }
                 
                  updateView(view);

          Hope this helps you

  25. Thanks. I added it to bitbeaker. I had one problem when I tried to keep this class in it’s current package but I got it resolved by importing our package’s R class into SeekBarPreference.java

    • I had one other problem: summary was overlapping with seekBarPrefValue. I ended up adding

      1
      android:layout_toLeftOf="@+id/seekBarPrefUnitsLeft"

      to summary. I had to include + sign before id or otherwise it just gave Error: No resource found that matches the given name. I have Android 2.3.6.

      • Hi,

        Thanks for this post, I had the same problem. Seems to occur if the summary is a bit longer than expected. The Error that you are talking about is explained here just for future reference.

        I do have a slight problem with this method though. For some reason, once I made this change, the unitsRight text is now on the left of the value :?. Anybody know why this could be the case?

        • I cannot explain this, but this is what I add to do completely to the layout file to fix the problem that I was having.

          Here is the layout of the file below title and above unitsLeft:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          <TextView
                  android:id="@android:id/summary"
                  android:layout_alignParentLeft="true"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_below="@android:id/title"
                  android:layout_toLeftOf="@+id/seekBarPrefUnitsLeft" >
              </TextView>
             
              <TextView
                  android:id="@+id/seekBarPrefValue"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_toLeftOf="@+id/seekBarPrefUnitsRight"
                  android:layout_below="@android:id/title" >
              </TextView>
             
              <TextView
                  android:id="@id/seekBarPrefUnitsRight"
                  android:layout_alignParentRight="true"
                  android:layout_below="@android:id/title"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content" >
              </TextView>
  26. You didn’t have a license on your work. Is it alright to use this in a commercial android application?

    • Yes, you can consider it public domain. If you find any bugs or make any improvements, I’d appreciate it if you post them here.

  27. Thanks much for sharing, Kirk,

    With your permission, Google should include this in the next Android release, as it’s very useful indeed.

  28. [...] classes I've seen floating around on the Internet (such as those by Hlidskialf and Robobunny), but is also designed with many best practice recommendations in mind (utilising advice from this [...]

  29. Hi
    I’m using your great seekbar in one of my apps.

    Instead of creating the layout from XML for the preferences page I’m creating the page dynamically in code as it’s quite variable.

    One issue I’ve come across is I’m not sure how to create a new instance of the seek bar with the AttributeSet attrs value. If using XML this gets automatically set. If I do it programatically how to I create it. If I just use the context – by changing the class a little, it mostly works, but then I get the indentation issue that I know you’ve referenced before here: http://stackoverflow.com/questions/5757012/custom-preference-targetsdkversion-11-missing-indent/14082812#14082812

    What’s the best way I can use it in combination with dynamic screen creation?

    • I apologize that I can’t help you much, I don’t have very much experience with android GUI stuff myself. I created this widget for a program I was working on, but I haven’t used the built in android UI stuff subsequently. Hopefully someone else can answer your question.

      BTW, the guy posting in the thread you referenced was not me, his post was just worded slightly confusingly. He seems to have a widget based on mine, he may be able to assist you.

  30. Great piece of code, helped me a lot!

    I want to ask one thing, though. How would I dynamically set the minimum value of a seekbar to be equal to the current value of another seekbar? Think Max speed / Min speed values, the min value should not be able to go above the max one.

    • “the min value should not be able to go above the max one.”

      wrong words there, the max value should not be able to go below the min one :)

    • You’d have to add a couple new parameters to SeekBarPreference.java that can be updated externally, then check those new values in onProgressChanged to see if the user tried to make the setting lower or higher than is allowed. You can just return without setting anything (like the code in the middle of onProgressChanged). You’d also have to update the setting on the bar when something changes the min or max value. Then of course you’d have to have the two seekbars send updates back and forth.

      It gets very tricky when you’re trying to link two bar settings together like that, especially for the user. The ideal solution would be to have two handles on one bar, so it’s obvious to the user what is going on when the handles won’t cross each other. You can also have one handle slide the other when they bump, or hit a minimum distance from each other. Many years ago I used something like that in a game for a bar used to allocate energy, and it worked out ok. That might actually be easier to do than making two seek bars talk to each other, too.

  31. Thank you very much.

  32. [...] with a HorizontalListView attached directly underneath it. I basically created it by modifying this code for a SeekBarPreference (which I am also using and is working fine). My ListViewPreference is [...]

  33. Dude ur the best! This is exactly where I got stuck.

  34. Thanks for sharing your code. This is a big help for me.
    I added another ‘android:layout_toLeftOf=”@+id/seekBarPrefUnitsLeft”‘ to the summary-block in seek_bar_preference.xml. This way long summaries don’t overlap the prefUnit-stuff.

  35. Hi Kirk,
    I’ve used your preference and it’s excellent. However, I had one problem with it – it doesn’t use the theme, so it looks completely different than the other preferences on my activity.
    So – I’ve changed it abit, basically so it will use the default Preference’s layout, and your layout deals only with the widgetFrame in it.
    If you would like – I can send you the changes I’ve done.

    Thanks!
    Yair

  36. Hi Kirk,
    nice work!

    But i’ve had some troubles while using your implementation on Android 4.2. with PreferenceFragments, so i decided to build my own one from scratch;)
    My implementation is based on the CheckButtonPreference and is reusing the base Android stuff, i just extend Androids default layout, so it should look like the Version of Android you are using.
    And it’s a Library Project btw.

    Feel free to check it out, the source is available on github.
    https://github.com/n0iz/CustomPreferences

    cheers!
    n0iz

  37. Thanks for the code :) I made a few changes to accept attribute values from resources (for example robobunny:min=”@integer/my_min_value”) I added this method:

    1
    2
    3
    4
    5
    6
    7
      private int getAttributeInt (Context context, AttributeSet attrs,
      String name, int defaultValue)
      {
        int resource = attrs.getAttributeResourceValue (NAMESPACE, name, 0);
        return (resource == 0) ? attrs.getAttributeIntValue (NAMESPACE, name,
        defaultValue) : context.getResources ().getInteger (resource);
      }
  38. Hii Kirk,

    I am working on a TEXT TO SPEECH (TTS) app development which read a text file, with varying voice speed and voice pitch.

    please suggest me how to use these two function on a two different seekbars :

    public int setPitch (float pitch) and public int setSpeechRate (float speechRate)

    Also currently I am reading from a string, instead of this I want to read from a text file.

    Please reply ASAP.

    Thanks in advance :)

    • I added a divisor as one of the fields and created a mDivisor in the code, and then added this code

      1
      2
      3
      4
      5
      if (mDivisor > 1) {
           mStatusText.setText(new DecimalFormat("0.0##").format((float)mCurrentValue / (float)mDivisor));
      } else {
          mStatusText.setText(String.valueOf(mCurrentValue));
      }

      in place of the existing setText at the two proper locations. upper and lower limits need to be multiplied by the divisor.

      Good luck.

  39. That works well. Thanks for posting …

  40. Hi,

    This preference component looks amazing! :)
    But I am having a problem :(.

    The SeekBarPreference is only displaying the seek bar and the title. I don’t see a summary or actual value or “unitsRight” either.

    Does anyone know what the problem could be? I have copied the code and everything exactly, and it’s not working :/. I am running this on min API 14, if that maybe has something to do with the problem.

    • I think I found my problem:

      1
      android:textColor="#ffffff"

      I did not realise that this was white :O, once removed, I could see everything :).

      Thanks for the preference component robobunny! :)

  41. Great work, thank you so much! I made a couple of improvements (in my view) and encountered one issue. First, in the XML for the preference, I removed the font specifics for the title and instead added

    1
    style="@android:style/TextAppearance.Large"

    - this automatically takes care of displaying the correct font for corresponding versions of android. Second, I added

    1
    showValue

    attribute, which can be true/false – when set to “false”, the value currently selected on the seek bar is not displayed in the

    1
    mStatusText

    (I needed this for my specific case).

    Now, the issue that I encountered. I have the seekbar set with min=0, max=91, interval=7 (to select from 0 to 13 weeks in 1 week interval – but get results in days). When I touch and slide, the seekbar adjusts once to the next step value (up or down) and then stops. I have to remove my finger, then touch again and slide again to continue adjustment. I don’t think this is the right behaviour though. What am I missing here?

    • Ok, on further investigation the issue seems to be caused by updates to ANY preference in the onPreferenceChangeListener of the SeekBarPreference. In my case, when the seekbar position changes, in my listener I am updating summary and enable/disable status of another preference just below this one. If I comment out this update code, then everything works as expected. Any ideas as to how to fix it?

      • I can’t think of any reason why this would be happening. It doesn’t make much sense. When moving the seek bar it should continue to move between intervals, that is my guess. This is very strange. Could you please help me out Aleks, does your SeekBarPreference saw everything that is shown in the picture at the top? For some reason, mine is only showing “Speech Speed”, it only appears to show the title. The summary, unitsLeft, and unitsRight are not visible :|

        • I have found some code that may be useful to you Aleks:

          1
          2
          3
          4
          public void onProgressChanged(SeekBar seek, int value, boolean fromTouch) {
              mCurrentValue = value + mMinValue;
              mValueText.setText(Integer.toString(mCurrentValue));
          }

          I found this here.

          I don’t know what you changed, but I think this might be worth looking at.

  42. [...] couple of years ago, I posted an Android SeekBar preference widget that I’d written. Since then, people have regularly posted fixes and enhancements. This is a [...]

  43. Thanks! Works just great!

    /timo

  44. How can I create a new object of type SeekBarPreference and your constructors? I do not know what values should I give to context and attributes.

  45. Howdy just wanted to give you a quick heads up and let you know a few
    of the images aren’t loading correctly. I’m not sure why but I think its a linking issue.
    I’ve tried it in two different web browsers and both show the same
    outcome.

 Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>