对于竖直的seekbar,大致有两种方式:
一、写一个verticalseekbar继承自seekbar;很简单的一种写法;
package com.ysl.verticalseekbar;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.SeekBar;
public class MySeekBar1 extends SeekBar {
public MySeekBar1(Context context) {
super(context);
}
public MySeekBar1(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MySeekBar1(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) {
// 将SeekBar转转90度
c.rotate(-90);
// 将旋转后的视图移动回来
c.translate(-getHeight(), 0);
Log.i("getHeight()", getHeight() + "");
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:
int i = 0;
// 获取滑动的距离
i = getMax() - (int) (getMax() * event.getY() / getHeight());
// 设置进度
setProgress(i);
Log.i("Progress", getProgress() + "");
// 每次拖动SeekBar都会调用
onSizeChanged(getWidth(), getHeight(), 0, 0);
Log.i("getWidth()", getWidth() + "");
Log.i("getHeight()", getHeight() + "");
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
}
这种错误可以用第二种方法就能解决了;
二、写一个verticalseekbar继承自absseekbar;重写其中的部分方法;
package com.ysl.verticalseekbar;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.AbsSeekBar;
import android.widget.SeekBar;
public class MySeekBar2 extends AbsSeekBar {
public MySeekBar2(Context context) {
super(context);
}
public MySeekBar2(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MySeekBar2(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.seekBarStyle);
}
private Drawable mThumb;
public interface OnSeekBarChangeListener {
void onProgressChanged(MySeekBar2 mySeekBar2, int progress, boolean fromUser);
void onStartTrackingTouch(MySeekBar2 mySeekBar2);
void onStopTrackingTouch(MySeekBar2 mySeekBar2);
}
private OnSeekBarChangeListener mOnSeekBarChangeListener;
public void setOnSeekBarChangeListener(OnSeekBarChangeListener l) {
mOnSeekBarChangeListener = l;
}
void onStartTrackingTouch() {
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStartTrackingTouch(this);
}
}
void onStopTrackingTouch() {
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStopTrackingTouch(this);
}
}
void onProgressRefresh(float scale, boolean fromUser) {
Drawable thumb = mThumb;
if (thumb != null) {
setThumbPos(getHeight(), thumb, scale, Integer.MIN_VALUE);
invalidate();
}
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), isPressed());
}
}
private void setThumbPos(int w, Drawable thumb, float scale, int gap) {
int available = w - getPaddingLeft() - getPaddingRight();
int thumbWidth = thumb.getIntrinsicWidth();
int thumbHeight = thumb.getIntrinsicHeight();
available -= thumbWidth;
// The extra space for the thumb to move on the track
available += getThumbOffset() * 2;
int thumbPos = (int) (scale * available);
int topBound, bottomBound;
if (gap == Integer.MIN_VALUE) {
Rect oldBounds = thumb.getBounds();
topBound = oldBounds.top;
bottomBound = oldBounds.bottom;
} else {
topBound = gap;
bottomBound = gap + thumbHeight;
}
thumb.setBounds(thumbPos, topBound, thumbPos + thumbWidth, bottomBound);
}
@Override
protected void onDraw(Canvas c) {
c.rotate(-90);// 反转90度,将水平SeekBar竖起来
c.translate(-getHeight(), 0);// 将经过旋转后得到的VerticalSeekBar移到正确的位置,注意经旋转后宽高值互换
super.onDraw(c);
}
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());// 宽高值互换
}
@Override
public void setThumb(Drawable thumb) {
mThumb = thumb;
super.setThumb(thumb);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldw, oldh);// 宽高值互换
}
// 与源码完全相同,仅为调用宽高值互换处理的onStartTrackingTouch()方法
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
setPressed(true);
onStartTrackingTouch();
trackTouchEvent(event);
break;
}
case MotionEvent.ACTION_MOVE: {
trackTouchEvent(event);
attemptClaimDrag();
break;
}
case MotionEvent.ACTION_UP: {
trackTouchEvent(event);
onStopTrackingTouch();
setPressed(false);
// ProgressBar doesn't know to repaint the thumb drawable
// in its inactive state when the touch stops (because the
// value has not apparently changed)
invalidate();
break;
}
case MotionEvent.ACTION_CANCEL: {
onStopTrackingTouch();
setPressed(false);
invalidate(); // see above explanation
break;
}
default:
break;
}
return true;
}
// 宽高值互换处理
private void trackTouchEvent(MotionEvent event) {
final int height = getHeight();
final int available = height - getPaddingBottom() - getPaddingTop();
int Y = (int) event.getY();
float scale;
float progress = 0;
if (Y > height - getPaddingBottom()) {
scale = 0.0f;
} else if (Y < getPaddingTop()) {
scale = 1.0f;
} else {
scale = (float) (height - getPaddingBottom() - Y) / (float) available;
}
final int max = getMax();
progress = scale * max;
setProgress((int) progress);
}
private void attemptClaimDrag() {
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
}
}
三、控件写好了,就剩下其他的代码了
xml文件如下:
package com.ysl.verticalseekbar;
import android.app.Activity;
import android.content.Context;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsSeekBar;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
public class MainActivity extends Activity {
private MySeekBar2 sb1;
private TextView tv;
private AudioManager audioManager;
private int currentVolume;
private int maxVolume;
private VolumeReceiver volumeReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
tv = (TextView) findViewById(R.id.tv);
sb1 = (MySeekBar2) findViewById(R.id.sb1);
sb1.setMax(maxVolume);
sb1.setProgress(currentVolume);
tv.setText(currentVolume*100/maxVolume + " %");
// sb1.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
//
// @Override
// public void onStopTrackingTouch(SeekBar arg0) {}
//
// @Override
// public void onStartTrackingTouch(SeekBar arg0) {}
//
// @Override
// public void onProgressChanged(SeekBar arg0, int progress, boolean arg2) {
// audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0);
// currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); //获取当前音量值
// sb1.setProgress(currentVolume);
// tv.setText(currentVolume*100/maxVolume + " %");
// }
// });
sb1.setOnSeekBarChangeListener(new MySeekBar2.OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(MySeekBar2 mySeekBar2) {}
@Override
public void onStartTrackingTouch(MySeekBar2 mySeekBar2) {}
@Override
public void onProgressChanged(MySeekBar2 mySeekBar2, int progress,
boolean fromUser) {
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0);
currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); //获取当前音量值
sb1.setProgress(currentVolume);
tv.setText(currentVolume*100/maxVolume + " %");
}
});
volumeReceiver = new VolumeReceiver(sb1);
registerReceiver(volumeReceiver, new IntentFilter("android.media.VOLUME_CHANGED_ACTION")) ;
}
@Override
protected void onDestroy() {
unregisterReceiver(volumeReceiver);
super.onDestroy();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:// 增大音量
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
AudioManager.ADJUST_RAISE, AudioManager.FLAG_PLAY_SOUND
| AudioManager.FLAG_SHOW_UI);
break;
case KeyEvent.KEYCODE_VOLUME_DOWN:// 减小音量
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
AudioManager.ADJUST_LOWER, AudioManager.FLAG_PLAY_SOUND
| AudioManager.FLAG_SHOW_UI);
break;
case KeyEvent.KEYCODE_HEADSETHOOK:
break;
default:
return super.onKeyDown(keyCode, event);
}
// 为true,则其它后台按键处理再也无法处理到该按键,为false,则其它后台按键处理可以继续处理该按键事件
return true;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
手机音了变化的广播接受者如下:
package com.ysl.verticalseekbar;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.widget.AbsSeekBar;
public class VolumeReceiver extends BroadcastReceiver {
private AbsSeekBar sb_volume;
public VolumeReceiver(AbsSeekBar sb_volume) {
this.sb_volume = sb_volume;
}
@Override
public void onReceive(Context context, Intent intent) {
AudioManager audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
//如果音量发生变化则更改seekbar的位置
if(intent.getAction().equals("android.media.VOLUME_CHANGED_ACTION")){
// 当前的媒体音量
int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) ;
sb_volume.setProgress(currentVolume) ;
}
}
}
同时又注册了音量变化的广播,从而改变seekbar的显示;
这样,seekbar和系统的音量值就可以完全关联起来了;不管哪一方改变,另一方都会随之改变了。
如果想要横向的,就把自定义的去掉,用系统默认的就行了。