2012-04-04 7 views
6

È da una settimana che cerco di eseguire il porting di una semplice app basata su attività sui frammenti. Sono completamente bloccato.Bloccato con il porting dall'attività al frammento

Questa bestia è una semplice lista, dettagli, Aggiungi/Modifica app con menu di scelta rapida e menu opzioni. Ho cercato di renderlo corretto: frammenti e attività ciascuno nel proprio file, utilizzando il pacchetto di supporto v4 per telefono e tablet, i frammenti fanno tutto ciò che un frammento riutilizzabile dovrebbe fare e i callback (molti di loro) volano per informare le attività e frammenti su cosa fare. Conversione da SQLiteOpenHelper a ContentProvider, conversione da optionmenu a actionbarmenu, and, and, and, ... (quasi tutto ciò che ho usato è deprecato ora).

È orribile. La mia semplice e piccola applicazione basata sull'attività lavorativa ha quasi 3 volte la dimensione e molte cose non funzionano ancora.

Se è necessario, posso aggiungere il mio codice qui - ma è un sacco di cose (siete stati avvisati).

La mia domanda: c'è qualcuno disposto a condividere un esempio completo con Elenco, Dettagli E Aggiungi/Modifica? Questo esempio dovrebbe usare file separati per Fragments and Activities (non quel pacchetto all-in-one di Google).

Si prega di non sottovalutare. Mi piacerebbe davvero vedere come farlo bene.

Molte grazie in anticipo.

EDIT:

Ecco l'attività di partenza con i suoi due layout (res/layout per telefono e res/layout di grandi terreni per i tablet) e il menu contestuale:

public class ActivityList extends FragmentActivity implements FragmentList.MyContextItemSelectedListener, 
                 FragmentList.MyDeleteListener, 
                 FragmentList.MyListItemClickListener, 
                 FragmentList.MyOptionsItemSelectedListener, 
                 FragmentDetails.MyDeleteListener, 
                 FragmentDetails.MyOptionsItemSelectedListener { 

    @Override 
    public void myContextItemSelected(final int action, final long id) { 
     if (action == R.id.men_add) { 
      processEdit(0); 
     } else if (action == R.id.men_delete) { 
      processUpdateList(); 
     } else if (action == R.id.men_details) { 
      processDetails(id); 
     } else if (action == R.id.men_edit) { 
      processEdit(id); 
     } 
    } 

    @Override 
    public void myDelete(final long id) { 
     processUpdateList(); 
    } 

    @Override 
    public void myListItemClick(final long id) { 
     processDetails(id); 
    } 

    @Override 
    public void myOptionsItemSelected(final int action) { 
     myOptionsItemSelected(action, 0); 
    } 

    @Override 
    public void myOptionsItemSelected(final int action, final long id) { 
     if (action == R.id.men_add) { 
      processEdit(0); 
     } else if (action == R.id.men_edit) { 
      processEdit(id); 
     } else if (action == R.id.men_preferences) { 
      processPreferences(); 
     } 
    } 

    @Override 
    protected void onActivityResult(final int requestCode, final int resultCode, final Intent intent) { 
     processUpdateList(); 
    } 

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

     setContentView(R.layout.activitylist); 
    } 

    private void processEdit(final long id) { 
     Intent intent = new Intent(this, ActivityEdit.class); 
     intent.putExtra("ID", id); 
     startActivityForResult(intent, MyConstants.DLG_TABLE1EDIT); 
    } 

    private void processDetails(final long id) { 
     if (Tools.isXlargeLand(getApplicationContext())) { 
      Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.right); 
      if (fragment == null || 
        (fragment instanceof FragmentDetails && ((FragmentDetails) fragment).getCurrentId() != id)) { 
       fragment = new FragmentDetails(id); 

       FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 
       transaction.replace(R.id.right, fragment); 
       transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); 
       transaction.commit(); 
      } 
     } else { 
      Intent intent = new Intent(this, ActivityDetails.class); 
      intent.putExtra("ID", id); 
      startActivityForResult(intent, MyConstants.DLG_TABLE1SHOW); 
     } 
    } 

    private void processPreferences() { 
     Intent intent = new Intent(this, MyPreferenceActivity.class); 
     startActivityForResult(intent, MyConstants.DLG_PREFERENCES); 
    } 

    private void processUpdateList() { 
     // TODO: 
    } 
} 

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="horizontal" > 

    <fragment 
     class="com.test.app.FragmentList" 
     android:id="@+id/fragmentlist" 
     android:layout_height="match_parent" 
     android:layout_width="match_parent" 
     android:name="com.test.app.FragmentList" /> 
</LinearLayout> 

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="horizontal" > 

    <fragment 
     class="com.test.app.FragmentList" 
     android:id="@+id/fragmentlist" 
     android:layout_height="match_parent" 
     android:layout_weight="1" 
     android:layout_width="0dip" 
     android:name="com.test.app.FragmentList" /> 

    <FrameLayout 
     android:id="@+id/right" 
     android:layout_height="match_parent" 
     android:layout_weight="2" 
     android:layout_width="0dip" /> 
</LinearLayout> 

Ecco il ListFragment con suo layout fila, optionsmenu e contextmenu:

public class FragmentList extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> { 

    private SimpleCursorAdapter   adapter; 
    private AlertDialog     alertDialog; 
    private Context      context; 
    private MyContextItemSelectedListener contextItemSelectedListener; 
    private MyDeleteListener    deleteListener; 
    private long       id; 
    private MyListItemClickListener  listItemClickListener; 
    private ListView      listView; 
    private MyOptionsItemSelectedListener optionsItemSelectedListener; 

    public interface MyContextItemSelectedListener { 
     public void myContextItemSelected(int action, long id); 
    } 

    public interface MyDeleteListener { 
     public void myDelete(long id); 
    } 

    public interface MyListItemClickListener { 
     public void myListItemClick(long id); 
    } 

    public interface MyOptionsItemSelectedListener { 
     public void myOptionsItemSelected(int action); 
    } 

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

     context = getActivity().getApplicationContext(); 

     listView = getListView(); 

     getActivity().getSupportLoaderManager().initLoader(MyConstants.LDR_TABLE1LIST, null, this); 

     adapter = new SimpleCursorAdapter(context, 
              R.layout.fragmentlist_row, 
              null, 
              new String[] { Table1.DESCRIPTION }, 
              new int[] { R.id.fragmentlist_row_description }, 
              CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); 
     setListAdapter(adapter); 
     setListShown(false); 

     registerForContextMenu(listView); 

     if (bundle != null && bundle.containsKey("ID")) { 
      id = bundle.getLong("ID"); 
      listItemClickListener.myListItemClick(id); 
     } 

     if (Tools.isXlargeLand(context)) { 
      listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); 
     } 

     setHasOptionsMenu(true); 
    } 

    @Override 
    public void onAttach(final Activity activity) { 
     super.onAttach(activity); 

     // Reduced: Check for implemented listeners 
    } 

    @Override 
    public boolean onContextItemSelected(final MenuItem menuItem) { 
     AdapterContextMenuInfo adapterContextMenuInfo = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); 

     final long id = adapterContextMenuInfo.id; 

     if (menuItem.getItemId() == R.id.men_delete) { 
      processAlertDialog(id); 
      return true; 
     } else { 
      contextItemSelectedListener.myContextItemSelected(menuItem.getItemId(), adapterContextMenuInfo.id); 
     } 

     return super.onContextItemSelected(menuItem); 
    } 

    @Override 
    public void onCreateContextMenu(final ContextMenu contextMenu, final View view, final ContextMenuInfo contextMenuInfo) { 
     super.onCreateContextMenu(contextMenu, view, contextMenuInfo); 

     if (view.getId() == android.R.id.list) { 
      getActivity().getMenuInflater().inflate(R.menu.fragmentlist_context, contextMenu); 
     } 
    } 

    @Override 
    public Loader<Cursor> onCreateLoader(final int id, final Bundle bundle) { 
     MyCursorLoader loader = null; 

     switch (id) { 
      case MyConstants.LDR_TABLE1LIST: 
       loader = new MyCursorLoader(context, 
              MySQLiteOpenHelper.TABLE1_FETCH, 
              null); 
       break; 
     } 

     return loader; 
    } 

    @Override 
    public void onCreateOptionsMenu(final Menu menu, final MenuInflater menuInflater) { 
     super.onCreateOptionsMenu(menu, menuInflater); 

     menu.clear(); 

     menuInflater.inflate(R.menu.fragmentlist, menu); 
    } 

    @Override 
    public void onListItemClick(final ListView listView, final View view, final int position, final long id) { 
     super.onListItemClick(listView, view, position, id); 

     this.id = id; 

     if (Tools.isXlargeLand(context)) { 
      listView.setItemChecked(position, true); 
     } 

     listItemClickListener.myListItemClick(id); 
    } 

    @Override 
    public void onLoaderReset(final Loader<Cursor> loader) { 
     adapter.swapCursor(null); 
    } 

    @Override 
    public void onLoadFinished(final Loader<Cursor> loader, final Cursor cursor) { 
     adapter.swapCursor(cursor); 

     setListShown(true); 
    } 

    @Override 
    public boolean onOptionsItemSelected(final MenuItem menuItem) { 
     optionsItemSelectedListener.myOptionsItemSelected(menuItem.getItemId()); 

     return super.onOptionsItemSelected(menuItem); 
    } 

    @Override 
    public void onSaveInstanceState(final Bundle bundle) { 
     super.onSaveInstanceState(bundle); 

     bundle.putLong("ID", id); 
    } 

    private void processAlertDialog(final long id) { 
     final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity()); 
     alertDialogBuilder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { 

      @Override 
      public void onClick(final DialogInterface dialogInterface, final int which) { 
       dialogInterface.dismiss(); 
      } 
     }); 
     alertDialogBuilder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 

      @Override 
      public void onClick(final DialogInterface dialogInterface, final int which) { 
       MyApplication.getSqliteOpenHelper().deleteTable1(id); 

       alertDialog.dismiss(); 

       deleteListener.myDelete(id); 
      } 
     }); 
     alertDialogBuilder.setCancelable(false); 
     alertDialogBuilder.setMessage(R.string.txt_reallydelete); 

     alertDialog = alertDialogBuilder.create(); 
     alertDialog.show(); 
    } 
} 

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_height="wrap_content" 
    android:layout_width="fill_parent" 
    android:orientation="horizontal" 
    android:paddingBottom="2dip" 
    android:paddingTop="2dip" > 

    <TextView 
     style="@style/TextViewLarge" 
     android:id="@+id/fragmentlist_row_description" 
     android:textStyle="bold" /> 
</LinearLayout> 


<menu 
    xmlns:android="http://schemas.android.com/apk/res/android"> 

    <item 
     android:icon="@drawable/ic_menu_add" 
     android:id="@+id/men_add" 
     android:showAsAction="ifRoom|withText" 
     android:title="@string/txt_add" /> 

    <item 
     android:icon="@drawable/ic_menu_preferences" 
     android:id="@+id/men_preferences" 
     android:showAsAction="ifRoom|withText" 
     android:title="@string/txt_preferences" /> 
</menu> 

<menu 
    xmlns:android="http://schemas.android.com/apk/res/android"> 

    <item 
     android:id="@+id/men_details" 
     android:title="@string/txt_details" /> 

    <item 
     android:id="@+id/men_edit" 
     android:title="@string/txt_edit" /> 

    <item 
     android:id="@+id/men_delete" 
     android:title="@string/txt_delete" /> 
</menu> 

Questo è DetailsActivity:

public class ActivityDetails extends FragmentActivity implements FragmentDetails.MyDeleteListener, 
                    FragmentDetails.MyOptionsItemSelectedListener { 

    private long id; 

    @Override 
    public void myDelete(final long id) { 
     setResult(RESULT_OK); 
     finish(); 
    } 

    @Override 
    public void myOptionsItemSelected(final int action, final long id) { 
     if (action == R.id.men_add) { 
      processEdit(0); 
     } else if (action == R.id.men_edit) { 
      processEdit(id); 
     } else if (action == R.id.men_preferences) { 
      processPreferences(); 
     } 
    } 

    @Override 
    protected void onActivityResult(final int requestCode, final int resultCode, final Intent intent) { 
     if (requestCode == MyConstants.DLG_PREFERENCES || requestCode == MyConstants.DLG_TABLE1EDIT) { 
      finish(); 

      startActivity(getIntent()); 
     } 
    } 

    @Override 
    protected void onCreate(final Bundle bundle) { 
     super.onCreate(bundle); 

     if (bundle != null) { 
      if (bundle.containsKey("ID")) { 
       id = bundle.getLong("ID"); 
      } 
     } else { 
      Bundle bundleExtras = getIntent().getExtras(); 
      if (bundleExtras != null) { 
       id = bundleExtras.getLong("ID"); 
      } 

      processDetails(id); 
     } 
    } 

    @Override 
    public void onSaveInstanceState(final Bundle bundle) { 
     super.onSaveInstanceState(bundle); 

     bundle.putLong("ID", id); 
    } 

    private void processDetails(final long id) { 
     FragmentDetails fragment = new FragmentDetails(id); 

     FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 
     transaction.replace(android.R.id.content, fragment); 
     transaction.commit(); 
    } 

    private void processEdit(final long id) { 
     Intent intent = new Intent(this, ActivityEdit.class); 
     intent.putExtra("ID", id); 
     startActivityForResult(intent, MyConstants.DLG_TABLE1EDIT); 
    } 

    private void processPreferences() { 
     Intent intent = new Intent(this, MyPreferenceActivity.class); 
     startActivityForResult(intent, MyConstants.DLG_PREFERENCES); 
    } 
} 

Ecco il DetailsFragment con layout e Menu:

public class FragmentDetails extends Fragment { 

    private AlertDialog     alertDialog; 
    private MyDeleteListener    deleteListener; 
    private long       id; 
    private MyOptionsItemSelectedListener optionsItemSelectedListener; 
    private TextView      textViewDescription; 
    private TextView      textViewId; 

    public FragmentDetails() { 
     id = 0; 
    } 

    public FragmentDetails(final long id) { 
     this.id = id; 
    } 

    public long getCurrentId() { 
     return id; 
    } 

    public interface MyDeleteListener { 
     public void myDelete(long id); 
    } 

    public interface MyOptionsItemSelectedListener { 
     public void myOptionsItemSelected(int action, long id); 
    } 

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

     if (bundle != null && bundle.containsKey("ID")) { 
      id = bundle.getLong("ID"); 
     } 

     setHasOptionsMenu(true); 
    } 

    @Override 
    public void onAttach(final Activity activity) { 
     super.onAttach(activity); 

     // Reduced 
    } 

    @Override 
    public void onCreateOptionsMenu(final Menu menu, final MenuInflater menuInflater) { 
     super.onCreateOptionsMenu(menu, menuInflater); 

     menu.clear(); 

     menuInflater.inflate(R.menu.fragmentdetails, menu); 
    } 

    @Override 
    public View onCreateView(final LayoutInflater inflater, final ViewGroup viewGroup, final Bundle bundle) { 
     View view = inflater.inflate(R.layout.fragmentdetails, null); 

     textViewDescription = (TextView) view.findViewById(R.id.tv_description); 
     textViewId = (TextView) view.findViewById(R.id.tv_id); 

     if (id != 0) { 
      Table1 table1; 
      if ((table1 = MyApplication.getSqliteOpenHelper().getTable1(id)) != null) { 
       textViewDescription.setText(Tools.defaultString(table1.getDescription())); 
       textViewId.setText(Tools.defaultString(String.valueOf(table1.getId()))); 
      } 
     } 

     return view; 
    } 

    @Override 
    public boolean onOptionsItemSelected(final MenuItem menuItem) { 
     if (menuItem.getItemId() == R.id.men_delete) { 
      processAlertDialog(id); 
      return true; 
     } else { 
      optionsItemSelectedListener.myOptionsItemSelected(menuItem.getItemId(), id); 
     } 

     return super.onOptionsItemSelected(menuItem); 
    } 

    @Override 
    public void onSaveInstanceState(final Bundle bundle) { 
     super.onSaveInstanceState(bundle); 

     bundle.putLong("ID", id); 
    } 

    private void processAlertDialog(final long id) { 
     final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity()); 
     alertDialogBuilder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { 

      @Override 
      public void onClick(final DialogInterface dialogInterface, final int which) { 
       alertDialog.dismiss(); 
       alertDialog = null; 
      } 
     }); 
     alertDialogBuilder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 

      @Override 
      public void onClick(final DialogInterface dialogInterface, final int which) { 
       MyApplication.getSqliteOpenHelper().deleteTable1(id); 

       alertDialog.dismiss(); 
       alertDialog = null; 

       deleteListener.myDelete(id); 
      } 
     }); 
     alertDialogBuilder.setCancelable(false); 
     alertDialogBuilder.setMessage(R.string.txt_reallydelete); 

     alertDialog = alertDialogBuilder.create(); 
     alertDialog.show(); 
    } 
} 

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_height="wrap_content" 
    android:layout_width="match_parent" 
    android:orientation="vertical" > 

    <LinearLayout 
     android:layout_height="wrap_content" 
     android:layout_width="match_parent" 
     android:orientation="horizontal" > 

     <TextView 
      style="@style/TextViewStandard" 
      android:layout_weight="1" 
      android:text="@string/txt_id" /> 

     <TextView 
      style="@style/TextViewStandard" 
      android:id="@+id/tv_id" 
      android:layout_weight="1" /> 
    </LinearLayout> 

    <LinearLayout 
     android:layout_height="wrap_content" 
     android:layout_width="match_parent" 
     android:orientation="horizontal" > 

     <TextView 
      style="@style/TextViewStandard" 
      android:layout_weight="1" 
      android:text="@string/txt_description" /> 

     <TextView 
      style="@style/TextViewStandard" 
      android:id="@+id/tv_description" 
      android:layout_weight="1" /> 
    </LinearLayout> 
</LinearLayout> 

<menu 
    xmlns:android="http://schemas.android.com/apk/res/android"> 

    <item 
     android:icon="@drawable/ic_menu_add" 
     android:id="@+id/men_add" 
     android:showAsAction="ifRoom|withText" 
     android:title="@string/txt_add" /> 

    <item 
     android:icon="@drawable/ic_menu_edit" 
     android:id="@+id/men_edit" 
     android:showAsAction="ifRoom|withText" 
     android:title="@string/txt_edit" /> 

    <item 
     android:icon="@drawable/ic_menu_delete" 
     android:id="@+id/men_delete" 
     android:showAsAction="ifRoom|withText" 
     android:title="@string/txt_delete" /> 

    <item 
     android:icon="@drawable/ic_menu_preferences" 
     android:id="@+id/men_preferences" 
     android:showAsAction="ifRoom|withText" 
     android:title="@string/txt_preferences" /> 
</menu> 

io non pubblicare l'EditActivity perché è semplicemente un FragmentActivity senza un frammento.

+0

Il mio suggerimento è prendere una cosa alla volta invece di provare a fare tutto in una volta. In questo modo è anche più facile da testare. – Warpzit

+0

Grazie. CursorLoader funziona, la barra delle azioni è quasi funzionante (attualmente gli ascoltatori di due attività si attivano quando qualcuno fa clic sulla barra di azione perché c'è un'attività di avvio e un'attività del telefono per un componente di un frammento). Il problema più grande per me è che non ottengo la navigazione giusta. Mi piacerebbe mostrare il mio codice qui, ma temo che alla gente non piacciano molte cose. –

+1

Se si desidera creare un collegamento a un intero progetto, inserirlo su github e fornire il collegamento al progetto. – Warpzit

risposta

3

Questa potrebbe non essere la risposta completa ma una parte della risposta: Hai ancora un'attività principale, nel tuo xml in cui hai usato listview ora aggiungi un framelayout.Poi nelle vostre attività onCreate si aggiunge seguente:

 mMainFragment = new ListFragment(); 
     FragmentManager fragmentManager = getSupportFragmentManager(); 
     FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); 

     fragmentTransaction.replace(R.id.center_container, mMainFragment); 

     fragmentTransaction.commit(); 
     mCurrentFragment = mMainFragment; 

Nella tua listfragment

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, 
     Bundle savedInstanceState) { 
    // setup view 
    View view = inflater.inflate(R.layout.calendar_list, null); 

    mListAdapter = new CustomAdapter(getActivity(), R.layout.calendar_row, (ArrayList<Item>) mFullList); 
    setListAdapter(mListAdapter); 

    return view; 
} 

XML per listfragment:

<somelayout> 
    <ListView android:id="@id/android:list" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent"/> 
</somelayout> 

Cliccare sulla lista è innescato in frammento con:

@Override 
public void onListItemClick(ListView list, View view, int position, long id) { 
    final Item item = (Item) list.getAdapter().getItem(position); 
    mListener.OnListClick(item); 
} 

Wh ich utilizza questo ascoltatore:

interfaccia pubblica OnListItemClickListener { vuoto pubblico OnListClick (elemento articolo); }

La Listfragment deve avere questo in alto:

@Override 
public void onAttach(Activity activity) { 
    super.onAttach(activity); 
    try { 
     mListener = (OnListItemClickListener) activity; 
    } catch (ClassCastException e) { 
     throw new ClassCastException(activity.toString() + " must implement OnListItemClickListener"); 
    } 
} 

L'attività principale sottoscrive indi implementando l'interfaccia e avviare il frammento dettaglio quando l'ascoltatore è attivato.

EDIT: OK, quindi la tua domanda è molto più fondamentale :) ricorda che oncreate viene chiamato nella tua attività ogni volta che ruoti così la tua attività ha bisogno di ricordare quale frammento mostrare proprio come deve ricordare quale vista mostrare. Inoltre è necessario aggiungere frammenti allo stack posteriore o il tasto indietro non funzionerà con essi. Pensa ai frammenti come viste con funzione, non sono attività.

+0

Grazie ancora. Sì, questa è la parte facile ;-) Ce l'ho già lavorando. Ho deciso di pubblicare le mie cose qui. Dammi solo qualche minuto - rimuoverò il codice che non appartiene alla navigazione ecc. –

1

Proverò a rispondere a uno dei problemi. Scrivi:

"Ora ruoto e faccio clic sul pulsante Indietro. Mi aspetto di tornare dalla pagina di modifica alla pagina di catalogo oe nel mio caso l'app termina".

Dal tuo esempio di codice sembra che tu non stia aggiungendo transazioni allo stack posteriore. Chiama addToBackStack() appena prima di chiamare commit(), in questo modo:

transaction.replace(R.id.fragment_container, newFragment); 
transaction.addToBackStack(null); 
transaction.commit(); 
+0

Grazie. Ci proveremo. –

Problemi correlati