android 自定义动态加载数据的折线图及相关问题解析

最近写了一个关于音频分析的app,实时的音频频率信息获取到了,怎么在一个view中显示折线图,按照时间去添加频率数据,当数组中个数大于屏幕宽度的时候,左边消失一个点,右边添加一个点,像流水线一样将数据显示出来。没有插件可以使用,只好自己写一个自定义的view去显示数据。通过定时刷新的方式去显示。
最后做出来的样式
声音的频率会实时获取到,这给了数据的来源,下面具体怎么实现这个折线图。关于声音频率获取这儿就不具体说了。
首先,你得画出这个自定义的view,并在数据传入的时候对界面重绘。

/**
 * 自定义的画折线图的view
 * @author wangjian
 *
 */
public class MyChartView  extends View{
    private AppContext mAppContext;//主要用到里面的dptopx()方法,将dp转为px值
    private int chartH;//组件存放位置高度
    private int chartW;//宽度
    private int left;//边框离左边的宽度
    private int top;//边框离上边的宽度
    private double scale;//中线代表的频率大小与中线到底线长度的比例
    private int maxDataSize;//x轴最大存放数据个数
    private int xWidth = 2;//x轴每个数据存放的宽度
    private List<Point> plist = new ArrayList<Point>();//点集
    private VoiceUtil mVoiceUtil;//声音实体类,有主音,唱音,频率等属性
    private int frequency;//存放声音频率

    //构造函数需要传过来空间的宽高
    public MyChartView(Context context,int chartH,int chartW,VoiceUtil mVoiceUtil) {
        super(context);
        setWillNotDraw(false);//保证重绘不会被制止
        mAppContext = AppContext.getInstance();
        this.chartH = chartH;
        this.chartW = chartW;
        this.mVoiceUtil = mVoiceUtil;
        this.frequency = (int) mVoiceUtil.getFrequency();
        left = mAppContext.dp2px(5);
        top = mAppContext.dp2px(5);
        scale = mVoiceUtil.getFrequency()/(chartH/2-top);//用来根据频率获取点的位置
        maxDataSize = (chartW-2*left)/xWidth-1;//剪掉边框的宽度
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //画边框
        drawBorder(canvas);
        //准备画折线的数据画折线
        preparePoint(canvas);
    }
    //画边框
    private void drawBorder(Canvas canvas) {
        //画外框
        Paint paint = new Paint();
        paint.setColor(Color.BLACK);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(mAppContext.dp2px(2));
        Rect chartRec = new Rect(left, top,chartW-left, chartH-top);
        canvas.drawRect(chartRec, paint);
        //画中心线
        Paint midpaint = new Paint();
        midpaint.setColor(Color.BLACK);
        midpaint.setStyle(Paint.Style.STROKE);
        midpaint.setStrokeWidth(mAppContext.dp2px(1));
        canvas.drawLine(left, chartH/2, chartW-left, chartH/2, midpaint);
        //画声音提示信息
        Path textPath = new Path();
        textPath.moveTo(left+mAppContext.dp2px(10), mAppContext.dp2px(20));
        textPath.lineTo(chartW-left, mAppContext.dp2px(20));
        Paint textpaint = new Paint();
        textpaint.setStyle(Paint.Style.FILL);
        textpaint.setColor(Color.WHITE);
        textpaint.setTextSize(32);
        textpaint.setAntiAlias(true);
        String text = "主音:"+mVoiceUtil.getKeynote()+",唱音:"+mVoiceUtil.getSingnote()+",八度:"+mVoiceUtil.getOctave();
        canvas.drawTextOnPath(text, textPath, 0, 0, textpaint);
        //画频率值
        Path frePath = new Path();
        frePath.moveTo(left+mAppContext.dp2px(10), chartH/2-mAppContext.dp2px(5));
        frePath.lineTo(chartW-left, chartH/2-mAppContext.dp2px(5));
        String fretext = "频率:"+(int)mVoiceUtil.getFrequency();
        canvas.drawTextOnPath(fretext, frePath, 0, 0, textpaint);
    }
    //准备画折线的数据并画折线
    private void preparePoint(Canvas canvas) {
        //准备数据
        //判断纵坐标是不是超出界限,让频率的点处在边框内
        int pointY = 0;
        if(frequency/scale>=(chartH-top)){
            pointY = top;
        }else if(frequency/scale<=top){
            pointY = chartH-top;
        }else{
            pointY = chartH-top-(int) (frequency/scale);
        }
        Point p = new Point(chartW-left , pointY );
        //判断点集有没有超出界限
        if(plist.size()>(maxDataSize+1)){
            plist.remove(0);
            //循环处理点
            for(int i=0;i<maxDataSize;i++){
                //将点的横坐标向左平移一格
                plist.get(i).x -= xWidth;
            }
            plist.add(p);//将新的点添加进去
        }else{
            for(int i = 0; i<plist.size()-1; i++){
                plist.get(i).x -= xWidth;
            }
            plist.add(p);
        }

        //准备画笔
        Paint paint = new Paint();
        paint.setColor(Color.WHITE);
        paint.setStrokeWidth(3);
        paint.setAntiAlias(true);
        //画折线图
        if(plist.size() >= 2){
            for(int i = 0; i<plist.size()-1; i++){
                canvas.drawLine(plist.get(i).x, plist.get(i).y, plist.get(i+1).x, plist.get(i+1).y, paint);
            }
        }
    }
    //更新界面显示
    public void updateLine(VoiceUtil voiceUtil,int frequencys){
        frequency = frequencys;
        mVoiceUtil = voiceUtil;
//      invalidate();
    }
}

在主界面中使用Handler和Runnable的方式去定时将数据传给view并去重绘

mHandler = new Handler();
mHandler.post(new TimerProcess());
private class TimerProcess implements Runnable {
        public void run() {
            if(started){//当开始录音
                if(isDrawNew){//是否改变了主音等一些参数
                    drawNewChaertView();//销毁并重绘
                    isDrawNew = false;
                }else{
                    mVoiceUtil = mDbmanager.findFrequencyByKeynoteAndSingnote(keynote, singnote, octave);
                    //更新数据
                    myChartView.updateLine(mVoiceUtil, frequencys);
                    //重绘
                    myChartView.invalidate();
                }
            }
            mHandler.postDelayed(this, 1);
        }
    }
    //重绘chartview
    private void drawNewChaertView() {
        ll_mylinechart.removeAllViews();
        mVoiceUtil = mDbmanager.findFrequencyByKeynoteAndSingnote(keynote, singnote, octave);
        myChartView = new MyChartView(this, chartH, chartW,mVoiceUtil);
        myChartView.invalidate();
        ll_mylinechart.addView(myChartView);
    }

相关bug
在调用myChartView.invalidate();去更新数据的时候却发现了一个bug,在程序切换到主界面再进去时,虽然一直在定时执行,但不会调用myChartView中的ondraw()方法。佷蛋疼。。就剩最后一步。。查找资料,也找不到为什么。后来有一位网友说需要将添加view的外面的linearlayout换了,我将linearlayout换成RelativeLayout结果好了。可以断点续传了。。。

<RelativeLayout 
    android:id="@+id/ll_mylinechart"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="4"
    android:background="#03a9f4">
</RelativeLayout>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值