竖直方向的seekbar

     对于竖直的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;
	}
}

  这种写法,可以实现和音量关联(我做的是媒体音量的关联),  只是有一个小小的问题,我同时又关联的手机的上下音量的物理按键;在操作seekbar时是正常的,可是但按物理按键调节音量时,seekbar的滑块会显示错误。

这种错误可以用第二种方法就能解决了;


二、写一个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);  
        }  
    }
}

这种方法是从源码中来改写的,可以参考这篇文章:http://blog.csdn.net/ueryueryuery/article/details/20608463;


三、控件写好了,就剩下其他的代码了


xml文件如下:



    
    



其中引用的自定义控件可以自己选;还有个style文件如下:


        

下面是我的activity中的代码:

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);
    }
}

其中关联的系统音量变化而改变seekbar的滑块位置,使用的是系统音量发生音量变化所发出的广播来实现的;只需写一个广播接受者,并且注册这个广播就可以了;

手机音了变化的广播接受者如下:

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的setOnSeekBarChangeListener()中设置了,系统的音量;

同时又注册了音量变化的广播,从而改变seekbar的显示;

这样,seekbar和系统的音量值就可以完全关联起来了;不管哪一方改变,另一方都会随之改变了。

如果想要横向的,就把自定义的去掉,用系统默认的就行了。









你可能感兴趣的:(android,样式)