1

Hey guys it is my first time using MPandroidChart and I am currently experimenting with it to fully understand how to use it and its capabilities. Presently, I am trying to update my line graph using the data inputted by the user (through fragments). I've seen a few similar questions on stack overflow, however none of them have seemed to work for me.

Any help would be much appreciated. Thanks in advance.

Note: There is some repeated code, since I was experimenting and trying to figure out different approaches in search of a solution.

MainFragment that gets user input

public class Fragment1 extends Fragment {

EditText Input;
Button EnterVal;
public float userInput;

//Fragment1Listener activityCommander;

public interface Fragment1Listener{
    void sendInput(float value);
}

/*@Override
public void onAttach(Context context) {
    super.onAttach(context);
    try{
        activityCommander = (Fragment1Listener) getTargetFragment();
    }catch (ClassCastException e){
        throw new ClassCastException(context.toString() + "Must Implement FragmentListener");
    }
}*/

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment1, container, false);

    //@SuppressWarnings("all")

    Input = view.findViewById(R.id.Input);
    EnterVal = view.findViewById(R.id.button);


    EnterVal.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            String val = Input.getText().toString();

            if(val != null && val.length() > 0) {
                userInput = Float.valueOf(val);
                Input.setText("");
                /*if(activityCommander != null) {
                    activityCommander.sendInput(userInput);
                }*/
                Fragment1Listener listener = getListener();
                if(listener != null){
                    listener.sendInput(userInput);
                    Log.d("User Val", "Listener was successfull!");
                }
            }
        }
    });

    return view;
}

@Nullable
private Fragment1Listener getListener(){
    Fragment1Listener activityCommander;
    try{
        Fragment onInputSelected_Frag = getTargetFragment();
        if(onInputSelected_Frag != null){
            activityCommander = (Fragment1Listener) onInputSelected_Frag;
        }
        else{
            Activity onInputSelected_Act = getActivity();
            activityCommander = (Fragment1Listener) onInputSelected_Act;
        }
        return activityCommander;
    }catch(ClassCastException e){
        Log.e("Fragment Listener", "getListener: ClassCastException + " + e.getMessage());
    }
    return null;
}
}

MPandroidchart Fragment (line graph)

public class Fragment2 extends Fragment implements Fragment1.Fragment1Listener{

private LineChart lineChart;
//public Fragment1 fragment1;

private ArrayList<ILineDataSet> DataSets = new ArrayList<>();
private LineDataSet lineDataSet5;
private ArrayList<Entry> userData = new ArrayList<>();
private LineData data = new LineData(DataSets);

@Override
public void sendInput(float value) {

    data = lineChart.getData();
    ILineDataSet xSet = data.getDataSetByIndex(0);

    data.addEntry(new Entry(xSet.getEntryCount(), value),0);

    //lineDataSet5.notifyDataSetChanged();
    //lineDataSets.add(lineDataSet5);
    data.notifyDataChanged();
    lineChart.notifyDataSetChanged();
    lineChart.invalidate();
    lineChart.moveViewToX(data.getEntryCount());
}

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment2, container, false);

    lineChart = view.findViewById(R.id.LineChart);

    //Enable Touch Gestures
    lineChart.setTouchEnabled(true);

    //We want also enable scaling and dragging
    lineChart.setDragEnabled(true);
    lineChart.setScaleEnabled(true);

    //Enable pinch zoom to avoid scaling x and y axis separately
    lineChart.setPinchZoom(true);

    //Double tap zoom to chart
    lineChart.setDoubleTapToZoomEnabled(true);

    //Alternate Background Color
    lineChart.setBackgroundColor(Color.parseColor("#2b2b2b"));

    ArrayList<String> xAXES = new ArrayList<>();
    ArrayList<Entry> yAXESsin = new ArrayList<>();
    ArrayList<Entry> yAXEScos = new ArrayList<>();
    ArrayList<Entry> yAXEStan = new ArrayList<>();
    ArrayList<Entry> random = new ArrayList<>();

    double x = 0;
    int numDataPoints = 1000;
    for(int i =0; i<numDataPoints; i++){
        float sinFunction = Float.parseFloat(String.valueOf(Math.sin(x)));
        float cosFunction = Float.parseFloat(String.valueOf(Math.cos(x)));
        float tanFunction = Float.parseFloat(String.valueOf(Math.tan(x)));
        x += 0.1;
        yAXESsin.add(new Entry(i, sinFunction));
        yAXEScos.add(new Entry(i, cosFunction));
        yAXEStan.add(new Entry(i, tanFunction));
        xAXES.add(i, String.valueOf(x));

        final int randomVal = getRandomNumber(-10, 10);
        float randomData = (float) randomVal;
        random.add(new Entry(i, randomData));
    }

    String[] xaxes = new String[xAXES.size()];
    for(int i =0; i<xAXES.size(); i++){
        xaxes[i] = xAXES.get(i).toString();
    }

    //Add user Input
    //@SuppressWarnings("all")
    //userInput = fragment1.getUserInput();
    //userData.add(new Entry(userData.size()+1, userInput));

    ArrayList<ILineDataSet> lineDataSets = new ArrayList<>();
    ArrayList<ILineDataSet> dataSets = new ArrayList<>();

    //Data Set 1

    LineDataSet lineDataSet1 = new LineDataSet(yAXEScos, "Cos");
    lineDataSet1.setAxisDependency(YAxis.AxisDependency.LEFT);
    lineDataSet1.setDrawCircles(false);
    lineDataSet1.setColor(Color.parseColor("#B71C1C"));
    lineDataSet1.setLineWidth(3f);
    lineDataSet1.setHighlightEnabled(true);
    lineDataSet1.setDrawHighlightIndicators(true);
    lineDataSet1.setHighLightColor(Color.parseColor("#FF4081"));
    lineDataSet1.setValueTextSize(10f);
    lineDataSet1.setValueTextColor(Color.WHITE);
    lineDataSet1.setFillAlpha(110);

    //Data Set 2

    LineDataSet lineDataSet2 = new LineDataSet(yAXESsin, "Sin");
    lineDataSet2.setAxisDependency(YAxis.AxisDependency.LEFT);
    lineDataSet2.setDrawCircles(false);
    lineDataSet2.setColor(Color.parseColor("#33bbff"));
    lineDataSet2.setLineWidth(3f);
    lineDataSet2.setHighlightEnabled(true);
    lineDataSet2.setDrawHighlightIndicators(true);
    lineDataSet2.setHighLightColor(Color.parseColor("#FFD600"));
    lineDataSet2.setValueTextSize(10f);
    lineDataSet2.setValueTextColor(Color.WHITE);
    lineDataSet2.setFillAlpha(110);

    //Data Set 3

    LineDataSet lineDataSet3 = new LineDataSet(yAXEStan, "Tan");
    lineDataSet3.setAxisDependency(YAxis.AxisDependency.LEFT);
    lineDataSet3.setDrawCircles(false);
    lineDataSet3.setColor(Color.parseColor("#FFD600"));
    lineDataSet3.setLineWidth(3f);
    lineDataSet3.setHighlightEnabled(true);
    lineDataSet3.setDrawHighlightIndicators(true);
    lineDataSet2.setHighLightColor(Color.parseColor("#FFD600"));
    lineDataSet2.setValueTextSize(10f);
    lineDataSet2.setValueTextColor(Color.WHITE);
    lineDataSet2.setFillAlpha(110);

    //Data Set 4

    /*LineDataSet lineDataSet4 = new LineDataSet(random, "Random Numbers");
    lineDataSet4.setAxisDependency(YAxis.AxisDependency.LEFT);
    lineDataSet4.setDrawCircles(false);
    lineDataSet4.setColor(Color.parseColor("#00897B"));
    lineDataSet4.setLineWidth(3f);
    lineDataSet4.setHighlightEnabled(true);
    lineDataSet4.setDrawHighlightIndicators(true);
    lineDataSet4.setHighLightColor(Color.parseColor("#64FFDA"));
    lineDataSet4.setValueTextSize(10f);
    lineDataSet4.setValueTextColor(Color.WHITE);
    lineDataSet4.setFillAlpha(110);*/

    //Data set 5 - User Input
    lineDataSet5 = new LineDataSet(userData, "User Data");
    lineDataSet5.setAxisDependency(YAxis.AxisDependency.LEFT);
    lineDataSet5.setDrawCircles(false);
    lineDataSet5.setColor(Color.parseColor("#9C27B0"));
    lineDataSet5.setLineWidth(3f);
    lineDataSet5.setHighlightEnabled(true);
    lineDataSet5.setDrawHighlightIndicators(true);
    lineDataSet5.setHighLightColor(Color.parseColor("#E040FB"));
    lineDataSet5.setValueTextSize(10f);
    lineDataSet5.setValueTextColor(Color.WHITE);
    lineDataSet5.setFillAlpha(110);

    //Add Data sets
    lineDataSets.add(lineDataSet1);
    lineDataSets.add(lineDataSet2);
    lineDataSets.add(lineDataSet3);
    //lineDataSets.add(lineDataSet4);
    //lineDataSets.add(lineDataSet5);

    lineChart.setData(new LineData(lineDataSets));
    lineChart.setData(new LineData());
    lineChart.setVisibleXRangeMaximum(65f);
    lineChart.setHighlightPerTapEnabled(true);

    //Get legend object
    Legend legend = lineChart.getLegend();

    //Customize Legend
    legend.setForm(Legend.LegendForm.LINE);
    legend.setTextColor(Color.WHITE);

    //Set x axis
    XAxis xAxis = lineChart.getXAxis();
    xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
    xAxis.setTextColor(Color.WHITE);
    xAxis.setTextSize(10f);
    xAxis.setDrawGridLines(true);
    xAxis.setAvoidFirstLastClipping(true);

    //Set y axis
    YAxis yAxis = lineChart.getAxisLeft();
    yAxis.setTextColor(Color.WHITE);
    yAxis.setTextSize(10f);
    yAxis.setDrawGridLines(true);
    yAxis.setAxisMaximum(10f);
    yAxis.setAxisMinimum(-10f);

    //Disable Right y-axis values
    lineChart.getAxisRight().setEnabled(false);

    //Set Description
    lineChart.getDescription().setText("Sin and Cos Functions");
    lineChart.getDescription().setTextColor(Color.WHITE);

    //Update and refresh for User Input
    lineChart.notifyDataSetChanged(); //let the chart know it's data changed
    lineChart.invalidate(); //Refresh

    return view;
}

private int getRandomNumber(int min,int max) {
    return (new Random()).nextInt((max - min) + 1) + min;
}
}

MainActivity:

It manages for 2 tabs, one tab contains the EditText for user to input their value, and the other tab contains the line graph that I want to be updated.

public class MainActivity extends AppCompatActivity {

private SectionsPagerAdapter mSectionsPagerAdapter;

private ViewPager mViewPager;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Toolbar toolbar = findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());

    // Set up the ViewPager with the sections adapter.
    mViewPager = findViewById(R.id.container);
    //mViewPager.setAdapter(mSectionsPagerAdapter);
    setupViewPager(mViewPager);

    TabLayout tabLayout = findViewById(R.id.tabs);
    tabLayout.setupWithViewPager(mViewPager);

    mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
    tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager));

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();
        }
    });

}

private void setupViewPager(ViewPager viewPager){
    SectionsPagerAdapter adapter = new SectionsPagerAdapter(getSupportFragmentManager());
    adapter.addFragment(new Fragment1(), "Fragment 1");
    adapter.addFragment(new Fragment2(), "Fragment 2");
    viewPager.setAdapter(adapter);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {

    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}
}
David
  • 153
  • 1
  • 14
  • Can you describe a bit more what about your current approach isn't working? – Tyler V Jul 22 '18 at 20:16
  • It doesn't seem to update the graph at all. None of the user inputs are being inputted in the graph. – David Jul 22 '18 at 22:06
  • 1
    Have you verified (in a debugger or just with Log/print statements) whether your `sendInput` method in Fragment2 is even being called? Just looking at the code it looks like `xSet` ought to be null since you put an empty line data object on the chart with `lineChart.setData(new LineData());` – Tyler V Jul 23 '18 at 04:40
  • You're right, `sendInput` doesn't get called at all. Why is that? Shouldn't it immediately get called once I pressed the button in Fragment1 through this code `activityCommander.sendInput(userInput);` – David Jul 23 '18 at 23:16
  • Not if `activityCommander` is null. Add some debug statements to check that it's set correctly in `onAttach` and isn't null. It could be null if `getTargetFragment()` returns null (you can cast null to anything, so no `ClassCastException` gets thrown). Might also help if you posted the activity code where you're launching the fragments and setting up their association. – Tyler V Jul 24 '18 at 00:31
  • I tried a different approach to handling the FragmentListener `activityCommander` but I am still getting a null value. When I run the code I got `Fragment Listener: getListener: ClassCastException + com.example.tablayoutexample1.MainActivity cannot be cast to com.example.tablayoutexample1.Fragment1$Fragment1Listener`, which is the catch statement in the new getListener function. I also added the main activity, which I have stated that it manages two tabs; one for user input and the other for the graph. – David Jul 24 '18 at 21:39

1 Answers1

2

You may be able to set the listener more directly. Try adding this method to Fragment1 (and removing getListener)

void setListener(Fragment1Listener listener) {
    activityCommander = listener;
}

then setting the listener in your Activity like this

private void setupViewPager(ViewPager viewPager){
    SectionsPagerAdapter adapter = new SectionsPagerAdapter(getSupportFragmentManager());
    Fragment1 f1 = new Fragment1();
    Fragment2 f2 = new Fragment2();
    f1.setListener(f2);
    adapter.addFragment(f1, "Fragment 1");
    adapter.addFragment(f2, "Fragment 2");
    viewPager.setAdapter(adapter);
}

There could be issues with this approach when the screen is rotated, but this should get you over this hurdle at least. If you want to continue using the getTargetFragment approach you'll need to call setTargetFragment somewhere too (see this question). Something like f1.setTargetFragment(f2); instead of f1.setListener(f2) in the code above.

I think you'll need to make some changes to the graph part once the listener is working to get it working still. For example, replace

lineChart.setData(new LineData(lineDataSets));
lineChart.setData(new LineData()); // this overrides the previous line with an empty data set

with

data.addDataSet([... the sets you want to add ...]);
lineChart.setData(data);

but this should get you started on the right path at least.

Tyler V
  • 9,694
  • 3
  • 26
  • 52
  • Hey thank you Tyler, the FragmentListener is finally working! But I am wondering why you set the listener within the main activity to get it to work, and not within the fragment itself? – David Jul 26 '18 at 00:12
  • 1
    If you mean the `f1.setListener(f2)` command, you have to set the listener somewhere you have both fragments available (so that you tell that instance of Fragment1 that its listener is a specific instance of Fragment2). Otherwise your instance of Fragment1 has no idea about the existence of any instance of Fragment2. Have to have them together in the same room to introduce them, and the Activity where they are created is a natural place. – Tyler V Jul 26 '18 at 00:31