Android/Java creating a helper class to create graphs

一世执手 提交于 2019-12-10 19:32:08

问题


Goal:

To create a helper class for graph generation

Background:

I have 3 fragments that each collect some sensor data (accelerometer, gyroscope, rotation) and plots a graph using GraphView. Here is what the code looks like for one of those fragments (this code currently works correctly):

public class GyroscopeFragment extends Fragment implements SensorEventListener {

    private final short TYPE_GYROSCOPE = Sensor.TYPE_GYROSCOPE;
    private final short POLL_FREQUENCY = 100; //in milliseconds
    private final short MAX_POINTS_DISPLAYED = 50;
    private SensorManager sensorManager;
    private Sensor sensor;
    private Sensor gyroscope;
    private long lastUpdate = -1;

    float curGyroX;
    float curGyroY;
    float curGyroZ;

    private LineGraphSeries<DataPoint> gyroXSeries;
    private LineGraphSeries<DataPoint> gyroYSeries;
    private LineGraphSeries<DataPoint> gyroZSeries;
    private double graphXTime = 1d;

    public GyroscopeFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_gyroscope, container, false);

        //Set the nav drawer item highlight
        MainActivity mainActivity = (MainActivity) getActivity();
        mainActivity.navigationView.setCheckedItem(R.id.nav_gyroscope);

        //Set actionbar title
        mainActivity.setTitle("Gyroscope");

        //Sensor manager
        sensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
        gyroscope = (Sensor) sensorManager.getDefaultSensor(TYPE_GYROSCOPE);

        return view;
    }


    @Override
    public void onResume() {
        super.onResume();
        sensorManager.registerListener(this, gyroscope, SensorManager.SENSOR_DELAY_FASTEST);

        //Create graph
        GraphView gyroGraph = (GraphView) getView().findViewById(R.id.gyroGraph);

        gyroGraph.getLegendRenderer().setVisible(true);
        gyroGraph.getLegendRenderer().setFixedPosition(0,0);

        gyroGraph.getGridLabelRenderer().setHighlightZeroLines(false);

        gyroXSeries = new LineGraphSeries<DataPoint>();
        gyroYSeries = new LineGraphSeries<DataPoint>();
        gyroZSeries = new LineGraphSeries<DataPoint>();

        gyroXSeries.setTitle("X");
        gyroYSeries.setTitle("Y");
        gyroZSeries.setTitle("Z");

        gyroXSeries.setColor(Color.RED);
        gyroYSeries.setColor(Color.GREEN);
        gyroZSeries.setColor(Color.BLUE);

        gyroGraph.addSeries(gyroXSeries);
        gyroGraph.addSeries(gyroYSeries);
        gyroGraph.addSeries(gyroZSeries);

        gyroGraph.getViewport().setYAxisBoundsManual(true);
        gyroGraph.getViewport().setMinY(-10);
        gyroGraph.getViewport().setMaxY(10);

        gyroGraph.getViewport().setXAxisBoundsManual(true);
        gyroGraph.getViewport().setMinX(0);
        gyroGraph.getViewport().setMaxX(MAX_POINTS_DISPLAYED);
    }

    @Override
    public void onPause() {
        super.onPause();
        sensorManager.unregisterListener(this);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        sensorManager.unregisterListener(this);
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        sensorManager.unregisterListener(this);
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        sensor = event.sensor;

        curGyroX = event.values[0];
        curGyroY = event.values[1];
        curGyroZ = event.values[2];

        long curTime = System.currentTimeMillis();
        long diffTime = (curTime - lastUpdate);

        // only allow one update every POLL_FREQUENCY.
        if (diffTime > POLL_FREQUENCY) {
            lastUpdate = curTime;

            graphXTime += 1d;
            gyroXSeries.appendData(new DataPoint(graphXTime, curGyroX), true, MAX_POINTS_DISPLAYED);
            gyroYSeries.appendData(new DataPoint(graphXTime, curGyroY), true, MAX_POINTS_DISPLAYED);
            gyroZSeries.appendData(new DataPoint(graphXTime, curGyroZ), true, MAX_POINTS_DISPLAYED);
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        //Safe not to implement
    }
}

I find that across the 3 fragments I am repeating a lot of the graph generation code in onResume so I wanted to create a GraphHelper class that I can instantiate to do the graph creation, with the benefit of only having to change code in one place if I wanted to alter the look of a graph. However, I am new to Java so having trouble wrapping my head around creating this new class.

Attempted solution:

I have managed to come up with the following class in an attempt to solve the problem:

public class GraphHelper {

    private final GraphView graph;
    private final LineGraphSeries<DataPoint> xSeries;
    private final LineGraphSeries<DataPoint> ySeries;
    private final LineGraphSeries<DataPoint> zSeries;

    public GraphHelper(GraphView graph, LineGraphSeries<DataPoint> xSeries,
                            LineGraphSeries<DataPoint> ySeries, LineGraphSeries<DataPoint> zSeries, short maxDisplayed){
        this.graph = graph;
        this.xSeries = xSeries;
        this.ySeries = ySeries;
        this.zSeries = zSeries;

        graph.getLegendRenderer().setVisible(true);
        graph.getLegendRenderer().setFixedPosition(0, 0);

        graph.getGridLabelRenderer().setHighlightZeroLines(false);

        xSeries = new LineGraphSeries<DataPoint>();
        ySeries = new LineGraphSeries<DataPoint>();
        zSeries = new LineGraphSeries<DataPoint>();

        xSeries.setTitle("X");
        ySeries.setTitle("Y");
        zSeries.setTitle("Z");

        xSeries.setColor(Color.RED);
        ySeries.setColor(Color.GREEN);
        zSeries.setColor(Color.BLUE);

        graph.addSeries(xSeries);
        graph.addSeries(ySeries);
        graph.addSeries(zSeries);

        graph.getViewport().setYAxisBoundsManual(true);
        graph.getViewport().setMinY(-10);
        graph.getViewport().setMaxY(10);

        graph.getViewport().setXAxisBoundsManual(true);
        graph.getViewport().setMinX(0);
        graph.getViewport().setMaxX(maxDisplayed);
    }
}

And have changed my fragment onResume to look like this (everything else is the same):

@Override
public void onResume() {
    super.onResume();
    sensorManager.registerListener(this, gyroscope, SensorManager.SENSOR_DELAY_FASTEST);

    //Create graph
    GraphView gyroGraph = (GraphView) getView().findViewById(R.id.gyroGraph);

    new GraphHelper(gyroGraph, gyroXSeries, gyroYSeries, gyroZSeries, MAX_POINTS_DISPLAYED);
}

Problem #1:

I am getting the following error whenever I try to view the graph fragment:

java.lang.NullPointerException: Attempt to invoke virtual method 'void com.jjoe64.graphview.series.LineGraphSeries.appendData(com.jjoe64.graphview.series.DataPointInterface, boolean, int)' on a null object reference

This is referring to the onSensorChanged method where I'm trying to append data to the series. It doesn't seem to know what gyroXSeries is because I'm adding it to the graph within the helper.

Problem #2:

Currently I'm declaring gyroXSeries in the main fragment class, as well as the top of the helper class, and also creating a new LineGraphSeries object in the helpers constructor. Is there a way to reduce the repetition of this code?

EDIT

In response to the comments below, I made the following changes and the new GraphHelper class looks like this:

public class GraphHelper {

    private final GraphView graph;
    private final LineGraphSeries<DataPoint> xSeries;
    private final LineGraphSeries<DataPoint> ySeries;
    private final LineGraphSeries<DataPoint> zSeries;


    public GraphHelper(GraphView graph, LineGraphSeries<DataPoint> xSeries,
                            LineGraphSeries<DataPoint> ySeries, LineGraphSeries<DataPoint> zSeries, short maxDisplayed){
        this.graph = graph;
        this.xSeries = xSeries;
        this.ySeries = ySeries;
        this.zSeries = zSeries;

        graph.getLegendRenderer().setVisible(true);
        graph.getLegendRenderer().setFixedPosition(0, 0);

        graph.getGridLabelRenderer().setHighlightZeroLines(false);

        xSeries.setTitle("X");
        ySeries.setTitle("Y");
        zSeries.setTitle("Z");

        xSeries.setColor(Color.RED);
        ySeries.setColor(Color.GREEN);
        zSeries.setColor(Color.BLUE);

        graph.addSeries(xSeries);
        graph.addSeries(ySeries);
        graph.addSeries(zSeries);

        graph.getViewport().setYAxisBoundsManual(true);
        graph.getViewport().setMinY(-10);
        graph.getViewport().setMaxY(10);

        graph.getViewport().setXAxisBoundsManual(true);
        graph.getViewport().setMinX(0);
        graph.getViewport().setMaxX(maxDisplayed);
    }
}

The gyroscope fragment is the same as the original, with the change to onResume that I made above

Which results in the following error:

FATAL EXCEPTION: main Process: net.binarysea.sensorload, PID: 25997 java.lang.NullPointerException: Attempt to invoke virtual method 'void com.jjoe64.graphview.series.LineGraphSeries.setTitle(java.lang.String)' on a null object reference at net.binarysea.sensorload.GraphHelper.(GraphHelper.java:30) at net.binarysea.sensorload.GyroscopeFragment.onResume(GyroscopeFragment.java:73) at android.support.v4.app.Fragment.performResume(Fragment.java:2005) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1108) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1248) at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:738) at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1613) at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:517) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5417) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)


回答1:


Change your graph helper class to this.

public class GraphHelper {
    public GraphHelper(GraphView graph, LineGraphSeries<DataPoint> xSeries,
                   LineGraphSeries<DataPoint> ySeries, LineGraphSeries<DataPoint> zSeries, short maxDisplayed){

    graph.getLegendRenderer().setVisible(true);
    graph.getLegendRenderer().setFixedPosition(0, 0);

    graph.getGridLabelRenderer().setHighlightZeroLines(false);

    xSeries.setTitle("X");
    ySeries.setTitle("Y");
    zSeries.setTitle("Z");

    xSeries.setColor(Color.RED);
    ySeries.setColor(Color.GREEN);
    zSeries.setColor(Color.BLUE);

    graph.addSeries(xSeries);
    graph.addSeries(ySeries);
    graph.addSeries(zSeries);

    graph.getViewport().setYAxisBoundsManual(true);
    graph.getViewport().setMinY(-10);
    graph.getViewport().setMaxY(10);

    graph.getViewport().setXAxisBoundsManual(true);
    graph.getViewport().setMinX(0);
    graph.getViewport().setMaxX(maxDisplayed);
    }
}

Call it like this:

@Override
public void onResume() {
    super.onResume();
    sensorManager.registerListener(this, gyroscope, SensorManager.SENSOR_DELAY_FASTEST);

    //Create graph
    GraphView gyroGraph = (GraphView) getView().findViewById(R.id.gyroGraph);

    gyroXSeries = new LineGraphSeries<DataPoint>();
    gyroYSeries = new LineGraphSeries<DataPoint>();
    gyroZSeries = new LineGraphSeries<DataPoint>();

    new GraphHelper(gyroGraph, gyroXSeries, gyroYSeries, gyroZSeries, MAX_POINTS_DISPLAYED);
}

Also this is a more proper way to do a helper in java:

public class GraphHelper {

public GraphHelper() { }

public static void initializeGraph(GraphView graph, LineGraphSeries<DataPoint> xSeries,
                                   LineGraphSeries<DataPoint> ySeries, LineGraphSeries<DataPoint> zSeries, short maxDisplayed){
    graph.getLegendRenderer().setVisible(true);
    graph.getLegendRenderer().setFixedPosition(0, 0);

    graph.getGridLabelRenderer().setHighlightZeroLines(false);

    xSeries.setTitle("X");
    ySeries.setTitle("Y");
    zSeries.setTitle("Z");

    xSeries.setColor(Color.RED);
    ySeries.setColor(Color.GREEN);
    zSeries.setColor(Color.BLUE);

    graph.addSeries(xSeries);
    graph.addSeries(ySeries);
    graph.addSeries(zSeries);

    graph.getViewport().setYAxisBoundsManual(true);
    graph.getViewport().setMinY(-10);
    graph.getViewport().setMaxY(10);

    graph.getViewport().setXAxisBoundsManual(true);
    graph.getViewport().setMinX(0);
    graph.getViewport().setMaxX(maxDisplayed);
}
}

This way you don't have a random object that's waiting to be garbage collected. To call it use:

GraphHelper.initializeGraph(...)


来源:https://stackoverflow.com/questions/35706153/android-java-creating-a-helper-class-to-create-graphs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!