Custom View - HorizontalSlider


SDK Version: 
M5

Introduction

In this tutorial we'll create a custom View called HorizontalSlider based on ProgressBar. This slider will allow the user to move the slider back and forth on the screen and get notified when this happens.

Click here to download the complete source.

Creating an interface

First think we'll want to do is create an interface to use to notify our Activities when a user changes the value of the slider. We'll call this interface OnProgressChangeListener to match the naming conventions of some of the other interfaces used in Android. Here is our interface:

  1. public interface OnProgressChangeListener {
  2.         void onProgressChanged(View v, int progress);
  3. }

Interfaces do not do anything, they just create an outline that you must use to implement an object later.

Creating the view

Creating the view in this case is very easy. All that we must do is create a class that extends ProgressBar. After we create this class we will just need to override the constructors and the onMotionEvent() method.

Tip:To override the constructors in Eclipse you can right click in the class's block and hit Source->Generate Constructors from Superclass

All that we must do in the constructors is set our progress bar's background to be the progress_horizontal drawable that is built into android, this will make it a horizontal progress bar instead of the circular one.

Here is the HorizontalSlider class:

  1. public class HorizontalSlider extends ProgressBar {
  2.  
  3.         private OnProgressChangeListener listener;
  4.  
  5.         private static int padding = 2;
  6.  
  7.         public interface OnProgressChangeListener {
  8.                 void onProgressChanged(View v, int progress);
  9.         }
  10.        
  11.         public HorizontalSlider(Context context, AttributeSet attrs,
  12.                         Map inflateParams, int defStyle) {
  13.                 super(context, attrs, inflateParams, defStyle);
  14.         }
  15.  
  16.         public HorizontalSlider(Context context, AttributeSet attrs,
  17.                         Map inflateParams) {
  18.                 super(context, attrs, inflateParams, android.R.attr.progressBarStyleHorizontal);
  19.  
  20.         }
  21.  
  22.         public HorizontalSlider(Context context) {
  23.                 super(context);
  24.  
  25.         }
  26.  
  27.         public void setOnProgressChangeListener(OnProgressChangeListener l) {
  28.                 listener = l;
  29.         }
  30.  
  31.         @Override
  32.         public boolean onTouchEvent(MotionEvent event) {
  33.  
  34.                 int action = event.getAction();
  35.  
  36.                 if (action == MotionEvent.ACTION_DOWN
  37.                                 || action == MotionEvent.ACTION_MOVE) {
  38.                         float x_mouse = event.getX() - padding;
  39.                         float width = getWidth() - 2*padding;
  40.                         int progress = Math.round((float) getMax() * (x_mouse / width));
  41.  
  42.                         if (progress < 0)
  43.                                 progress = 0;
  44.  
  45.                         this.setProgress(progress);
  46.  
  47.                         if (listener != null)
  48.                                 listener.onProgressChanged(this, progress);
  49.  
  50.                 }
  51.  
  52.                 return true;
  53.         }
  54. }

First we include our interface as a member class of this view. Then we setup the 3 constructors, having the second one pass in a style for the horizontal progress bar.

Next we create a function setOnProgressChangeListener() so that when you use this code you can use the interface created above to monitor changes in the progress level. We'll save this to our private variable named "listener".

Now, all the work happens starting on line 42 in our onTouchEvent() class. This class is notified when the user is clicking or dragging a click.

This method is pretty simple, we just calculate what the progress is based on the width of the progress bar and the x value for the click. I have created a static paddedPixels variable that will offset our clicks and widths because there is about 2 pixels of unused space on each side of the bar.

After we set our progress we can also call our OnProgressChangeListener's onProgressChanged() method to notify interested parties that the progress has changed.

On the next page we'll figure out how to use this new object.

Using HorizontalSlider

You can use HorizontalSlider just like any ProgressBar. In this example I'm going to utilize the new ProgresBar that is built into the title of the application and a HorizontalSlider. When the user clicks to change the HorizontalSlider value I'll update the ProgressBar in the title to show that it's working. Here is the main.xml layout I've used for this example:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.         android:layout_width="fill_parent"
  4.         android:layout_height="fill_parent"
  5.         android:paddingLeft="10dip"
  6.         android:paddingRight="10dip"
  7.         android:paddingTop="100dip"
  8.         android:gravity="center_horizontal" >
  9.  
  10.         <com.helloandroid.android.horizontalslider.HorizontalSlider
  11.                 android:id="@+id/slider"
  12.                 android:layout_width="250dip"
  13.                 android:layout_height="20dip"
  14.                 android:max="10000"
  15.                 android:layout_centerHorizontal="true" />
  16.    
  17. </LinearLayout>

Now, we can create a simple Activity for this layout. Here is SliderTest.java:

  1. public class SliderTest extends Activity {
  2.  
  3.         private HorizontalSlider slider;
  4.  
  5.         @Override
  6.         public void onCreate(Bundle icicle) {
  7.                 super.onCreate(icicle);
  8.  
  9.                 requestWindowFeature(Window.FEATURE_PROGRESS);
  10.                 setContentView(R.layout.main);
  11.                 setProgressBarVisibility(true);
  12.  
  13.                 slider = (HorizontalSlider) this.findViewById(R.id.slider);
  14.                 slider.setOnProgressChangeListener(changeListener);
  15.         }
  16.  
  17.         private OnProgressChangeListener changeListener = new OnProgressChangeListener() {
  18.  
  19.                 public void onProgressChanged(View v, int progress) {
  20.                         setProgress(progress);
  21.                 }
  22.  
  23.         };
  24. }

In our onCreate() function we request that the title progress bar feature is enabled with the requestWindowFeature() method. Then all we have to do is create a OnProgressChangeListener for the slider and update the progress bar from there!

You can do anything you want with custom views, in this case it was a very simple change to a built in view, but don't limit yourself to the ones that were handed to us.

Good luck!