一个简单的Android自定义View - 音波波形图

       最近有个项目中需要录音,参考了一个APP,录音实时用声波图显示声音大小,并且录音结束后可通过拖拽定位修改其中某一段时间的声音。这两天比较闲了,写了个没任何技术含量的波形图自定义view(就是在画线),欢迎指教。

因为音频录制的方式很多,所以这个view只负责接收表示音量大小的值,具体传过来的值多大多小不限制,取所有的值中最大值按比例缩放。




代码如下:

public class WaveView extends View {
    public WaveView(Context context) {
        super(context);
    }

    public WaveView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public WaveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    private Integer waveType;//波形展示类型
    private int centerLineColor = Color.BLACK;
    private int centerLineWidth = 1;
    private int lineColor = Color.GREEN;
    private int lineWidth = 10;//竖线的宽度
    private int lineSpace = 30;//竖线之间的间隔宽度

    public static final int WVTYPE_CENTER_LINE = 0;//竖线从中间开始 向上向下长度相同
    public static final int WVTYPE_SINGLE = 1;//竖线从底部开始向上计算


    private List<Integer> values;//存放数值
    private int fullValue = 100;//相对最大值
    private float mScale = 0;//传入值转换为有效数值需要使用的比例
    private int maxValue = 1;//当前数组中的最大值 该值乘以scale应等于fullValue
    private int maxLineCount ;
    private boolean hasOver;//值记录是否已完毕

    Paint paintCenterLine,paintLine;



    private void init(AttributeSet attrs){
        final TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.WaveView);
        waveType = typedArray.getInt(R.styleable.WaveView_wvType, 0);
        centerLineColor = typedArray.getColor(R.styleable.WaveView_wvCenterLineColor,Color.BLACK);
        centerLineWidth = typedArray.getDimensionPixelSize(R.styleable.WaveView_wvCenterLineWidth,1);
        lineColor = typedArray.getColor(R.styleable.WaveView_wvLineColor,Color.GREEN);
        lineWidth = typedArray.getDimensionPixelSize(R.styleable.WaveView_wvLineWidth,10);
        lineSpace = typedArray.getDimensionPixelSize(R.styleable.WaveView_wvLineSpace,30);

        paintCenterLine = new Paint();
        paintCenterLine.setStrokeWidth(centerLineWidth);
        paintCenterLine.setColor(centerLineColor);

        paintLine = new Paint();
        paintLine.setStrokeWidth(lineWidth);
        paintLine.setAntiAlias(true);
        paintLine.setColor(lineColor);
    }



    public void putValue(int value){
        if (value>maxValue){
            maxValue = value;
            mScale = (float) fullValue/maxValue;
        }
        if (values==null){
            values = new ArrayList<>();
        }else {
//            values.add(value);
//            invalidate();
        }
        values.add(value);
        invalidate();
    }

    public void setHasOver(boolean over){
        hasOver = over;
    }

    public boolean hasOver(){
        return hasOver;
    }


    private int lastX,moveX;
    private boolean hasBeenEnd=false;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastX = (int) (event.getRawX());
                break;
            case MotionEvent.ACTION_MOVE:
                int x = (int) event.getRawX();
                //到达边缘时不能向该方向继续移动
                if (!hasBeenEnd || (moveX>0&&(lastX-x)<0 ||(moveX<0&&(lastX-x)>0)))  {
                    moveX += (lastX-x)*0.7;
                    lastX = x;
                    invalidate();
                }
                break;
        }
        return true;
    }



    @Override
    protected void onDraw(Canvas canvas) {
        int yCenter = getHeight() / 2;
        if (maxLineCount==0){
            maxLineCount = getWidth()/(lineSpace+lineWidth);
        }
        if (waveType==WVTYPE_CENTER_LINE){
            /***************画中线*****************/
            canvas.drawLine(0,yCenter,getWidth(),yCenter,paintCenterLine);
        }
        /***************画竖线*****************/
        //判断当前数组中的数据是否超出了可画竖线最大条数
        if(values!=null){
            /**找出当前第一条竖线以及偏移量*/
            int startIndex = 0;//第一条线
            int startOffset = 0;//第一条线的偏移
            if (!hasOver || moveX==0){//仍在记录中或未手动滑动过
                //线条数量超出最大数 只画后面的线
                if(values.size()>maxLineCount){
                    startIndex = values.size()-maxLineCount;
                }
            }else {//已结束录值 且x轴有过移动
                //先得到第一条线原本应该的位置
                if(values.size()>maxLineCount){
                    startIndex = values.size()-maxLineCount;
                }
                //计算移动线条数
                int moveLineSize = moveX/(lineWidth+lineSpace);
                startOffset = moveX%(lineWidth+lineSpace);
                int currentIndex = startIndex+moveLineSize;
                if (currentIndex<0){//到达最左边
                    startIndex = 0;
                    startOffset = 0;
                    hasBeenEnd = true;
                }else if (currentIndex>=values.size()){
                    startIndex = values.size()-1;
                    startOffset=0;
                    hasBeenEnd = true;
                }else {
                    startIndex = currentIndex;
                    hasBeenEnd = false;
                }
                Log.d("XXXXXXX","move-x:"+moveX+"   moveLineSize:"+moveLineSize
                        +"   startIndex:"+startIndex+"  startOffset:"+startOffset);
            }
            //画竖线
            for (int i=startIndex;i<values.size();i++){
                int startX =0;
                int endX =0;
                int startY =0;
                int endY =0;
                int lineHeight = (int) ((((float)values.get(i)*mScale)/fullValue)*getHeight());
                switch (waveType){
                    case WVTYPE_CENTER_LINE:
                        startX = (i-startIndex)*(lineSpace+lineWidth)+lineWidth/2 - startOffset ;
                        endX =  startX;
                        startY = (getHeight()-lineHeight)/2;
                        endY = (getHeight()-lineHeight)/2+lineHeight;
                        break;
                    case WVTYPE_SINGLE:
                        startX = (i-startIndex)*(lineSpace+lineWidth)+lineWidth/2 - startOffset ;
                        endX =  startX;
                        startY = getHeight()-lineHeight;
                        endY = getHeight();
                        break;
                }
                canvas.drawLine(startX,startY,endX,endY,paintLine);
               // Paint pNum = new Paint();
               // pNum.setColor(Color.RED);
               // canvas.drawText(""+i,startX,yCenter,pNum); 画出竖线index便于测试
            }
        }
    }
}
样式相关:

<resources>
    <declare-styleable name="WaveView">
        <attr name="wvType" format="enum">
            <enum name="centerLine" value="0"/>
            <enum name="single" value="1"/>
        </attr>
        <attr name="wvCenterLineColor" format="color" />
        <attr name="wvCenterLineWidth" format="dimension" />
        <attr name="wvLineColor" format="color" />
        <attr name="wvLineWidth" format="dimension" />
        <attr name="wvLineSpace" format="dimension" />
    </declare-styleable>
</resources>

源码下载:github源码

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Android 中的自定义 View 是开发中非常重要的一部分,可以帮助开发者创建出各种各样的交互效果和用户界面。本文将介绍如何使用 Android Studio 实现一个简单自定义 View。 1. 创建一个新项目并新建一个自定义 View 类 在 Android Studio 中创建一个新的项目,并在其中创建一个新的类,命名为 CustomView。CustomView 继承自 View,因此需要在该类中添加构造函数和必要的方法。 ```java public class CustomView extends View { public CustomView(Context context) { super(context); } public CustomView(Context context, AttributeSet attrs) { super(context, attrs); } public CustomView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 绘制代码 } } ``` 2. 在 CustomView 中添加绘制代码 在 CustomView 的 onDraw 方法中添加绘制代码,绘制一个简单的圆形,在这里我们使用 Canvas 和 Paint 类来绘制图形。 ```java @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Paint.Style.FILL); canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, paint); } ``` 3. 在布局文件中添加 CustomView 在布局文件中添加 CustomView,设置宽度和高度为 200dp,并设置背景色为白色。 ```xml <com.example.customview.CustomView android:layout_width="200dp" android:layout_height="200dp" android:background="@android:color/white" /> ``` 4. 运行程序 完成以上步骤后,即可运行程序,在屏幕上看到一个红色的圆形。 完整代码如下: ```java public class CustomView extends View { public CustomView(Context context) { super(context); } public CustomView(Context context, AttributeSet attrs) { super(context, attrs); } public CustomView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Paint.Style.FILL); canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, paint); } } ``` ```xml <com.example.customview.CustomView android:layout_width="200dp" android:layout_height="200dp" android:background="@android:color/white" /> ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值