Android 麥克風錄音帶音量大小動態顯示的圓形自定義View


1、所謂無圖無真相,先上效果圖。我們要實現的就是中間那個錄音的按鈕,周邊會顯示一圈音量大小的波形

 

 

2、VolumCircleBar繼承自View,我們進行了自定義,代碼如下

package com.rdinfo.ccenglish.ui.ccprofile.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

import com.panshi.xuexiao.R;
/**
 * 麥克風音量圓形按鈕
 * @author hiphonezhu@gmail.com
 * @version [CCEnglish, 2014-7-25]
 */
public class VolumCircleBar extends View
{
    private double volumRate; // 音量百分比
    private boolean isRecording; // 錄音標志
    private Object lock = new Object();
    private Thread uiThread;
    private Paint mPaint;
    private RectF arcRect;
    private Matrix matrix = new Matrix();
    private final int VOLUM_INDICATE_LENGTH = 5; // 音量大小線長度
    private final int CIRCLE_INNER_DISTANCE_TO_OUTSIDE = 5; // 內切圓距離外圓的距離
    public VolumCircleBar(Context context)
    {
        this(context, null);
    }
    
    public VolumCircleBar(Context context, AttributeSet attrs)
    {
        this(context, attrs, 0);
    }
    
    public VolumCircleBar(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.VolumCircleBar, defStyle, 0); 
        init(typedArray);
    }
    
    private int recordingColor; // 錄音背景色
    private int stoppedColor; // 停止背景色
    private Bitmap centerRes; // 中間麥克風圖片
    private int totalBlockCount; // 塊數量
    private int spliteAngle; // 塊之間的間隔角度大小
    private int circleWidth; // 直徑
    /**
     * 初始化
     */
    private void init(TypedArray typedArray)
    {
        for (int i = 0; i < typedArray.length(); i++)  
        {  
            int attr = typedArray.getIndex(i);  
            switch (attr)
            {
                case R.styleable.VolumCircleBar_recordingColor:
                    recordingColor = typedArray.getColor(i, Color.GREEN);
                    break;
                case R.styleable.VolumCircleBar_stoppedColor:
                    stoppedColor = typedArray.getColor(i, Color.GRAY);
                    break;
                case R.styleable.VolumCircleBar_centerRes:
                    centerRes = BitmapFactory.decodeResource(getContext().getResources(), typedArray.getResourceId(i, R.drawable.ic_launcher));
                    break;
                case R.styleable.VolumCircleBar_blockCount:
                    totalBlockCount = typedArray.getInt(i, 50);
                    break;
                case R.styleable.VolumCircleBar_splitAngle:
                    spliteAngle = typedArray.getInt(i, 2);
                    break;
            }
        }  
        typedArray.recycle();  
        uiThread = Thread.currentThread();
        mPaint = new Paint();
        if (spliteAngle * totalBlockCount > 360)
        {
            throw new IllegalArgumentException("spliteAngle * blockCount > 360, while the result should be less than 360.");
        }
        
        // debug for test
        isRecording = true;
        volumRate = 0.5;
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        // 直徑
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        circleWidth = width > height? width : height;
        if (arcRect == null)
        {
            arcRect = new RectF(CIRCLE_INNER_DISTANCE_TO_OUTSIDE, CIRCLE_INNER_DISTANCE_TO_OUTSIDE, 
                    circleWidth - CIRCLE_INNER_DISTANCE_TO_OUTSIDE, circleWidth - CIRCLE_INNER_DISTANCE_TO_OUTSIDE); // 音量顯示區域, 內偏移幾個像素
            // 圖片處理矩陣
            initBitmapMatrix();
        }
        // 強制設置view大小
        setMeasuredDimension(circleWidth, circleWidth);
    }
    
    /**
     * 中間圖片壓縮處理
     */
    private void initBitmapMatrix()
    {
        float innerCircleRadius = (circleWidth - 2 * (VOLUM_INDICATE_LENGTH + CIRCLE_INNER_DISTANCE_TO_OUTSIDE)) / 2f; // 內圓的半徑
        float innerRectangleWidth = (float)Math.cos((Math.PI / 180) * 45) * innerCircleRadius * 2; // 內圓的內切正方形的邊長
        float translateOffset = VOLUM_INDICATE_LENGTH + CIRCLE_INNER_DISTANCE_TO_OUTSIDE + innerCircleRadius - innerRectangleWidth / 2; // 偏移的offset
        if (centerRes.getWidth() > (innerRectangleWidth) || centerRes.getHeight() > (innerRectangleWidth))
        {
            // 圖片寬度或高度大於(直徑-內偏移), 等比壓縮
            if (centerRes.getWidth() > centerRes.getHeight())
            {
                // 按照寬度壓縮
                float ratio = innerRectangleWidth / centerRes.getWidth();
                matrix.postScale(ratio, ratio);
                float translateY = (innerRectangleWidth - (centerRes.getHeight() * ratio)) / 2f;
                // 在縱坐標方向上進行偏移,以保證圖片居中顯示
                matrix.postTranslate(translateOffset, translateY + translateOffset);
            }
            else
            {
                // 按照高度壓縮
                float ratio = innerRectangleWidth / (centerRes.getHeight() * 1.0f);
                matrix.postScale(ratio, ratio);
                float translateX = (innerRectangleWidth - (centerRes.getWidth() * ratio)) / 2f;
                // 在橫坐標方向上進行偏移,以保證圖片居中顯示
                matrix.postTranslate(translateX + translateOffset, translateOffset);
            }
        }
        else
        {
            // 當圖片的寬高都小於屏幕寬高時,直接讓圖片居中顯示
            float translateX = (innerRectangleWidth - centerRes.getWidth()) / 2f;
            float translateY = (innerRectangleWidth - centerRes.getHeight()) / 2f;
            matrix.postTranslate(translateX + translateOffset, translateY + translateOffset);
        }
    }
    
    /**
     * 設置音量百分比
     * @param rate
     */
    public void updateVolumRate(double rate)
    {
        synchronized (lock)
        {
            this.volumRate = rate;
            if (Thread.currentThread() != uiThread)
            {
                postInvalidate();
            }
            else
            {
                invalidate();
            }
        }
    }
    
    /**
     * 開始、停止錄音
     */
    public void toggleRecord()
    {
        synchronized (lock)
        {
            isRecording = !isRecording;
            if (Thread.currentThread() != uiThread)
            {
                postInvalidate();
            }
            else
            {
                invalidate();
            }
        }
    }
    
    @Override
    protected void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);
        canvas.drawColor(Color.TRANSPARENT); 
        synchronized (lock)
        {
            if (isRecording) // 正在錄音
            {
                //1.繪制綠色圓圈
                mPaint.setAntiAlias(true); //消除鋸齒   
                mPaint.setColor(recordingColor);
                mPaint.setStrokeWidth(1);
                mPaint.setStyle(Paint.Style.FILL); // 填充
                canvas.drawCircle(circleWidth / 2f, circleWidth / 2f, circleWidth / 2f, mPaint);
                //2.根據音量百分比、塊數量、塊間隔大小計算角度動態繪制音量大小
                // 計算塊的角度
                float blockAngle = (360 * 1.0f - spliteAngle * totalBlockCount) / totalBlockCount;
                int drawBlockCount = (int)(totalBlockCount * volumRate); // 繪制的block數量
                mPaint.setStrokeWidth(VOLUM_INDICATE_LENGTH);
                mPaint.setColor(stoppedColor);
                mPaint.setStyle(Paint.Style.STROKE); // 空心
                for (int i = 0; i < drawBlockCount; i++)
                {
                    canvas.drawArc(arcRect, i * (blockAngle + spliteAngle) - 90, blockAngle, false, mPaint);
                }
            } 
            else // 錄音停止
            {
                //1.繪制灰色圓圈
                mPaint.setColor(stoppedColor);
                mPaint.setStrokeWidth(1);
                mPaint.setStyle(Paint.Style.FILL); // 填充
                canvas.drawCircle(circleWidth / 2f, circleWidth / 2f, circleWidth / 2f, mPaint);
            }
        }
        // 繪制中間話筒
        canvas.drawBitmap(centerRes, matrix, null);
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM