Android 音量调节View

来源:互联网 发布:简单数据库软件 编辑:程序博客网 时间:2024/05/21 22:33

导语

手机直播一般都会通过移动屏幕来调节音量的大小,本篇只实现了图例,并不能改变音量
先看效果:
这里写图片描述

需要的素材:小喇叭图片,点击这里获取
这里写图片描述

预热

如果你对Path,PathMeasure,RectF,Canvas等不适很了解的话,强烈建议看这位哥们的教程:
点击这里查看教程
如果你将这哥们的十几篇帖子都看完了的话,这个View实际上是非常简单的

步骤介绍

用动态图来介绍:

这里写图片描述

这里用文字翻译下:

  1. 将小喇叭画到中心位置
  2. 围绕着喇叭画一个圆圈,浅色的
  3. 画一个圆弧,深色的
  4. 根据触摸的位置来改变圆弧的大小

分解之后,发现并没有什么难度(可能本身就没有什么难度),下面来看每一步的操作,最后会将整个View的代码贴出来

绘制小喇叭

这里写图片描述

相关代码片(不要复制,只是看的)

@Overrideprotected void onDraw(Canvas canvas) {    super.onDraw(canvas);    //坐标移动到中心    canvas.translate(mViewWidth / 2, mViewHeight / 2);    //拿到小喇叭图片    Bitmap voice = BitmapFactory.decodeResource(getResources(), R.mipmap.voice);    //获取图片的宽高    int bWidth = voice.getWidth();    int bHeight = voice.getHeight();    //移动坐标到中心位置    canvas.drawBitmap(voice, -bWidth / 2, -bHeight / 2, outerCirclePaint);}

分析
核心:将图片放到中心位置:
将图片向上移动高度的一半,向左移动宽度的一半,就可以移动到中心如图所示:
这里写图片描述

绘制浅色的圆环

这里写图片描述
相关代码片(不要复制,只是看的)

@Overrideprotected void onDraw(Canvas canvas) {    super.onDraw(canvas);    initPaint();    //坐标移动到中心    canvas.translate(mViewWidth / 2, mViewHeight / 2);    //底层圆圈    if (cirPath == null)        cirPath = new Path();    if (rectF == null) {        //半径选取为图片宽度一半的1.3倍,可以调节        r = (int) (bWidth / 2 * 1.3f);        rectF = new RectF(-r, -r, r, r);    }    cirPath .addArc(rectF, 0, 360);    //画底层浅色的圆圈    canvas.drawPath(cirPath, outerCirclePaint);}

分析:
这步没有什么难度,只是绘制一个圆圈

画一个深色的圆弧

这里写图片描述
相关代码片(不要复制,只是看的)

@Overrideprotected void onDraw(Canvas canvas) {    super.onDraw(canvas);    //坐标移动到中心    canvas.translate(mViewWidth / 2, mViewHeight / 2);    //音量大小    if (innerCirclePath == null)        innerCirclePath = new Path();    //绘制外层深色的音量弧形    drawVoicePath(innerCirclePath);    //画音量强度    canvas.drawPath(voicePath, voicePaint);}private void drawVoicePath(Path path) {    if (voiceRectf == null) {        //与底层圆圈保持一致        voiceRectf = new RectF(-r, -r, r, r);        voicePath = new Path();    }    voicePath.reset();    //道听途说使用359.9可以测量的更准    path.addArc(voiceRectf, -90, 359.9f);    //通过PathMeasure来绘制部分的圆圈,表现出来就是绘制了弧形    PathMeasure measure = new PathMeasure(path, false);    //获取圆的总长度    float length = measure.getLength();    //根据音量大小来绘制部分的弧形    measure.getSegment(0, voiceNumber * length, voicePath, true);}

分析
这段代码需要了解PathMeasure的用法点击这里了解。
圆弧从上往下绘制,所以:addArc(RectF oval, float startAngle, float sweepAngle)中的第二个参数startAngle为-90;
圆各部分的对应的Angle:
这里写图片描述
另外,通过可变参数voiceNumber来控制圆弧的大小,用于之后的修改。

触摸改变圆弧的大小

这里写图片描述

相关代码片(不要复制,只是看的)

@Overridepublic boolean onTouchEvent(MotionEvent event) {    switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            //记录起始位置的坐标和音量大小            startY = (int) event.getY();            oldVoiceNumber = voiceNumber;            break;        case MotionEvent.ACTION_MOVE:            //记录当前位置的坐标            moveY = (int) event.getY();            //与起始位置进行比较来确定音量的大小            changedVoiceNumber();            break;        case MotionEvent.ACTION_UP:            //手指离开屏幕,重置数据            resetData();            break;    }    //绘制图形    invalidate();    //这里需要改View处理事件,所以放回true;    return true;}

最后一步的代码比较多,这里没有贴完,详细代码在最后都会贴出来。
分析:
既然要通过滑动来改变深色圆弧的大小,那么可定是在onTouchEvent()中来进行相关的操作。
1. 通过滑动,图形改变,需要重新绘制,所以调用invalidate()
2. 滑动事件需要改View来处理,所以返回值为true
3. 核心部分是通过ACTION_DOWN,ACTION_MOVE,ACTION_UP的相关操作来实现的

核心部分的大致流程图是这样的,如果与后面的代码有出入,以代码为准:
这里写图片描述

完整代码

这里可以复制粘贴了

/** * Created by Kevin on 2016/8/31. */public class VoiceView extends View {    //控件的宽高    private int mViewWidth;    private int mViewHeight;    //小喇叭    private Bitmap voice;    //小喇叭的宽度    private int bWidth;    //小喇叭的高度    private int bHeight;    //表示音量大小    private float voiceNumber = 0.5f;    //调节之前音量的大小    private float oldVoiceNumber;    //开始时的坐标    private int startY;    //移动后的坐标    private int moveY;    //圆圈的半径    private int r;    //音量从0-->1所需要移动的距离    private int voiceChangedY;    //音量的画笔    private Paint voicePaint;    //顶层圆圈的画笔    private Paint outerCirclePaint;    //音量的Path    private Path voicePath;    //顶层圆圈的的Path    private Path cirPath;    //音量圆圈的Path    private Path innerCirclePath;    //顶层的RectF    private RectF rectF;    //音量的RectF    private RectF voiceRectf;    public VoiceView(Context context) {        super(context);        initBitmap();    }    public VoiceView(Context context, AttributeSet attrs) {        super(context, attrs);        initBitmap();    }    public VoiceView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initBitmap();    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        initPaint();        //坐标移动到中心        canvas.translate(mViewWidth / 2, mViewHeight / 2);        //外层圆圈        if (cirPath == null)            cirPath = new Path();        //音量大小        if (innerCirclePath == null)            innerCirclePath = new Path();        //绘制底层浅色圆圈        drawCirclePath(cirPath);        //绘制外层深色的音量弧形        drawVoicePath(innerCirclePath);        //移动坐标到中心位置        canvas.drawBitmap(voice, -bWidth / 2, -bHeight / 2, outerCirclePaint);        //画顶层浅色的圆圈        canvas.drawPath(cirPath, outerCirclePaint);        //画音量强度        canvas.drawPath(voicePath, voicePaint);    }    /**     * 通过触摸来改变音量的大小     *     * @param event     * @return     */    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                //记录起始位置的坐标和音量大小                startY = (int) event.getY();                oldVoiceNumber = voiceNumber;                break;            case MotionEvent.ACTION_MOVE:                //记录当前位置的坐标                moveY = (int) event.getY();                //与起始位置进行比较来确定音量的大小                changedVoiceNumber();                break;            case MotionEvent.ACTION_UP:                //手指离开屏幕,重置数据                resetData();                break;        }        //绘制图形        invalidate();        //这里需要改View处理事件,所以放回true;        return true;    }    /**     * 根据起始位置和当前位置来确定音量的大小     * <p>     * 一般情况下:音量大小的改变量 = (当前位置 - 起始位置) / 一个固定的长度     * 注释:这里选取的“一个固定的长度”为高度的一半     * <p>     * 极端情况:如果音量调节到1或者0,仍然以最开始的位置作为起始位置,感觉会很奇怪(可以将resetData()中的的代码屏蔽来感觉一下);     * 处理:当为0或者1时,重置数据     */    private void changedVoiceNumber() {        int changeY = moveY - startY;        float changedVoice = changeY / (voiceChangedY * 1.0f);        if (changedVoice > 0) {            //音量增加            if (voiceNumber >= 1) {                voiceNumber = 1;                resetData();                return;            } else {                float afterChange = oldVoiceNumber + changedVoice;                if (afterChange >= 1) {                    voiceNumber = 1;                    resetData();                } else {                    voiceNumber = afterChange;                }            }        } else if (changedVoice < 0) {            //音量减少            if (voiceNumber <= 0) {                voiceNumber = 0;                resetData();                return;            } else {                float afterChange = oldVoiceNumber + changedVoice;                if (afterChange <= 0) {                    voiceNumber = 0;                    resetData();                } else {                    voiceNumber = afterChange;                }            }        } else if (changedVoice == 0) {            //音量不变        }//        String print = String.format("startY-->%d,moveY-->%d,voiceNumber-->%f,changeVoice-->%f", startY, moveY, voiceNumber, changedVoice);//        Log.e("ddd", print);    }    /**     * 当音量达到0或者1时,重置数据     */    private void resetData() {        startY = moveY;        oldVoiceNumber = voiceNumber;    }    /**     * 绘制底层层圆圈     *     * @param path     */    private void drawCirclePath(Path path) {        if (rectF == null) {            //半径选取为图片宽度一半的1.3倍,可以调节            r = (int) (bWidth / 2 * 1.3f);            rectF = new RectF(-r, -r, r, r);        }        path.addArc(rectF, 0, 360);    }    /**     * 绘制音量     *     * @param path     */    private void drawVoicePath(Path path) {        if (voiceRectf == null) {            //与底层圆圈保持一致            voiceRectf = new RectF(-r, -r, r, r);            voicePath = new Path();        }        voicePath.reset();        //道听途说使用359.9可以测量的更准        path.addArc(voiceRectf, -90, 359.9f);        //通过PathMeasure来绘制部分的圆圈,表现出来就是绘制了弧形        PathMeasure measure = new PathMeasure(path, false);        //获取圆的总长度        float length = measure.getLength();        //根据音量大小来绘制部分的弧形        measure.getSegment(0, voiceNumber * length, voicePath, true);    }    /**     * 小喇叭     */    private void initBitmap() {        voice = BitmapFactory.decodeResource(getResources(), R.mipmap.voice);        bWidth = voice.getWidth();        bHeight = voice.getHeight();    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        mViewWidth = w;        mViewHeight = h;        //音量从0-->1所需要移动的距离        voiceChangedY = h / 2;    }    /**     * 初始化画笔     */    private void initPaint() {        int stroke = 20;        if (outerCirclePaint == null) {            outerCirclePaint = new Paint();            outerCirclePaint.setStrokeWidth(stroke);            outerCirclePaint.setStyle(Paint.Style.STROKE);            outerCirclePaint.setColor(0x8089cff0);            outerCirclePaint.setAntiAlias(true);        }        if (voicePaint == null) {            voicePaint = new Paint();            voicePaint.setStrokeWidth(stroke);            voicePaint.setStyle(Paint.Style.STROKE);            voicePaint.setColor(0xff1d8ffe);            voicePaint.setAntiAlias(true);        }    }}

结语

该View的绘制难度并不大,涉及了不少基础,当做练习是一个很好的素材。

转载请标明出处:http://blog.csdn.net/qq_26411333/article/details/52383186#t6

1 0