android - 使用Android自定义 View 实时绘制绘图

标签 android

我想在我的应用程序中使用实时绘图制作简单的线图。我知道有很多不同的库,但它们太大或者没有合适的功能或许可证。

我的想法是制作自定义 View 并扩展 View 类。在这种情况下使用 OpenGL 就像用大炮射鸭子一样。我已经有了绘制静态数据的 View - 首先我将所有数据放入 Plot 对象的 float 数组中,然后使用循环在 onDraw 中绘制所有内容() PlotView 类的方法。

我还有一个主题可以为我的情节提供新数据。但是现在的问题是如何在添加新数据的同时绘制它。第一个想法是简单地添加新点并绘制。再添加一个。但我不确定在 100 点或 1000 点会发生什么。我正在添加新点,要求 View 使自身无效,但仍未绘制一些点。在这种情况下,即使使用一些队列也可能很困难,因为 onDraw() 将再次从头开始,因此队列元素的数量只会增加。

为了实现这个目标,您有什么建议?

最佳答案

这应该可以解决问题。

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Bundle;
import android.support.v4.view.ViewCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.AttributeSet;
import android.view.View;

import java.io.Serializable;


public class MainActivity
    extends AppCompatActivity
{
    private static final String STATE_PLOT = "statePlot";

    private MockDataGenerator mMockDataGenerator;
    private Plot mPlot;


    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        if(savedInstanceState == null){
            mPlot = new Plot(100, -1.5f, 1.5f);
        }else{
            mPlot = (Plot) savedInstanceState.getSerializable(STATE_PLOT);
        }

        PlotView plotView = new PlotView(this);
        plotView.setPlot(mPlot);
        setContentView(plotView);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState)
    {
        super.onSaveInstanceState(outState);
        outState.putSerializable(STATE_PLOT, mPlot);
    }

    @Override
    protected void onResume()
    {
        super.onResume();
        mMockDataGenerator = new MockDataGenerator(mPlot);
        mMockDataGenerator.start();
    }

    @Override
    protected void onPause()
    {
        super.onPause();
        mMockDataGenerator.quit();
    }


    public static class MockDataGenerator
        extends Thread
    {
        private final Plot mPlot;


        public MockDataGenerator(Plot plot)
        {
            super(MockDataGenerator.class.getSimpleName());
            mPlot = plot;
        }

        @Override
        public void run()
        {
            try{
                float val = 0;
                while(!isInterrupted()){
                    mPlot.add((float) Math.sin(val += 0.16f));
                    Thread.sleep(1000 / 30);
                }
            }
            catch(InterruptedException e){
                //
            }
        }

        public void quit()
        {
            try{
                interrupt();
                join();
            }
            catch(InterruptedException e){
                //
            }
        }
    }

    public static class PlotView extends View
        implements Plot.OnPlotDataChanged
    {
        private Paint mLinePaint;
        private Plot mPlot;


        public PlotView(Context context)
        {
            this(context, null);
        }

        public PlotView(Context context, AttributeSet attrs)
        {
            super(context, attrs);
            mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mLinePaint.setStyle(Paint.Style.STROKE);
            mLinePaint.setStrokeJoin(Paint.Join.ROUND);
            mLinePaint.setStrokeCap(Paint.Cap.ROUND);
            mLinePaint.setStrokeWidth(context.getResources()
                .getDisplayMetrics().density * 2.0f);
            mLinePaint.setColor(0xFF568607);
            setBackgroundColor(0xFF8DBF45);
        }

        public void setPlot(Plot plot)
        {
            if(mPlot != null){
                mPlot.setOnPlotDataChanged(null);
            }
            mPlot = plot;
            if(plot != null){
                plot.setOnPlotDataChanged(this);
            }
            onPlotDataChanged();
        }

        public Plot getPlot()
        {
            return mPlot;
        }

        public Paint getLinePaint()
        {
            return mLinePaint;
        }

        @Override
        public void onPlotDataChanged()
        {
            ViewCompat.postInvalidateOnAnimation(this);
        }

        @Override
        protected void onDraw(Canvas canvas)
        {
            super.onDraw(canvas);

            final Plot plot = mPlot;
            if(plot == null){
                return;
            }

            final int height = getHeight();
            final float[] data = plot.getData();
            final float unitHeight = height / plot.getRange();
            final float midHeight  = height / 2.0f;
            final float unitWidth  = (float) getWidth() / data.length;

            float lastX = -unitWidth, lastY = 0, currentX, currentY;
            for(int i = 0; i < data.length; i++){
                currentX = lastX + unitWidth;
                currentY = unitHeight * data[i] + midHeight;
                canvas.drawLine(lastX, lastY, currentX, currentY, mLinePaint);
                lastX = currentX;
                lastY = currentY;
            }
        }
    }


    public static class Plot
        implements Serializable
    {
        private final float[] mData;
        private final float   mMin;
        private final float   mMax;

        private transient OnPlotDataChanged mOnPlotDataChanged;


        public Plot(int size, float min, float max)
        {
            mData = new float[size];
            mMin  = min;
            mMax  = max;
        }

        public void setOnPlotDataChanged(OnPlotDataChanged onPlotDataChanged)
        {
            mOnPlotDataChanged = onPlotDataChanged;
        }

        public void add(float value)
        {
            System.arraycopy(mData, 1, mData, 0, mData.length - 1);
            mData[mData.length - 1] = value;
            if(mOnPlotDataChanged != null){
                mOnPlotDataChanged.onPlotDataChanged();
            }
        }

        public float[] getData()
        {
            return mData;
        }

        public float getMin()
        {
            return mMin;
        }

        public float getMax()
        {
            return mMax;
        }

        public float getRange()
        {
            return (mMax - mMin);
        }

        public interface OnPlotDataChanged
        {
            void onPlotDataChanged();
        }
    }
}

关于android - 使用Android自定义 View 实时绘制绘图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30244436/

相关文章:

android - 如何从纬度和经度获取当前位置的天气?

具有相同功能的多个项目的 Android 模块结构

android - 可以删除 recyclerview 中以前的项目吗?

c# - Xamarin Forms Android DatePicker - 只希望显示微调器,但日历也是如此

android - 由于无效原因,我的应用被 Instagram Basic Display API 审核拒绝

java - 如何在android上处理加速度计强制关闭

java - 使用 Joda-Time 获取日期的分钟数

java - Android ICS MediaPlayer 仅在用于同时播放的变量上崩溃

Android TextView设置跑马灯,如何去掉TextView左右两侧的阴影

android - Instabug Proguard 问题