Fragment和Activity的回调机制又是OOP的一次完美演绎!
接下来,我们来实现图书信息管理的实例: 在一个Activity中,有两个Fragment,其中左边的Fragment用于显示新闻列表,右表的Fragment用于显示新闻详情.
1) 新闻详情
public class BookDetailFragment extends Fragment { public static final String ITEM_ID = "item_id"; // 保存该Fragment显示的Book对象 BookContent.Book book; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 如果启动该Fragment时包含了ITEM_ID参数 if (getArguments().containsKey(ITEM_ID)) { book = BookContent.ITEM_MAP.get(getArguments().getInt(ITEM_ID)); // ① } } // 重写该方法,该方法返回的View将作为Fragment显示的组件 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // 加载/res/layout/目录下的fragment_book_detail.xml布局文件 View rootView = inflater.inflate(R.layout.fragment_book_detail, container, false); if (book != null) { // 让book_title文本框显示book对象的title属性 ((TextView) rootView.findViewById(R.id.book_title)).setText(book.title); // 让book_desc文本框显示book对象的desc属性 ((TextView) rootView.findViewById(R.id.book_desc)).setText(book.desc); } return rootView; } }
该Fragment会加载res/layout/目录下的fragment_book_detail.xml界面布局文件。 上面①处的代码用于获取启动该Fragment时传入的ITEM_ID参数,并根据该ID获取BookContent的ITEM_MAP中的图书信息。
public class BookContent { // 定义一个内部类,作为系统的业务对象 public static class Book { public Integer id; public String title; public String desc; public Book(Integer id, String title, String desc) { this.id = id; this.title = title; this.desc = desc; } @Override public String toString() { return title; } } // 使用List集合记录系统所包含的Book对象 public static List<Book> ITEMS = new ArrayList<Book>(); // 使用Map集合记录系统所包含的Book对象 public static Map<Integer, Book> ITEM_MAP = new HashMap<Integer, Book>(); static { // 使用静态初始化代码,将Book对象添加到List集合、Map集合中 addItem(new Book(1, "疯狂Java讲义", "一本全面、深入的Java学习图书,已被多家高校选做教材。")); addItem(new Book(2, "疯狂Android讲义", "Android学习者的首选图书,常年占据京东、当当、 " + "亚马逊3大网站Android销量排行榜的榜首")); addItem(new Book(3, "轻量级Java EE企业应用实战", "全面介绍Java EE开发的Struts 2、Spring 3、Hibernate 4框架")); } private static void addItem(Book book) { ITEMS.add(book); ITEM_MAP.put(book.id, book); } }
2) 新闻列表
新闻列表,我们可以直接使用ListFragment,当使用ListFragment的子类时,无需重写onCreateView()方法——与ListActivity类似的是,只要调用ListFragment的setAdapter()方法为该Fragment设置Adapter即可。
public class BookListFragment extends ListFragment { private Callbacks mCallbacks; // 定义一个回调接口,该Fragment所在Activity需要实现该接口 // 该Fragment将通过该接口与它所在的Activity交互 public interface Callbacks { public void onItemSelected(Integer id); } // 当该Fragment被添加、显示到Activity时,回调该方法 @Override public void onAttach(Activity activity) { super.onAttach(activity); // 如果Activity没有实现Callbacks接口,抛出异常 if (!(activity instanceof Callbacks)) { throw new IllegalStateException("BookListFragment所在的Activity必须实现Callbacks接口!"); } // 把该Activity当成Callbacks对象 mCallbacks = (Callbacks) activity; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 为该ListFragment设置Adapter setListAdapter(new ArrayAdapter<BookContent.Book>(getActivity(), android.R.layout.simple_list_item_activated_1, android.R.id.text1, BookContent.ITEMS)); } // 当该Fragment从它所属的Activity中被删除时回调该方法 @Override public void onDetach() { super.onDetach(); // 将mCallbacks赋为null。 mCallbacks = null; } // 当用户点击某列表项时激发该回调方法 @Override public void onListItemClick(ListView listView, View view, int position, long id) { super.onListItemClick(listView, view, position, id); // 激发mCallbacks的onItemSelected方法 mCallbacks.onItemSelected(BookContent.ITEMS.get(position).id); } public void setActivateOnItemClick(boolean activateOnItemClick) { getListView().setChoiceMode(activateOnItemClick ? ListView.CHOICE_MODE_SINGLE : ListView.CHOICE_MODE_NONE); } }
3) Fragment与Activity的通信
首先,我们定义Activity的布局文件:
<?xml version="1.0" encoding="utf-8"?> <!-- 定义一个水平排列的LinearLayout,并指定使用中等分隔条 --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" android:divider="?android:attr/dividerHorizontal" android:orientation="horizontal" android:showDividers="middle" > <!-- 添加一个Fragment --> <fragment android:id="@+id/book_list" android:name="com.chen.yuan.BookListFragment" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" /> <!-- 添加一个FrameLayout容器 --> <FrameLayout android:id="@+id/book_detail_container" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="3" /> </LinearLayout>
定义BookActivity,用于管理列表Fragment和新闻详情Fragment
public class SelectBookActivity extends Activity implements BookListFragment.Callbacks { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 加载/res/layout目录下的activity_book_twopane.xml布局文件 setContentView(R.layout.activity_book_twopane); } // 实现Callbacks接口必须实现的方法 @Override public void onItemSelected(Integer id) { // 创建Bundle,准备向Fragment传入参数 Bundle arguments = new Bundle(); arguments.putInt(BookDetailFragment.ITEM_ID, id); // 创建BookDetailFragment对象 BookDetailFragment fragment = new BookDetailFragment(); // 向Fragment传入参数 fragment.setArguments(arguments); // 使用fragment替换book_detail_container容器当前显示的Fragment getFragmentManager().beginTransaction().replace(R.id.book_detail_container, fragment).commit(); // ① } }
此处,我们讲下Fragment和Activity之间的数据传递:
· Activity向Fragment传递数据: 在Activity中创建Bundle数据包,并调用Fragment的setArguments(Bundle bundle)方法即可将Bundle数据包传递给Fragment。
· Fragment想Activity传递数据或Activity需要在Fragment运行中进行实时通信:
在Fragment中定义一个内部回调接口,再让包含该Fragment的Activity实现该回调接口,这样Fragment即可调用该回调方法将数据传递给Activity。比如上述示例中,我们再BookListFragment里定义了接口Callbacks,然后在用户点击某列表项时激发该回调接口里的onItemSelected方法mCallbacks.onItemSelected(BookContent.ITEMS.get(position).id); 在方法参数传递时,将数据传递都到Activity。
代码下载(免费): http://download.csdn.net/detail/zuiwuyuan/7957801