Sunday, April 21, 2013

Handle onListItemClick() of ListFragment, to pass data between fragment

Last exercise "Handle different layout for phone and tablet" demonstrate how to display difference layouts on phone and tablet. Here we are going to handle user click on list item and display the item on the detail fragment.

There are two case in our approach:

- In phone mode with single pane, the target fragment (MyDetailFragment) is not yet displayed. We have to replace the fragment in container with FragmentTransaction, and pass the data via Bundle by calling setArguments(), and retrieve the data in target fragment by calling getArguments() method.

Replace fragment in phone mode


- In tablet mode with two panes, the detail fragment is already displayed. We can call a method in target fragment via FragmentManager and find the target fragment by calling findFragmentById() method.

Pass data between fragment in tablet mode


Modify /res/layout/layout_detailfragment.xml to add a TextView to display the clicked item.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/title_detailfragment"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Detail Fragment"/>
    <TextView
        android:id="@+id/text_detail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    
</LinearLayout>


MainActivity.java
package com.example.androiddualmode;

import android.os.Bundle;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.app.ListFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends Activity {
 
 // if run on phone, isSinglePane = true
 // if run on tablet, isSinglePane = false
 static boolean isSinglePane;
 
 static String[] month ={
   "January", "February", "March", "April",
   "May", "June", "July", "August",
   "September", "October", "November", "December"};

 public static class MyListFragment extends ListFragment {

  @Override
  public void onActivityCreated(Bundle savedInstanceState) {
   // TODO Auto-generated method stub
   super.onActivityCreated(savedInstanceState);
   
   ListAdapter myArrayAdapter = 
     new ArrayAdapter<String>(
       getActivity(), android.R.layout.simple_list_item_1, month);
   setListAdapter(myArrayAdapter);
   
  }

  @Override
  public void onListItemClick(ListView l, View v, int position, long id) {
   
   String clickedDetail = (String)l.getItemAtPosition(position);
   
   if(isSinglePane == true){
    /*
     * The second fragment not yet loaded. 
     * Load MyDetailFragment by FragmentTransaction, and pass 
     * data from current fragment to second fragment via bundle.
     */
    MyDetailFragment myDetailFragment = new MyDetailFragment();
    Bundle bundle = new Bundle();
    bundle.putString("KEY_DETAIL", clickedDetail);
    myDetailFragment.setArguments(bundle);
    FragmentTransaction fragmentTransaction =
      getActivity().getFragmentManager().beginTransaction();
    fragmentTransaction.replace(R.id.phone_container, myDetailFragment);
    fragmentTransaction.commit();
    
   }else{
    /*
     * Activity have two fragments. Pass data between fragments
     * via reference to fragment
     */
    
    //get reference to MyDetailFragment
    MyDetailFragment myDetailFragment =
      (MyDetailFragment)getFragmentManager().findFragmentById(R.id.detail_fragment);
    myDetailFragment.updateDetail(clickedDetail);
    
   }
  }

 }
 
 public static class MyDetailFragment extends Fragment {
  
  TextView textDetail;

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {
   // TODO Auto-generated method stub
   View view = inflater.inflate(R.layout.layout_detailfragment, null);
   textDetail = (TextView)view.findViewById(R.id.text_detail);
   
   Bundle bundle = getArguments();
   if(bundle != null){
    String detail = bundle.getString("KEY_DETAIL", "no argument pass");
    textDetail.setText(detail);
   }
   
   return view;
  }

  public void updateDetail(String detail) {
   textDetail.setText(detail);
  }

 }

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  View v = findViewById(R.id.phone_container);
  if(v == null){
   //it's run on tablet
   isSinglePane = false;
   /*
    * MyListFragment and MyDetailFragment have been loaded in XML,
    * no need load.
    */
   
  }else{
   //it's run on phone
   //Load MyListFragment programmatically
   isSinglePane = true;
   
   if(savedInstanceState == null){
    //if's the first time created
    MyListFragment myListFragment = new MyListFragment();
    FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
    fragmentTransaction.add(R.id.phone_container, myListFragment);
    fragmentTransaction.commit();
   }
  }
 }

}




download filesDownload the files.

Next:
- Allow navigate BACK through FragmentTransaction, by calling addToBackStack()


3 comments:

  1. after successfully replacing the details fragment in phone mode

    after pressing back button app exits

    any solution for this???

    ReplyDelete
  2. You can manage the Fragent transaction by adding the TransactionManager object into the addToBackStack() method. By doing this user can easly move through each fragment transaction by pressing the back button.
    Here is an article shows what are the methods to add a Fragment

    ReplyDelete
  3. hello,
    i am creating an android application with many fragments , but i create them in a slightly different way.

    1. On mainActivity i create a vector of 6 fragments.
    2. i create a viewpager , which keeps the fragments and they scroll left/ right.
    3. One of the 6 fragments is the Product Categories list, so , on itemClick i Need to show a New fragment with the appropriate Products.

    can this happen , to create and replace the categories Fragment with a new Products Fragment ?

    i dont know if i make myself clear, but can you help me with that?

    ReplyDelete