How can I rotate the RatingBar?
Is there an example for a vertical custom RatingBar?
This is sample code for vertical rating bar...
public class VerticalRatingBar extends RatingBar {
private int x, y, z, w;
#Override
protected void drawableStateChanged() {
// TODO Auto-generated method stub
super.drawableStateChanged();
}
public VerticalRatingBar(Context context) {
super(context);
}
public VerticalRatingBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public VerticalRatingBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
this.x = w;
this.y = h;
this.z = oldw;
this.w = oldh;
}
#Override
protected synchronized void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
protected void onDraw(Canvas c) {
c.rotate(-90);
c.translate(-getHeight(), 0);
super.onDraw(c);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
setSelected(true);
setPressed(true);
break;
case MotionEvent.ACTION_MOVE:
setProgress(getMax()
- (int) (getMax() * event.getY() / getHeight()));
onSizeChanged(getWidth(), getHeight(), 0, 0);
break;
case MotionEvent.ACTION_UP:
setSelected(false);
setPressed(false);
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
#Override
public synchronized void setProgress(int progress) {
if (progress >= 0)
super.setProgress(progress);
else
super.setProgress(0);
onSizeChanged(x, y, z, w);
}
}
There's a setRotation method on the view which will hopefully achieve what you're after.
Related
pic of wrong seekbar I am using custom vertical seekbar in a fragment, it was working fine before but suddenly it started showing wrongly. The seekbar thumb is being misplaced in some devices, in my case I am using an HTC desire which is running on API 21.
I am posting my seekbar code snippets here.
This is the xml version of the vertical seekbar
<com.sampleApp.progressbars.VerticalProgressBar
android:id="#+id/exposureSlider"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_above="#id/icBrightnessDim"
android:layout_below="#id/icBrightnessBright"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:maxWidth="50dp"
android:minHeight="50dp"
android:minWidth="50dp"
android:progressDrawable="#drawable/custom_seekbar_progress"
android:thumb="#drawable/seek_bar_thumb"
app:ssbPopupBackgroundColor="#color/grey"
app:ssbPopupEnabled="true"
app:ssbPopupHeight="80dp"
app:ssbPopupThumbColor="#color/grey"
app:ssbPopupWidth="20dp"
app:ssbThrottleEnabled="false"
app:ssbTouchAreaOffset="16dp"
/>
This is Vertical seekbar code
public class VerticalProgressBar extends android.support.v7.widget.AppCompatSeekBar {
public VerticalProgressBar(Context context) {
super(context);
}
public VerticalProgressBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public VerticalProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
}
#Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
protected void onDraw(Canvas c) {
c.rotate(-90);
c.translate(-getHeight(), 0);
super.onDraw(c);
}
private OnSeekBarChangeListener onChangeListener;
#Override
public void setOnSeekBarChangeListener(OnSeekBarChangeListener onChangeListener){
this.onChangeListener = onChangeListener;
}
private int lastProgress = 0;
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
onChangeListener.onStartTrackingTouch(this);
setPressed(true);
setSelected(true);
break;
case MotionEvent.ACTION_MOVE:
super.onTouchEvent(event);
int progress = getMax() - (int) (getMax() * event.getY() / getHeight());
// Ensure progress stays within boundaries
if(progress < 0) {progress = 0;}
if(progress > getMax()) {progress = getMax();}
setProgress(progress); // Draw progress
if(progress != lastProgress) {
// Only enact listener if the progress has actually changed
lastProgress = progress;
onChangeListener.onProgressChanged(this, progress, true);
}
onSizeChanged(getWidth(), getHeight() , 0, 0);
setPressed(true);
setSelected(true);
break;
case MotionEvent.ACTION_UP:
onChangeListener.onStopTrackingTouch(this);
setPressed(false);
setSelected(false);
break;
case MotionEvent.ACTION_CANCEL:
super.onTouchEvent(event);
setPressed(false);
setSelected(false);
break;
}
return true;
}
I made vertical seekbar. Now I want to display progress on seekbar thumb. I successfully implemented this but now my text is showing below seekbar thumb. I want to draw text on top of seekbar thumb How can I achieve this? I also add my code
public class TextThumbSeekBar extends SeekBar {
public TextThumbSeekBar(Context context) {
super(context);
}
private int mThumbSize;
private TextPaint mTextPaint;
public TextThumbSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mThumbSize = 50;
mTextPaint = new TextPaint();
mTextPaint.setColor(Color.BLACK);
mTextPaint.setTextSize(50);
mTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
mTextPaint.setTextAlign(Paint.Align.CENTER);
}
public TextThumbSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
mThumbSize = 50;
mTextPaint = new TextPaint();
mTextPaint.setColor(Color.BLACK);
mTextPaint.setTextSize(50);
mTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
mTextPaint.setTextAlign(Paint.Align.CENTER);
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
}
#Override
public synchronized void setProgress(int progress) // it is necessary for calling setProgress on click of a button
{
super.setProgress(progress);
onSizeChanged(getWidth(), getHeight(), 0, 0);
}
#Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
protected void onDraw(Canvas c) {
String progressText = String.valueOf(getProgress());
c.drawText(progressText, getThumb().getBounds().centerY(), getHeight() - getThumb().getBounds().centerX(), mTextPaint);
c.rotate(-90);
c.translate(-getHeight(), 0);
super.onDraw(c);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
super.onTouchEvent(event);
setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
onSizeChanged(getWidth(), getHeight(), 0, 0);
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
}
Here how I used this class in my xml
<demo.smart.com.smartchargeboxdemoapp.TextThumbSeekBar
android:max="100"
android:thumb="#drawable/shape_seek_bar_text_thumb"
android:layout_gravity="center"
android:id="#+id/seekbar"
android:layout_width="30dp"
android:layout_height="match_parent" />
And here's the result that I get
Although the question is old, I did in this case the thumbTint transparent and before the drawText I did 'drawRect' to draw my thumb that won't be rotated.
Hope I helped.
Hey guys I am currently working on my own Android SeekBar and I want to make the progress color to be drawn from the center of the bar to the thumb. Currently, my SeekBar class looks like this:
public class VerticalSeekBar extends SeekBar {
public VerticalSeekBar(Context context) {
super(context);
}
public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public VerticalSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
setThumb(getResources().getDrawable(R.drawable.slidera));
setProgressDrawable(getResources().getDrawable(R.drawable.sl_bg));
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
}
#Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
protected void onDraw(Canvas c) {
c.rotate(-90);
c.translate(-getHeight(), 0);
super.onDraw(c);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
[...]
My Problem now is that I don't know what method I need to override in order to change the behaviour of the drawing of the ProgressDrawable, I am sure there must be some way.
Thanks in advance
So this is now the whole class. The getRekt methods get the size of the SeekBar and the other stuff you see in the onDraw method is pretty much self-explaining. The background drawable is a drawable consisting of the color value 0x00000000 so it doesn't cover the "new" progress background.
public class VerticalSeekBar extends SeekBar {
public VerticalSeekBar(Context context) {
super(context);
}
public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public VerticalSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
setThumb(getResources().getDrawable(R.drawable.slidera));
setThumbOffset(0);
setProgressDrawable(getResources().getDrawable(R.drawable.sl_bg));
setPadding(0,0,0,0);
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
}
#Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
protected void onDraw(Canvas c) {
c.rotate(-90);
c.translate(-getHeight(), 0);
Paint paint = new Paint();
paint.setColor(0xffff7700);
paint.setStrokeWidth(11);
BlurMaskFilter blurMaskFilter=new BlurMaskFilter(15.0f,BlurMaskFilter.Blur.OUTER);
c.drawLine(
getRectX()+(getProgress()-50)*getRHeight()/100, getRectY(),getRectX(), getRectY(), paint);
paint.setMaskFilter(blurMaskFilter);
c.drawLine(
getRectX()+(getProgress()-50)*getRHeight()/100, getRectY(),getRectX(), getRectY(), paint);
drawMeter(c);
super.onDraw(c);
}
public int getRectX(){
Rect MLG= getProgressDrawable().getBounds();
return MLG.centerX();
}
public int getRectY(){
Rect MLG= getProgressDrawable().getBounds();
return MLG.centerY();
}
public int getRHeight(){
Rect MLG= getProgressDrawable().getBounds();
return MLG.width();
}
public void drawMeter(Canvas c){
Paint paint = new Paint();
paint.setColor(0xffff7700);
paint.setStrokeWidth(2);
c.drawLine(
0,100,0, 0, paint);
c.drawLine(
getRHeight()/2,100,getRHeight()/2, 0, paint);
c.drawLine(
getRHeight(),100,getRHeight(), 0, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
onSizeChanged(getWidth(), getHeight(), 0, 0);
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
}
This should then look like this:
Here is my VerticalSeeker Class:
public class VerticalSeekBar extends SeekBar implements SeekBar.OnSeekBarChangeListener{
public VerticalSeekBar(Context context) {
super(context);
this.setOnSeekBarChangeListener(this);
}
public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public VerticalSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
}
#Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
protected void onDraw(Canvas c) {
c.rotate(-90);
c.translate(-getHeight(), 0);
super.onDraw(c);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
setProgress((int) (getMax() * event.getY() / getHeight()) - 0);
onSizeChanged(getWidth(), getHeight(), 0, 0);
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
}
#Override
public void onStartTrackingTouch(SeekBar seekBar)
{
Toast.makeText(this.getContext(), "Start", Toast.LENGTH_SHORT).show();
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
Toast.makeText(this.getContext(), "Stop", Toast.LENGTH_SHORT).show();
}
}
Can someone explain, why only the "onProgressChanged"-event is firing? How to get "onStartTrackingTouch" and "onStopTrackingTouch" to work?
At the minimum change your Motion event switch case code to
case MotionEvent.ACTION_UP:
setProgress(getMax()- (int) (getMax() * event.getY() / getHeight()));
onSizeChanged(getWidth(), getHeight(), 0, 0);
onChangeListener.onStopTrackingTouch(this);
Found complete implementation at http://hackskrieg.wordpress.com/2012/04/20/working-vertical-seekbar-for-android/
public class VerticalSeekBar extends SeekBar {
public VerticalSeekBar(Context context) {
super(context);
}
public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public VerticalSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
}
#Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
protected void onDraw(Canvas c) {
c.rotate(-90);
c.translate(-getHeight(), 0);
super.onDraw(c);
}
private OnSeekBarChangeListener onChangeListener;
#Override
public void setOnSeekBarChangeListener(OnSeekBarChangeListener onChangeListener){
this.onChangeListener = onChangeListener;
}
private int lastProgress = 0;
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
onChangeListener.onStartTrackingTouch(this);
setPressed(true);
setSelected(true);
break;
case MotionEvent.ACTION_MOVE:
super.onTouchEvent(event);
int progress = getMax() - (int) (getMax() * event.getY() / getHeight());
// Ensure progress stays within boundaries
if(progress < 0) {progress = 0;}
if(progress > getMax()) {progress = getMax();}
setProgress(progress); // Draw progress
if(progress != lastProgress) {
// Only enact listener if the progress has actually changed
lastProgress = progress;
onChangeListener.onProgressChanged(this, progress, true);
}
onSizeChanged(getWidth(), getHeight() , 0, 0);
setPressed(true);
setSelected(true);
break;
case MotionEvent.ACTION_UP:
onChangeListener.onStopTrackingTouch(this);
setPressed(false);
setSelected(false);
break;
case MotionEvent.ACTION_CANCEL:
super.onTouchEvent(event);
setPressed(false);
setSelected(false);
break;
}
return true;
}
public synchronized void setProgressAndThumb(int progress) {
setProgress(progress);
onSizeChanged(getWidth(), getHeight() , 0, 0);
if(progress != lastProgress) {
// Only enact listener if the progress has actually changed
lastProgress = progress;
onChangeListener.onProgressChanged(this, progress, true);
}
}
public synchronized void setMaximum(int maximum) {
setMax(maximum);
}
public synchronized int getMaximum() {
return getMax();
}
}
I've implemented the commonly-pointed-to VerticalSeekBar post here. As it is, the SeekBar operates a little quirky. Here is my slightly adapted onTouchEvent() from that example:
public boolean onTouchEvent(MotionEvent event)
{
xPos = event.getX();
yPos = event.getY();
oOffset = this.getThumbOffset();
oProgress = this.getProgress();
//Code from example - Not working
//this.setThumbOffset( progress * (this.getBottom()-this.getTop()) );
this.setProgress((int)(29*yPos/this.getBottom()));
return true;
}
I've managed to implement one VerticalSeekBar in which the progress updates as expected and is fully-functional, but the thumb does not follow suit. This is only a graphical glitch, so I'm overlooking it for now. But, it would be nice to have that working. This SeekBar has max = 20.
However, I tried implementing another VerticalSeekBar with max = 1000. Obviously, it uses the same code, so you'd assume the same behavior. I'm only able to achieve a progress of 0~35, even as my finger slides beyond the SeekBar and eventually off the screen. If I just tap near the end of the progress bar (which should be progress ~ 900) it returns a progress of about 35 and the yellow progress bar reflects that value by staying near the top.
My question is: Does anyone have a link to a working vertical SeekBar, or know how to adapt this particular example?
Here is a working VerticalSeekBar implementation:
package android.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
public class VerticalSeekBar extends SeekBar {
public VerticalSeekBar(Context context) {
super(context);
}
public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public VerticalSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
}
#Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
protected void onDraw(Canvas c) {
c.rotate(-90);
c.translate(-getHeight(), 0);
super.onDraw(c);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
onSizeChanged(getWidth(), getHeight(), 0, 0);
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
}
To implement it, create a new class in your project, choosing the right package:
There, paste the code and save it. Now use it in your XML layout:
<android.widget.VerticalSeekBar
android:id="#+id/seekBar1"
android:layout_width="wrap_content"
android:layout_height="200dp"
/>
The code given in the accepted answer didn't intercept the onStartTrackingTouch and the onStopTrackingTouch events, so I've modified it to have more control over this two events.
Here is my code:
public class VerticalSeekBar extends SeekBar {
private OnSeekBarChangeListener myListener;
public VerticalSeekBar(Context context) {
super(context);
}
public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public VerticalSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
}
#Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
#Override
public void setOnSeekBarChangeListener(OnSeekBarChangeListener mListener){
this.myListener = mListener;
}
protected void onDraw(Canvas c) {
c.rotate(-90);
c.translate(-getHeight(), 0);
super.onDraw(c);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if(myListener!=null)
myListener.onStartTrackingTouch(this);
break;
case MotionEvent.ACTION_MOVE:
setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
onSizeChanged(getWidth(), getHeight(), 0, 0);
myListener.onProgressChanged(this, getMax() - (int) (getMax() * event.getY() / getHeight()), true);
break;
case MotionEvent.ACTION_UP:
myListener.onStopTrackingTouch(this);
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
}
I had problems while using this code with setProgress method. To solve them I suggest overriding setProgress and adding onSizeChanged call to it.
I had problem while using this code with setProgress method. To solve them I suggest overriding setProgress and adding onSizeChanged call to it.Added code here ..
private int x,y,z,w;
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
this.x=w;
this.y=h;
this.z=oldw;
this.w=oldh;
}
#Override
public synchronized void setProgress(int progress) {
super.setProgress(progress);
onSizeChanged(x, y, z, w);
}
selected hover actions are performed by adding the following code:
1.setPressed(true);setSelected(true);//Add this in ACTION_DOWN
2.setPressed(false);setSelected(false);//Add this in ACTION_UP
And Write code for selected hover options in ur xml.
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:state_window_focused="true"
android:drawable="#drawable/thumb_h" />
<item android:state_selected="true"
android:state_window_focused="true"
android:drawable="#drawable/thumb_h" />
<item android:drawable="#drawable/thumb" />
</selector>
This is working for me...
Thanks to Paul Tsupikoff, Fatal1ty2787 and Ramesh for this excellent code.
Personally, I wanted a vertical slider that is upside-down compared to the given code. In other words, the value increases, rather than decreases, the lower the thumb. Changing four lines seems to have taken care of this.
First, I changed the onDraw() method as originally given by Paul. The rotate() and translate() calls now have these arguments:
c.rotate(90);
c.translate(0, -getWidth());
Then I made two changes to the ACTION_MOVE case in onTouchEvent() as given by Fatal1ty2787. The call to setProgress() now looks like this:
setProgress((int) (getMax() * event.getY() / getHeight()));
Finally, the call to onProgressChanged() looks like this:
myListener.onProgressChanged(this, (int) (getMax() * event.getY() / getHeight()), true);
Now, if only Google shared our interest in this feature....
For API 11 and later, can use seekbar's XML attributes(android:rotation="270") for vertical effect.
<SeekBar
android:id="#+id/seekBar1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:rotation="270"/>
For older API level (ex API10),use:
https://github.com/AndroSelva/Vertical-SeekBar-Android
Another idea could be to change the X and Y coordinates of the MotionEvent and pass them to the super-implementation:
public class VerticalSeekBar extends SeekBar {
public VerticalSeekBar(Context context) {
super(context);
}
public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public VerticalSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
float x = (getHeight() - event.getY()) * getWidth() / getHeight();
float y = event.getX();
MotionEvent verticalEvent = MotionEvent
.obtain(event.getDownTime(), event.getEventTime(), event.getAction(), x, y,
event.getPressure(), event.getSize(), event.getMetaState(),
event.getYPrecision(), event.getXPrecision(), event.getDeviceId(),
event.getEdgeFlags());
return super.onTouchEvent(verticalEvent);
}
protected void onDraw(Canvas c) {
c.rotate(-90);
c.translate(-getHeight(), 0);
super.onDraw(c);
}
#Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
}
}
In this case it is not necessary to call the setProgress(int) method and therefore you could use the boolean-flag "fromUser" in OnSeekBarChangeListener.onProgressChanged() to determine if the seeking was produced by an user interaction.
Based on Paul Tsupikoff's answer, here is the AppCompatVerticalSeekBar:
package com.my.apppackage;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.appcompat.widget.AppCompatSeekBar;
public class AppCompatVerticalSeekBar extends AppCompatSeekBar {
public AppCompatVerticalSeekBar(Context context) {
super(context);
}
public AppCompatVerticalSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AppCompatVerticalSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
}
#Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
#Override
protected synchronized void onDraw(Canvas canvas) {
canvas.rotate(-90);
canvas.translate(-getHeight(), 0);
super.onDraw(canvas);
}
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP: {
setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
onSizeChanged(getWidth(), getHeight(), 0, 0);
break;
}
case MotionEvent.ACTION_CANCEL: {
break;
}
}
return true;
}
}
Use it in your layout file:
<com.my.apppackage.AppCompatVerticalSeekBar
android:id="#+id/verticalSeekBar1"
android:layout_width="wrap_content"
android:layout_height="200dp" />
Target platforms
from Android 2.3.x (Gingerbread)
to Android 7.x (Nougat)
Getting started
This library is published on jCenter. Just add these lines to build.gradle.
dependencies {
compile 'com.h6ah4i.android.widget.verticalseekbar:verticalseekbar:0.7.2'
}
Usage
Layout XML
<!-- This library requires pair of the VerticalSeekBar and VerticalSeekBarWrapper classes -->
<com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper
android:layout_width="wrap_content"
android:layout_height="150dp">
<com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBar
android:id="#+id/mySeekBar"
android:layout_width="0dp"
android:layout_height="0dp"
android:max="100"
android:progress="0"
android:splitTrack="false"
app:seekBarRotation="CW90" /> <!-- Rotation: CW90 or CW270 -->
</com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper>
NOTE: android:splitTrack="false" is required for Android N+.
Java code
public class TestVerticalSeekbar extends AppCompatActivity {
private SeekBar volumeControl = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_vertical_seekbar);
volumeControl = (SeekBar) findViewById(R.id.mySeekBar);
volumeControl.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
int progressChanged = 0;
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
progressChanged = progress;
}
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
public void onStopTrackingTouch(SeekBar seekBar) {
Toast.makeText(getApplicationContext(), "seek bar progress:" + progressChanged,
Toast.LENGTH_SHORT).show();
}
});
}
}