Implementing a slider (SeekBar) in Android

Flavian Hautbois picture Flavian Hautbois · Dec 25, 2011 · Viewed 101.1k times · Source

I want to implement a slider, which is basically two lines, one vertical and one horizontal, crossing where the screen is touched. I have managed to make one but I have to issues:

  1. The slider is not very smooth, there is a slight delay when I'm moving the finger
  2. If I place two sliders it is not multitouch, and I'd like to use both of them simultaneously

Here is the code:

public class Slider extends View {

    private Controller controller = new Controller();
    private boolean initialisedSlider;
    private int sliderWidth, sliderHeight;
    private Point pointStart;
    private Paint white;
    private int mode;

    final static int VERTICAL = 0, HORIZONTAL = 1, BOTH = 2;

    public Slider(Context context) {
        super(context);
        setFocusable(true);    
        // TODO Auto-generated constructor stub
    }
    public Slider(Context context, AttributeSet attrs) {
        super(context, attrs);     
        setFocusable(true);    
        pointStart = new Point();
        initialisedSlider = false;   
        mode = Slider.BOTH;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if(!initialisedSlider) {
            initialisedSlider = true;
            sliderWidth = getMeasuredWidth();
            sliderHeight = getMeasuredHeight();

            pointStart.x = (int)(sliderWidth/2.0);
            pointStart.y = (int)(sliderHeight/2.0);
            controller = new Controller(pointStart, 3);

            white = new Paint();
            white.setColor(0xFFFFFFFF);
        }

        canvas.drawLine(controller.getCoordX(),0,
                        controller.getCoordX(),sliderHeight, 
                        white);
        canvas.drawLine(0, controller.getCoordY(), 
                        sliderWidth, controller.getCoordY(), 
                        white);

    }

    public boolean onTouchEvent(MotionEvent event) {
        int eventaction = event.getAction();     
        int X = (int)event.getX(); 
        int Y = (int)event.getY(); 
        switch (eventaction) { 
        case MotionEvent.ACTION_DOWN:
            if(isInBounds(X,Y)) {
                updateController(X, Y);
            }
            break;
        case MotionEvent.ACTION_MOVE:
            if(isInBounds(X,Y)) {
                updateController(X, Y);
            }
            break;
        case MotionEvent.ACTION_UP:
            if(isInBounds(X,Y)) {
                updateController(X, Y);
            }
            break;
        }
        invalidate();  
        return true; 
    }

    private boolean isInBounds(int x, int y) {
        return ((x<=(sliderWidth)) && (x>=(0)) 
                 && (y<=(sliderHeight)) && (y>=(0)));
    }
    private void updateController(int x, int y) {
        switch(mode) {
        case Slider.HORIZONTAL:
            controller.setCoordX(x);
            break;
        case Slider.VERTICAL:
            controller.setCoordY(y);
            break;
        case Slider.BOTH:
            controller.setCoordX(x);
            controller.setCoordY(y);
            break;
        }
    }

    private class Controller {
        private int coordX, coordY;
        Controller() {

        }
        Controller(Point point, int width) {
            setCoordX(point.x);
            setCoordY(point.y);
        }
        public void setCoordX(int coordX) {
            this.coordX = coordX;
        }
        public int getCoordX() {
            return coordX;
        }
        public void setCoordY(int coordY) {
            this.coordY = coordY;
        }
        public int getCoordY() {
            return coordY;
        }
    }
}

And the XML file:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
    <com.android.lasttest.Slider 
        android:id="@+id/slider"
        android:layout_width="100dp" 
        android:layout_height="100dp" 
        android:layout_gravity="center_horizontal" 
        android:adjustViewBounds="true"/>
    <com.android.lasttest.Slider 
        android:id="@+id/slider"
        android:layout_width="150dp" 
        android:layout_height="150dp" 
        android:layout_gravity="center_horizontal" 
        android:adjustViewBounds="true"/>
    <com.android.lasttest.Slider 
        android:id="@+id/slider"
        android:layout_width="200dp" 
        android:layout_height="200dp" 
        android:layout_gravity="center_horizontal" 
        android:adjustViewBounds="true"/>

</LinearLayout>

Answer

Suragch picture Suragch · Oct 14, 2017

How to implement a SeekBar

enter image description here

Add the SeekBar to your layout

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

    <TextView
        android:id="@+id/textView"
        android:layout_margin="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <SeekBar
        android:id="@+id/seekBar"
        android:max="100"
        android:progress="50"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

Notes

  • max is the highest value that the seek bar can go to. The default is 100. The minimum is 0. The xml min value is only available from API 26, but you can just programmatically convert the 0-100 range to whatever you need for earlier versions.
  • progress is the initial position of the slider dot (called a "thumb").
  • For a vertical SeekBar use android:rotation="270".

Listen for changes in code

public class MainActivity extends AppCompatActivity {

    TextView tvProgressLabel;

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

        // set a change listener on the SeekBar
        SeekBar seekBar = findViewById(R.id.seekBar);
        seekBar.setOnSeekBarChangeListener(seekBarChangeListener);

        int progress = seekBar.getProgress();
        tvProgressLabel = findViewById(R.id.textView);
        tvProgressLabel.setText("Progress: " + progress);
    }

    SeekBar.OnSeekBarChangeListener seekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {

        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            // updated continuously as the user slides the thumb
            tvProgressLabel.setText("Progress: " + progress);
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
            // called when the user first touches the SeekBar
        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            // called after the user finishes moving the SeekBar
        }
    };
}

Notes

  • If you don't need to do any updates while the user is moving the seekbar, then you can just update the UI in onStopTrackingTouch.

See also