2013-04-11 4 views
5

Ich versuche eine App zu erstellen, die einen Master-/Detailfluss mit Fragmenten hat. Wenn Sie ein Objekt auswählen, öffnet sich ein Detailfragment, das dann ein anderes Fragment "öffnen" und zum hinteren Stapel hinzufügen kann.Umschalten von Fragmenten im Master-/Detailfluss

Ich habe Klassen umbenannt, um zu veranschaulichen, was sie tun.

public class ListOfDetails extends FragmentActivity { 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     ... 
    } 

    //Callback method indicating that an item with the given ID was selected. 
    public void onItemSelected(String id) { 
     // Performing logic to determine what fragment to start omitted 

     if (ifTwoPanes()) { 
      Fragment fragment = new DetailFragmentType1(); 
      getSupportFragmentManager().beginTransaction().replace(R.id.aContainer, fragment).commit(); 
     } else { 
      Intent newIntent = new Intent(this, SinglePaneFragmentWrapper.class); 
      newIntent.putExtra("id", id); 
      startActivity(newIntent); 
     } 
    } 

    // My attempt at making it possible to change displayed fragment from within fragments 
    public void changeDetailFragment(Fragment fragment) { 
     FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 
     transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); 
     transaction.addToBackStack(null); 
     transaction.replace(R.id.aContainer, fragment); 
     transaction.commit(); 
    } 
} 

Ein Beispiel für eines der Detailfragmente. Es gibt viele verschiedene Fragmente, die unter verschiedenen Umständen erstellt werden können.

public class DetailFragmentType1 extends Fragment { 
    private ListOfDetails parent; 

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

     Activity a = getActivity(); 
     if (a instanceof ListOfDetails) { 
      parent = (ListOfDetails) a; 
     } 
    } 

    @Override 
    public void onActivityCreated(Bundle savedInstanceState) { 
     super.onActivityCreated(savedInstanceState); 

     Button aButton = (Button) getActivity().findViewById(R.id.aButton); 
     aButton.setOnClickListener(new OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       parent.changeDetailFragment(new SubDetailFragment()); 
      } 
     }); 
    } 
} 

Wenn am Telefon ist eine Wrapper-Aktivität verwendet, um das Fragment zu halten

public class SinglePaneFragmentWrapper extends FragmentActivity { 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     // Duplicate logic must be performed to start fragment 
     // Performing logic to determine what fragment to start omitted 
     String id = getIntent().getStringExtra("id"); 
     if(id == "DetailFragmentType1") { 
      Fragment fragment = new DetailFragmentType1(); 
      getSupportFragmentManager().beginTransaction().replace(R.id.aContainer, fragment).commit(); 
     } else { 
      ... 
     } 
    } 
} 

Was ist der richtige Weg ist, um das Fragment zu ändern, die im Detailbereich unter diesen Umständen geöffnet ist? Meine Methode fühlt sich an wie ein Hack bei Verwendung von zwei Fensterbereichen und funktioniert nicht einmal, wenn nur ein Fensterbereich verwendet wird, da getParent() von SinglePaneFragmentWrapper null zurückgibt, was es mir unmöglich macht, parent.changeDetailFragment() aufzurufen.

Dies ist eine komplizierte Frage, hoffentlich habe ich es gut erklärt. Lass es mich wissen, wenn ich etwas verpasst habe. Danke

Antwort

1

Es gibt viele Meinungen dazu und viele Möglichkeiten, es zu tun. Ich denke in diesem Fall ist das Problem "Wer ist verantwortlich für das Ändern des Fragments?" Auf der Oberfläche scheint es, dass ein Listener auf der Schaltfläche der offensichtliche Ort ist, aber dann sollte das Fragment nicht wissen, wo es gehostet ist (ein Symptom dafür ist, ein unerwünschtes Ergebnis wie null von getParent() zu bekommen).

In Ihrem Fall würde ich vorschlagen, implementieren Sie eine "Listener" -Schnittstelle in den Eltern und "Benachrichtigen" von dem Fragment .. Wenn der Elternteil benachrichtigt wird, ändert es das Fragment. Auf diese Weise das Fragment sich nicht ändert (also nicht wissen muss, wie) .. so .. für Ihren Fall ..

eine neue Schnittstelle hinzufügen:

public interface FragmentChangeListener { 
    void onFragmentChangeRequested(Fragment newFragment); 
} 

die Schnittstelle in Ihrem ListOfDetails Implement Aktivität

public class ListOfDetails extends FragmentActivity implements FragmentChangeListener { 
@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    ... 
} 

//Callback method indicating that an item with the given ID was selected. 
public void onItemSelected(String id) { 
    // Performing logic to determine what fragment to start omitted 

    if (ifTwoPanes()) { 
     Fragment fragment = new DetailFragmentType1(); 
     getSupportFragmentManager().beginTransaction().replace(R.id.aContainer, fragment).commit(); 
    } else { 
     Intent newIntent = new Intent(this, SinglePaneFragmentWrapper.class); 
     newIntent.putExtra("id", id); 
     startActivity(newIntent); 
    } 
} 

// My attempt at making it possible to change displayed fragment from within fragments 
public void changeDetailFragment(Fragment fragment) { 
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 
    transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); 
    transaction.addToBackStack(null); 
    transaction.replace(R.id.aContainer, fragment); 
    transaction.commit(); 
} 

// This is the interface implementation that will be called by your fragments 
void onFragmentChangeRequested(Fragment newFragment) { 
    changeDetailFragment(newFragment); 
} 

} 

Added Zuhörer zum Detail Fragment

public class DetailFragmentType1 extends Fragment { 

    private FragmentChangeListener fragmentChangeListener; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     // Actually you might not have an activity here.. you should probably be 
     // doing this in onAttach 
     //Activity a = getActivity(); 
     //if (a instanceof ListOfDetails) { 
     // parent = (ListOfDetails) a; 
     //} 
    } 

    @Override 
    public void onActivityCreated(Bundle savedInstanceState) { 
     super.onActivityCreated(savedInstanceState); 

     Button aButton = (Button) getActivity().findViewById(R.id.aButton); 
     aButton.setOnClickListener(new OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       // parent.changeDetailFragment(new SubDetailFragment()); 
       notifyFragmentChange(new SubDetailFragment()); 
      } 
     }); 
    } 

    @Override 
    public void onAttach(Activity activity) { 
     // This is called when the fragment is attached to an activity.. 
     if (activity instanceof FragmentChangeListener) { 
      fragmentChangeListener = (FragmentChangeListener) activity; 
     } else { 
     // Find your bugs early by making them clear when you can... 
     if (BuildConfig.DEBUG) { 
      throw new IllegalArgumentException("Fragment hosts must implement FragmentChangeListener"); 
     } 
     } 
    } 

    private void notifyFragmentChange(Fragment newFragment) { 
     FragmentChangeListener listener = fragmentChangeListener; 
     if (listener != null) { 
     listener.onFragmentChangeRequested(newFragment); 
     } 
    } 
} 

und Umsetzung der gleiche Schnittstelle zu Ihrer Einscheibensicherheitsglas Aktivität ...

public class SinglePaneFragmentWrapper extends FragmentActivity implements FragmentChangeListener { 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     // Duplicate logic must be performed to start fragment 
     // Performing logic to determine what fragment to start omitted 
     String id = getIntent().getStringExtra("id"); 
     if(id == "DetailFragmentType1") { 
      Fragment fragment = new DetailFragmentType1(); 
      getSupportFragmentManager().beginTransaction().replace(R.id.aContainer, fragment).commit(); 
     } else { 
      ... 
     } 
    } 
// My attempt at making it possible to change displayed fragment from within fragments 
public void changeDetailFragment(Fragment fragment) { 
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 
    transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); 
    transaction.addToBackStack(null); 
    transaction.replace(R.id.aContainer, fragment); 
    transaction.commit(); 
} 

// This is the interface implementation that will be called by your fragments 
void onFragmentChangeRequested(Fragment newFragment) { 
    changeDetailFragment(newFragment); 
} 

} 

Man beachte die Ähnlichkeit zwischen dem Einscheibensicherheitsglas und Ihren Mehrscheiben-Aktivitäten .. Dies läßt vermuten, dass Sie entweder alle von dem duplizierten Code setzen könnten (changefragment usw.) in eine einzelne Tätigkeit, die sie beide erweitern oder das in vielleicht sind sie die gleichen Tätigkeiten mit verschiedenen Layouts ...

Ich hoffe, dass hilft, viel Glück.

Grüße, CJ