问题
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