ludolover
ludolover

Reputation: 21

Is having several fragments working "on their own" OK?

I have come up with this design where one fragment serves as a menu, and is replaced whenever a choice is being made.

public class HistoryFragment extends Fragment implements View.OnClickListener {

private Button buttonLinechart;
private Button buttonPiechart;

public HistoryFragment() {
    // 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_history, container, false);
    findViewsById(view);
    setListeners();
    return view;
}

private void findViewsById(View view) {
    buttonLinechart = (Button) view.findViewById(R.id.btn_linechart);

}

private void setListeners() {
    buttonLinechart.setOnClickListener(this);
    buttonPiechart.setOnClickListener(this);
}

@Override
public void onClick(View v) {
    if (v == buttonLinechart) {
        getFragmentManager().beginTransaction()
                .replace(R.id.fragment_container, new LineChartFragment()).commit();
    }
}
}

The new fragment is then accessing sqlite database and drawing a linechart on it's own using getActivity(), so no other communication with the activity seems needed.

public class LineChartFragment extends Fragment implements View.OnClickListener{

private Button buttonBack;
private LineChart chart;

public LineChartFragment() {
    // 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_line_chart, container, false);

    findViewsById(view);
    setListeners();

    createLineChartAll();
    return view;
}

private void createLineChartAll() {
    DBHandler dbHandler = new DBHandler(getActivity(), null, null, 1);

    List<String> list = dbHandler.listInr();

    Timber.d("list sample: " + list.get(0));

    String regex = "^(\\d+\\.\\d{1})";
    Pattern p = Pattern.compile(regex);

    ArrayList<Entry> entries = new ArrayList<>();
    for (int i = 0; i < list.size(); i++) {
        Matcher m = p.matcher(list.get(i));
        while (m.find()) {
            Timber.d("regex result: " + m.group());
            entries.add(new Entry(Float.parseFloat(m.group()), i));
        }
    }

    regex = "\\d{4}-\\d{2}-\\d{2}";
    p = Pattern.compile(regex);

    LineDataSet dataSet = new LineDataSet(entries, "INR");

    ArrayList<String> labels = new ArrayList<>();
    for (int i = 0; i < list.size(); i++) {
        Matcher m = p.matcher(list.get(i));
        while (m.find()) {
            Timber.d("regex result: " + m.group());
            labels.add(m.group());
        }
    }

    LineData data = new LineData(labels, dataSet);
    chart.setData(data);
    chart.setDescription("");
}

private void findViewsById(View view) {
    buttonBack = (Button) view.findViewById(R.id.btn_history);
    chart = (LineChart) view.findViewById(R.id.chart);
}

private void setListeners() {
    buttonBack.setOnClickListener(this);
}

@Override
public void onClick(View v) {
    if (v == buttonBack) {
        getFragmentManager().beginTransaction()
                .replace(R.id.fragment_container, new HistoryFragment()).commit();
    }
}
}

A fellow student is being sceptical, but without a good answer for why, I wanted to ask here. Does this way seem OK?

Cheers

Upvotes: 1

Views: 32

Answers (1)

Matt
Matt

Reputation: 11805

It's fine to have multiple fragments, and have them come in and out of the enclosing activity via the .replace method. In fact, this is relatively standard.

However, what i would say about your code is that it's not considered good practice to have direct interaction between fragments. In your case, you have the two fragments, each which knows about the other. It's best practice to have the individual fragments tell their enclosing activity about the event that just happened, and let the activity determine what to do. This makes it easier for

a. Re-use of fragments within multiple activities

b. configuration specific layouts (ie. landscape vs portait, phone vs tablet)

Right from the google developer site: http://developer.android.com/training/basics/fragments/communicating.html

Often you will want one Fragment to communicate with another, for example to
change the content based on a user event. All Fragment-to-Fragment communication
is done through the associated Activity. Two Fragments should never communicate
directly.

I find it best to define an interface within my fragment which has the callbacks that an enclosing activity needs to implement in order to house the fragment:

public class MyFragment extends Fragment {
  public interface MyFragmentCallbacks {
     void somethingPushed();
  }

  public void onCreate(Bundle savedInstanceState) {
     ...

     findViewById(R.id.some_button).setOnClickListener(
      new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          ((MyFragmentCallbacks)getActivity()).somethingPushed();
        }
      });
  }
}

Upvotes: 1

Related Questions