From:http://www.cnblogs.com/wenjiang/archive/

2013/05/10/3071103.html

做音乐播放器,有时会要求EQ均衡器,但android默认的样式是水平的,这时就需要费点心思了。

先是实现默认SeekBar样式的EQ均衡器:

这是4.0以上默认样式的SeekBar,2.3或以下就像是进度条一样。

要实现这样的效果,其实并不难,先贴上源码:

public class MainActivity extends Activity {private MediaPlayer mMediaPlayer;private Equalizer mEqualizer;private LinearLayout mLayout;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setVolumeControlStream(AudioManager.STREAM_MUSIC);mLayout = new LinearLayout(this);mLayout.setOrientation(LinearLayout.VERTICAL);setContentView(mLayout);mMediaPlayer = new MediaPlayer();try {mMediaPlayer.setDataSource("/sdcard/陪我去流浪.mp3");mMediaPlayer.prepare();mMediaPlayer.start();} catch (IllegalArgumentException e) {// TODO Auto-generated catch block
            e.printStackTrace();} catch (SecurityException e) {// TODO Auto-generated catch block
            e.printStackTrace();} catch (IllegalStateException e) {// TODO Auto-generated catch block
            e.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch block
            e.printStackTrace();}setEqualize();}@TargetApi(Build.VERSION_CODES.GINGERBREAD)private void setEqualize() {mEqualizer = new Equalizer(0, mMediaPlayer.getAudioSessionId());mEqualizer.setEnabled(true);short bands = mEqualizer.getNumberOfBands();final short minEqualizer = mEqualizer.getBandLevelRange()[0];final short maxEqualizer = mEqualizer.getBandLevelRange()[1];for (short i = 0; i < bands; i++) {final short band = i;TextView freqTextView = new TextView(this);freqTextView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));freqTextView.setGravity(Gravity.CENTER_HORIZONTAL);freqTextView.setText((mEqualizer.getCenterFreq(band) / 1000) + "HZ");mLayout.addView(freqTextView);LinearLayout row = new LinearLayout(this);row.setOrientation(LinearLayout.HORIZONTAL);TextView minDbTextView = new TextView(this);minDbTextView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));minDbTextView.setText((minEqualizer / 100) + " dB");TextView maxDbTextView = new TextView(this);maxDbTextView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));maxDbTextView.setText((maxEqualizer / 100) + " dB");LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);SeekBar seekbar = new SeekBar(this);seekbar.setLayoutParams(layoutParams);seekbar.setMax(maxEqualizer - minEqualizer);seekbar.setProgress(mEqualizer.getBandLevel(band));seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@TargetApi(Build.VERSION_CODES.GINGERBREAD)@Overridepublic void onProgressChanged(SeekBar seekBar, int progress,boolean fromUser) {// TODO Auto-generated method stub
                    mEqualizer.setBandLevel(band,(short) (progress + minEqualizer));}});row.addView(minDbTextView);row.addView(seekbar);row.addView(maxDbTextView);mLayout.addView(row);}}@TargetApi(Build.VERSION_CODES.GINGERBREAD)@Overrideprotected void onPause() {// TODO Auto-generated method stubsuper.onPause();if (isFinishing() && mMediaPlayer != null) {mMediaPlayer.release();mEqualizer.release();mMediaPlayer = null;}}
}

这里我采用了手动设置Layout布局的方式,因为上面的Layout采用LinearLayout实现,并且每个SeekBar的属性设置都一样,考虑到SeekBar的数量较多,采用这个方式可能会更好点,至少我们不需要写太繁琐的Layout布局文件。
      撇开那些Layout的布局代码,我们看看最主要的部分:SeekBar和Equalizer的设置。

android系统提供内置的Equalizer支持,我们可以直接声明并且使用。但必须注意,当我们在代码中使用Equalizer的时候,其实就是调整音量(EQ均衡器是改变音频使得声音发生变化,像是洪亮或者低沉)。所以,我们需要在我们的代码中声明这么一句:

setVolumeControlStream(AudioManager.STREAM_MUSIC);

因为涉及到硬件方面的修改,我们需要权限:

<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

它允许我们进行全局的音频设置。
      我们再来看看这句:

mEqualizer = new Equalizer(0, mMediaPlayer.getAudioSessionId());

每个MediaPlayer都有自己独一无二的SessionId,我们需要将Equalizer附加到这个MediaPlayer上,就必须获取该SessionId。然后我们再创建一个优先级为0的Equalizer对象。所谓的优先级,是因为一个Equalizer engine可以被多个应用程序共享,所以我们必须设置优先级,优先级0代表该应用程序为正常级别。
     要想启动Equalizer,我们还必须这样子:

mEqualizer.setEnabled(true);

这就是学过单片机的同学非常熟悉的"使能"。

然后我们再获取支持的频谱:

 short bands = mEqualizer.getNumberOfBands();

不同的硬件设备支持的频谱是不一样的,像是电脑能支持的频谱就比手机要多得多。
    接着就是获取频谱中的等级范围,我们只需要获取最低和最高即可。

final short minEqualizer = mEqualizer.getBandLevelRange()[0];final short maxEqualizer = mEqualizer.getBandLevelRange()[1];

接下来就是遍历频谱,设置SeekBar了:

seekbar.setMax(maxEqualizer - minEqualizer);
seekbar.setProgress(mEqualizer.getBandLevel(band));seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@TargetApi(Build.VERSION_CODES.GINGERBREAD)@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {mEqualizer.setBandLevel(band, (short) (progress + minEqualizer));}
});

我们必须设置SeekBar的最大进度范围,也就是进度顶端所代表的值。接着就是实现SeekBar进度改变时,Equalizer的音频也跟着相应的改变,这就需要监听SeekBar。
      好了,到了这里,基本的EQ均衡器已经实现了,但我们想要的并不是功能的实现,而是界面的实现(这往往是我们这些移动互联网开发者的恶梦,美工设计师们从来都没有考虑过我们要设计出他们的界面有时是多么难甚至不可能的一件事啊!)。

既然默认的SeekBar样式无法满足我们的要求,我们只能自定义自己想要的SeekBar了。幸好,android还是提供了这种支持:只要继承自AbsSeekBr,我们就能得到自己想要的SeekBar了。

依然先上源码:

public class VerticalSeekBar extends AbsSeekBar {private Drawable mThumb;private int height;private int width;public interface OnSeekBarChangeListener {void onProgressChanged(VerticalSeekBar VerticalSeekBar, int progress,boolean fromUser);void onStartTrackingTouch(VerticalSeekBar VerticalSeekBar);void onStopTrackingTouch(VerticalSeekBar VerticalSeekBar);}private OnSeekBarChangeListener mOnSeekBarChangeListener;public VerticalSeekBar(Context context) {this(context, null);}public VerticalSeekBar(Context context, AttributeSet attrs) {this(context, attrs, android.R.attr.seekBarStyle);}public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}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(),fromUser);}}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;
        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);}protected voidonDraw(Canvas c) {c.rotate(-90);c.translate(-height, 0);super.onDraw(c);}protected synchronized void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {height = View.MeasureSpec.getSize(heightMeasureSpec) / 2;width = 50;this.setMeasuredDimension(width, height);}@Overridepublic void setThumb(Drawable thumb) {mThumb = thumb;super.setThumb(thumb);}protected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(h, w, oldw, oldh);}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);break;case MotionEvent.ACTION_CANCEL:onStopTrackingTouch();setPressed(false);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);}}public boolean dispatchKeyEvent(KeyEvent event) {if (event.getAction() == KeyEvent.ACTION_DOWN) {KeyEvent newEvent = null;switch (event.getKeyCode()) {case KeyEvent.KEYCODE_DPAD_UP:newEvent = new KeyEvent(KeyEvent.ACTION_DOWN,KeyEvent.KEYCODE_DPAD_RIGHT);break;case KeyEvent.KEYCODE_DPAD_DOWN:newEvent = new KeyEvent(KeyEvent.ACTION_DOWN,KeyEvent.KEYCODE_DPAD_LEFT);break;case KeyEvent.KEYCODE_DPAD_LEFT:newEvent = new KeyEvent(KeyEvent.ACTION_DOWN,KeyEvent.KEYCODE_DPAD_DOWN);break;case KeyEvent.KEYCODE_DPAD_RIGHT:newEvent = new KeyEvent(KeyEvent.ACTION_DOWN,KeyEvent.KEYCODE_DPAD_UP);break;default:newEvent = new KeyEvent(KeyEvent.ACTION_DOWN,event.getKeyCode());break;}return newEvent.dispatch(this);}return false;}
}

代码很长,但关键是我上面标记的几个方法。
       onDraw()该方法就是实现竖直SeekBar的关键。我们可以看到,关键就是那个c.rotate(-90),它实现了我们的竖直:通过反转-90度。setThumbPos()方法是为了调整thumb的大小,这个很重要,尤其是当我们开始排版的时候,thumb很可能因为布局的问题而显示不完全或者根本就不见了。onMeasure()针对的是进度条的设置。至于具体的大小怎样设置,得看大家自己的具体应用了。

这个是实现的效果:

前面的代码基本保持不变,只要将SeekBar改为VerticalSeekBar就可以了:

for (short i = 0; i < bands; i++) {final short band = i;int index = (int) i;VerticalSeekBar seekBar = (VerticalSeekBar) findViewById(seekBars[index]);seekBar.setMax(maxEqualizer - minEqualizer);seekBar.setProgress(mEqualizer.getBandLevel(band));seekBar.setOnSeekBarChangeListener(new VerticalSeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(VerticalSeekBar VerticalSeekBar,int progress, boolean fromUser) {mEqualizer.setBandLevel(band,(short) (progress + minEqualizer));}@Overridepublic void onStartTrackingTouch(VerticalSeekBar VerticalSeekBar) {// TODO Auto-generated method stub
}@Overridepublic void onStopTrackingTouch(VerticalSeekBar VerticalSeekBar) {// TODO Auto-generated method stub
}});

为了实现我上面的布局效果,我这次使用了RelativeLayout布局文件,至于那些间距的调整,就留给读者们了。

这样的效果还是不行的,因为有个致命的缺陷:android2.3的默认样式和android4.0的默认样式是不一样的!别看我上面的效果挺美观的,在android2.3上可不是这样。

最好的解决方法就是替换默认样式。

替换默认样式的方法很简单:在res目录下,新建drawable文件夹,然后我们在该目录下新建一个xml文件:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" ><item android:id="@android:id/secondaryProgress"><clip android:drawable="@drawable/progress2" /></item><item android:id="@android:id/progress"><clip android:drawable="@drawable/progress1" /></item></layer-list>

我们可以使用layer-list来替换默认样式,secondaryProgress指的是我们进度条中剩下的进度,progress指的就是我们当前的进度。
     分别设置好它们的替换图片后,我们再在SeekBar中这样设置:

<com.example.eqpratice.VerticalSeekBarandroid:id="@+id/sec"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_toRightOf="@id/fir"android:progressDrawable="@drawable/progress"android:secondaryProgress="100"android:thumb="@drawable/thumb" />

这里android: secondaryProgress="100"设置的是我们thumb能达到的最大进度,这个是要设置的,不然会出现thumb超出进度条的情况。
     现在的效果如图:

大家可以根据自己的需要,自定义自己想要的样式。

因为是菜鸟,很多东西都是一笔带过,写得不好或者是有错的,还请各位大神指教。

android手机上实现竖直seekbar的EQ均衡器相关推荐

  1. 如何在 Android 手机上实现抓包?

    如何在 Android 手机上实现抓包? http://www.zhihu.com/question/20467503 我想知道某个应用究竟在数据提交到哪里,提交了什么. 网上的教程太复杂,不想麻烦. ...

  2. Cocos2dx游戏开发系列笔记9:android手机上运行《战神传说》,并解决横竖屏即分辨率自适应问题

    转载:http://blog.csdn.net/iamlazybone/article/details/17191539 懒骨头(http://blog.csdn.net/iamlazybone  Q ...

  3. Cocos2dx游戏开发系列笔记9:android手机上运行《战神传说》,并解决横竖屏即分辨率自适应...

    2019独角兽企业重金招聘Python工程师标准>>> 上节说到cygwin下成功编译出so文件,下面我们要把游戏运行在android上. 开始干活! 其实步骤可以参考 Cocos2 ...

  4. android手机进行android开发,如何在Android手机上进行自动化测试(上)

    版权声明:允许转载,但转载必须保留原链接:请勿用作商业或者非法用途 前言 通过阅读本节教程,你将了解到以下内容: 如何在脚本代码中.运行脚本时指定手机 如何填写--device Android:/// ...

  5. unity3d shader编程中GrabPass 在某些android手机上失效的解决方案

    unity3d shader编程中GrabPass 在某些android手机上失效的解决方案 参考文章: (1)unity3d shader编程中GrabPass 在某些android手机上失效的解决 ...

  6. 如何在Android手机上进行Google Map的开发。

    1.题记 提起谷歌Map相信大家都不会陌生,那进入我们今天的话题,如何在Android手机上进行Google Map的开发. 2.Map应用程序的开发 2.1 准备工作 2.1.1 申请Android ...

  7. android 如何打开s3db,[转载]xe5 android 手机上使用sqlite

    本篇我们介绍一下在android手机上怎样使用sqlite数据库,这里用Navigator实现 增删改查. 1.新建firemonkey mobile application 2.选择blank ap ...

  8. 编写一个可在android手机上运行的jar包

    我们知道,在pc上通过 java -jar   xxx.jar  即可运行该jar包,那么在android手机上如何看运行jar包呢 一.首先编写一个程序(可在这里下载测试文件:https://dow ...

  9. Android 在Android手机上获取其他应用的包名及版本号

    获取Android手机上其他应用的包名及版本号方法有很多,可以通过AAPT从APK包中直接获取,也可以通过代码在手机上获取.显然,对于产品或者用户来说要获取这些信息,在手机上获取更为简便. 下面我们来 ...

最新文章

  1. 微软“叛变”谷歌 Chromium!
  2. LINQ To SQL 语法及实例大全
  3. linux中menu命令,menucofig 详解
  4. 中原工学院c语言期末考试题,中原工学院软件学院 2010年C语言 试卷A
  5. 用ASP.NET Core 2.1 建立规范的 REST API -- 保护API和其它
  6. EE JSP:使用自定义标签库生成动态内容
  7. 【转】【重要】破除“系统学习”的情结
  8. Devexpress控件中TreeList的递归绑定数据
  9. 洛谷——P1422 小玉家的电费
  10. Vue router-link 两种传参方法及参数的使用
  11. iOS CoreData (二) 版本升级和数据库迁移
  12. Git下载安装及设置详细教程
  13. Xposed框架分析
  14. html高德地图无法显示,关于高德地图不显示的问题
  15. mach_absolute_time 高效计算时间差
  16. 详解PON基础知识:OLT、ONU、ONT和ODN
  17. Android开发之自定义圆角矩形图片ImageView
  18. Factory Track 7 链接 Infor CloudSuite Industrial 10 操作设置
  19. 科普文章-另一个视角解读计算机编码(修订版)
  20. 04特性源码分析-ReentrantReadWriteLock原理-AQS-并发编程(Java)

热门文章

  1. 【Android 开发实例】时间管理APP开发
  2. vue-07 表单双向绑定-视频自学
  3. 研一Python基础课程第三周课后习题分享(下)(含源代码)
  4. 苹果计算机怎么开科学,iPhone技巧篇 教你如何调用科学计算器
  5. 《SMPTE RP 168-2002》阅读整理
  6. UILabelUITextView文本嵌入图片处理
  7. 【视频版】PDF合并器破解视频教程
  8. python量化交易:筹码分布(3)_基础知识及计算原理
  9. Polyworks脚本开发学习笔记(十九)-将数据对象与参考对象对齐的方法
  10. 会覆盖本地_新服务进阶,阿里本地生活开启“三环阵营”