自定义一个voiceview音量调节控件

记得以前有一个需求

QQ截图20170607115701.png

做一个播放界面,左边的是需要这种效果可以随着拖动调节音量,然后点击速图标还可以收缩伸展开这个音量栏,尼玛当初的我做的那个累啊,整整弄了一个星期,大概写了3,4个组合控件有3000多行代码以及用了各种切图,勉强弄好后还有点Bug,还依稀记得我们技术总监最后看我的眼神~~~,当然现在翻过头来看这种需求也不是那么特别难了,前段时间正好没事把它撸了出来。
我们看下怎么实现的。

public class VoiceView extends View 

首先定义VoiceView

    private void initView() {
        //音量大小默认为1
        mSpeedLength = 5;
        //矩形弧度minimum
        mXRound = 30;
        mYRound = 30;

        //每个item需要的宽度
        mVoiceItemWidth = 10;
        mVoiceItemHeight = 20;
        mMinimumVoiceHeight = mVoiceItemHeight+mVoiceItemHeight*1/3;
        mPointBitmap = Bitmap.createBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.point));

        //初始化音量控件初始狂宽高大小
        mMinWidth=100;
        mMinHeight=30;

        mPointWidth = mVoiceItemWidth*2;
        mCurrVolum = 4;

        //右侧图片宽高
    }

我在创建控件的时候定义一个init方法进行一些初始化,这里有初始速度,矩形弧度,每个音量块的宽度以及每个音量块的高度,音量指示器图片等,这里我把指示器的宽度设置为音量块宽度的二倍。

好我们主要看一下draw方法里面怎么实现的。主要的逻辑都在这里

 protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int count=0;
        mMeasuredWidth = getMeasuredWidth();
        mMeasuredHeight = getMeasuredHeight();
            Paint backgroundPaint = new Paint();
            backgroundPaint.setColor(Color.parseColor("#a4c2f4"));
            canvas.drawRoundRect(0, 0, mMeasuredWidth, mMeasuredHeight, mXRound, mYRound, backgroundPaint);
            //音量栏所需要的上下左右宽距离
            if(mRightImageBitmap!=null){
                mRightBitmapWidthAndHeight=mMeasuredHeight;
                //mVoiceItemMarginLeft = (mMeasuredWidth - mMeasuredHeight - (mVoiceItemWidth * mSpeedLength))/(mSpeedLength+1);
                RectF rectF = new RectF();
                rectF.set(mMeasuredWidth-mRightBitmapWidthAndHeight,0,mMeasuredWidth,mRightBitmapWidthAndHeight);
                canvas.drawBitmap(mRightImageBitmap,null,rectF,null);
                Log.e("wwww","isnull");
            }else{
                mRightBitmapWidthAndHeight=0;
                //mVoiceItemMarginLeft=(mMeasuredWidth - (mVoiceItemWidth * mSpeedLength))/(mSpeedLength+1);
                Log.e("wwww","isnullNO");
            }
            mVoiceItemMarginLeft = (mMeasuredWidth - mRightBitmapWidthAndHeight - (mVoiceItemWidth * mSpeedLength))/(mSpeedLength+1);
            int voiceLeft;
            int voiceRight;
            backgroundPaint.setColor(Color.WHITE);
            //设置初始的7个音量调节位置和大小   上下都是不变的变得是左右位置
            for (int i = 0; i < mCurrVolum; i++) {
                voiceLeft = (i + 1) * mVoiceItemMarginLeft + i * mVoiceItemWidth;
                voiceRight = (i + 1) * mVoiceItemMarginLeft + (i +1)* mVoiceItemWidth ;
                canvas.drawRect(voiceLeft, mVoiceItemHeight, voiceRight, mMeasuredHeight - mVoiceItemHeight, backgroundPaint);
                //绘制point
                if(i==(mCurrVolum-1)){
                    int pointLeft = voiceLeft - (mPointWidth-mVoiceItemWidth) / 2;
                    RectF rectF = new RectF();
                    rectF.set(pointLeft,0,pointLeft+mPointWidth,mPointWidth);
                    canvas.drawBitmap(mPointBitmap,null,rectF,null);
                    Log.e("TAG","mCurrVolum:"+mCurrVolum);
                }

            }

             backgroundPaint.setColor(Color.parseColor("#a4c2f4"));
            for (int i = mCurrVolum; i < mSpeedLength; i++) {
                //定义每个Item音量的宽度
                voiceLeft = (i + 1) * mVoiceItemMarginLeft + i * mVoiceItemWidth;
                voiceRight = (i + 1) * mVoiceItemMarginLeft + i * mVoiceItemWidth + mVoiceItemWidth;
                canvas.drawRect(voiceLeft, mVoiceItemHeight, voiceRight, mMeasuredHeight - mVoiceItemHeight, backgroundPaint);
            }

    }

由于这个控件实现的比较单一所以实现细节直接写里面了没有抽出来,
首先绘制矩形,然后我们判断右边的bitmap是否为null,因为这是提供的一个开放方法这个图片是由我们设置进去的如果是null,那么mRightBitmapWidthAndHeight设置为0,否则我们把它的宽和高都设置为整个控件的高。

ok右边的图片绘制完后,正式开始撸我们的音量啦~一开始我默认设置音量len=7我们分别绘制7个音块

            for (int i = 0; i < mCurrVolum; i++) {
                voiceLeft = (i + 1) * mVoiceItemMarginLeft + i * mVoiceItemWidth;
                voiceRight = (i + 1) * mVoiceItemMarginLeft + (i +1)* mVoiceItemWidth ;
                canvas.drawRect(voiceLeft, mVoiceItemHeight, voiceRight, mMeasuredHeight - mVoiceItemHeight, backgroundPaint);
            }

每个音块的左边距离等我们设置的marginleft+音块的宽度

然后开始绘制指示器point

                //绘制point
                if(i==(mCurrVolum-1)){
                    int pointLeft = voiceLeft - (mPointWidth-mVoiceItemWidth) / 2;
                    RectF rectF = new RectF();
                    rectF.set(pointLeft,0,pointLeft+mPointWidth,mPointWidth);
                    canvas.drawBitmap(mPointBitmap,null,rectF,null);
                    Log.e("TAG","mCurrVolum:"+mCurrVolum);
                }

这里我们把指示器默认绘制在第四档的位置,这里需要注意下,由于指示器的宽度是音块宽度的二倍所以指示器的
左侧距离=第四个音块的左侧距离-(指示器宽度-音块宽度)/2
好了这里我们指示器和音块全部绘制完毕了

 if(i==(mCurrVolum-1))

这里为啥要去-1呢你想啊我们绘制音块的时候是从0开始的,假如
mCurrVolum值是4当然要在index=3处绘制这个时候音块长度为4

但是这个控件不能是静态的啊,我们要滑动控制它啊,我们重写一下
onTouchEvent方法

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN:
                return true;

            case ACTION_MOVE:
                getScrollLength(event);
                invalidate();
                return true;
            case MotionEvent.ACTION_UP:
                getScrollLength(event);
                invalidate();
                return true;

        }
        return super.onTouchEvent(event);
    }

可以看到代码并不复杂,在滑动和停止滑动时候都调用了getscrolllenth()这个方法是个什么鬼我们贴上代码看看


    private void getScrollLength(MotionEvent event) {
        int eventX= (int) (event.getX()+0.5f);
/*
        if(eventX>0&&eventY>0&&eventX<mMeasuredWidth-mMeasuredHeight&&eventY<mMeasuredHeight){*/

             //方案1:
            int  marginLeftAndVoiceWidth= (mMeasuredWidth - mRightBitmapWidthAndHeight - mVoiceItemMarginLeft) / mSpeedLength;

             //方案2:
            //int  marginLeftAndVoiceWidth= mVoiceItemMarginLeft;
            mCurrVolum=eventX/marginLeftAndVoiceWidth;
            if(mCurrVolum>mSpeedLength){
                mCurrVolum=mSpeedLength;
            }
            Log.e("TAG","volum:"+mCurrVolum);
            if(mOnSpeedClickListener!=null){
                mOnSpeedClickListener.onSpeedChange(mCurrVolum);
            }
    }

这里采用的方案1是当我们触摸到每个音块最右边的时候,这个音块显示或者消失,而方案2是触摸到音块左边的时候显示消失,这个根据个人习惯选择了。
我们用控件宽度-最右侧图标的宽度-音块的左边距/音量长度就可以得到每个音块+左边距长度

我们根据这个值和我们手指滑动的距离跟新我们的音量大小,拿我们滑动的当前位置eventX/刚才计算的边距 =当前音量位置然后把结果赋值给我们mCurrVolum重新绘制一下,音量就更新好啦。

最终我们实现的效果如下

vBwQjCjwME.gif

3000多行代码撸出来的效果现在200行左右就全部搞定省时省力,哈
这个控件虽然不是很复杂但是还是有应用场景的的,贴上git地址:
老铁们随手给个Star支持下我这小小的进步哈 ~
https://github.com/boboyuwu/voice-view

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,057评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,945评论 4 60
  • 杲杲日出,彼草离离。 行迈靡靡,中心摇摇。 知我者知我所思,不知我者多说无益。 杲杲日出,彼稻青青。 行迈靡靡,中...
    倚诗爱世阅读 282评论 0 1
  • 文.孙亮来到一间咖啡巴 他和她对面坐下 他面对着她开始慌了神的表达 她看着他在述说生活里的枝芽 一杯咖啡放糖 一杯...
    朦胧诗人孙亮阅读 308评论 1 4
  • 为了把孩子培养成大脑的主人,要相信孩子的大脑是一个完整的客观存在。也就是说承认孩子是一个独立的人性个体,其具有无限...
    金勇Maya阅读 194评论 0 1