自定義一個(gè)voiceview音量調(diào)節(jié)控件

記得以前有一個(gè)需求

QQ截圖20170607115701.png

做一個(gè)播放界面,左邊的是需要這種效果可以隨著拖動(dòng)調(diào)節(jié)音量,然后點(diǎn)擊速圖標(biāo)還可以收縮伸展開這個(gè)音量欄,尼瑪當(dāng)初的我做的那個(gè)累啊,整整弄了一個(gè)星期,大概寫了3,4個(gè)組合控件有3000多行代碼以及用了各種切圖,勉強(qiáng)弄好后還有點(diǎn)Bug,還依稀記得我們技術(shù)總監(jiān)最后看我的眼神~~~,當(dāng)然現(xiàn)在翻過頭來看這種需求也不是那么特別難了,前段時(shí)間正好沒事把它擼了出來。
我們看下怎么實(shí)現(xiàn)的。

public class VoiceView extends View 

首先定義VoiceView

    private void initView() {
        //音量大小默認(rèn)為1
        mSpeedLength = 5;
        //矩形弧度minimum
        mXRound = 30;
        mYRound = 30;

        //每個(gè)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;

        //右側(cè)圖片寬高
    }

我在創(chuàng)建控件的時(shí)候定義一個(gè)init方法進(jìn)行一些初始化,這里有初始速度,矩形弧度,每個(gè)音量塊的寬度以及每個(gè)音量塊的高度,音量指示器圖片等,這里我把指示器的寬度設(shè)置為音量塊寬度的二倍。

好我們主要看一下draw方法里面怎么實(shí)現(xiàn)的。主要的邏輯都在這里

 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);
            //設(shè)置初始的7個(gè)音量調(diào)節(jié)位置和大小   上下都是不變的變得是左右位置
            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++) {
                //定義每個(gè)Item音量的寬度
                voiceLeft = (i + 1) * mVoiceItemMarginLeft + i * mVoiceItemWidth;
                voiceRight = (i + 1) * mVoiceItemMarginLeft + i * mVoiceItemWidth + mVoiceItemWidth;
                canvas.drawRect(voiceLeft, mVoiceItemHeight, voiceRight, mMeasuredHeight - mVoiceItemHeight, backgroundPaint);
            }

    }

由于這個(gè)控件實(shí)現(xiàn)的比較單一所以實(shí)現(xiàn)細(xì)節(jié)直接寫里面了沒有抽出來,
首先繪制矩形,然后我們判斷右邊的bitmap是否為null,因?yàn)檫@是提供的一個(gè)開放方法這個(gè)圖片是由我們設(shè)置進(jìn)去的如果是null,那么mRightBitmapWidthAndHeight設(shè)置為0,否則我們把它的寬和高都設(shè)置為整個(gè)控件的高。

ok右邊的圖片繪制完后,正式開始擼我們的音量啦~一開始我默認(rèn)設(shè)置音量len=7我們分別繪制7個(gè)音塊

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

每個(gè)音塊的左邊距離等我們設(shè)置的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);
                }

這里我們把指示器默認(rèn)繪制在第四檔的位置,這里需要注意下,由于指示器的寬度是音塊寬度的二倍所以指示器的
左側(cè)距離=第四個(gè)音塊的左側(cè)距離-(指示器寬度-音塊寬度)/2
好了這里我們指示器和音塊全部繪制完畢了

 if(i==(mCurrVolum-1))

這里為啥要去-1呢你想啊我們繪制音塊的時(shí)候是從0開始的,假如
mCurrVolum值是4當(dāng)然要在index=3處繪制這個(gè)時(shí)候音塊長度為4

但是這個(gè)控件不能是靜態(tài)的啊,我們要滑動(dòng)控制它啊,我們重寫一下
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);
    }

可以看到代碼并不復(fù)雜,在滑動(dòng)和停止滑動(dòng)時(shí)候都調(diào)用了getscrolllenth()這個(gè)方法是個(gè)什么鬼我們貼上代碼看看


    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是當(dāng)我們觸摸到每個(gè)音塊最右邊的時(shí)候,這個(gè)音塊顯示或者消失,而方案2是觸摸到音塊左邊的時(shí)候顯示消失,這個(gè)根據(jù)個(gè)人習(xí)慣選擇了。
我們用控件寬度-最右側(cè)圖標(biāo)的寬度-音塊的左邊距/音量長度就可以得到每個(gè)音塊+左邊距長度

我們根據(jù)這個(gè)值和我們手指滑動(dòng)的距離跟新我們的音量大小,拿我們滑動(dòng)的當(dāng)前位置eventX/剛才計(jì)算的邊距 =當(dāng)前音量位置然后把結(jié)果賦值給我們mCurrVolum重新繪制一下,音量就更新好啦。

最終我們實(shí)現(xiàn)的效果如下

vBwQjCjwME.gif

3000多行代碼擼出來的效果現(xiàn)在200行左右就全部搞定省時(shí)省力,哈
這個(gè)控件雖然不是很復(fù)雜但是還是有應(yīng)用場景的的,貼上git地址:
老鐵們隨手給個(gè)Star支持下我這小小的進(jìn)步哈 ~
https://github.com/boboyuwu/voice-view

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,517評論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,087評論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,521評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,493評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,207評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,603評論 1 325
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,624評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,813評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,364評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,110評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,305評論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,874評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,532評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,953評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,209評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,033評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,268評論 2 375

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,692評論 25 708
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,158評論 4 61
  • 杲杲日出,彼草離離。 行邁靡靡,中心搖搖。 知我者知我所思,不知我者多說無益。 杲杲日出,彼稻青青。 行邁靡靡,中...
    倚詩愛世閱讀 294評論 0 1
  • 文.孫亮來到一間咖啡巴 他和她對面坐下 他面對著她開始慌了神的表達(dá) 她看著他在述說生活里的枝芽 一杯咖啡放糖 一杯...
    朦朧詩人孫亮閱讀 320評論 1 4
  • 為了把孩子培養(yǎng)成大腦的主人,要相信孩子的大腦是一個(gè)完整的客觀存在。也就是說承認(rèn)孩子是一個(gè)獨(dú)立的人性個(gè)體,其具有無限...
    金勇Maya閱讀 202評論 0 1