Fragment开发实例
SVN源码下载地址: https://svn.codespot.com/a/eclipselabs.org/demo1/trunk/myAndroid2_2
或者请下载附件myAndroid2_2.rar.
说明:
这个实例原本是用ANDROID2.2开发的,因此取了2.2的名字。 现在, 该应用的ANDROID已经提升到4.0.
Fragment的使用
android应用了一个UI设计理念:一个activity可以包含多个fragment。 比如实例中的国家管理模型, 当进入国家管理时(layout activity), 左边出现已经输入的国家列表(list fragment), 点击列表中的国家名称,右边则出现该国家的具体内容(details fragment)。 以上设计就是典型的 list-details 模式。
这个模式不仅适用于水平展示的屏幕(landscape), 也适用于竖直展示的屏幕(portrait)。 当水平展示时,屏幕左侧是list, 右侧是details; 而竖直展示时,屏幕只显示list, 点击其中的具体内容,屏幕显示details.
实例中的代码解释:
1. CountryLayout.java 整个国家管理界面。 该界面提供了2个MENU,一个是返回主菜单(back), 另一个是添加菜单(new).
2. CountryListFragment.java 显示了所有的国家列表。
3. CountryDetailsFragment.java 显示了具体的国家内容。该界面提供了几个按钮: 在landscape情况下, 有修改(edit)按钮和删除(delete)按钮, 在portrait情况下,不仅有修改和删除两个按钮,还增加了返回列表(list)的按钮(to previous)
4. CountryDialogFragment.java 显示了国家的操作界面,保存按钮保存(save)添加或修改的内容, 关闭按钮(close)关闭操作界面。
源码要点:
1. Activity和fragment之间的联系
1) 在fragment中建立接口,并利用fragment的onAttach生命周期建立activity和fragment的联系。
xxxfragment.javapublic interface OnHeadlineSelectedListener { /** Called by HeadlinesFragment when a list item is selected */ public void onArticleSelected(int position, long id); }@Override public void onAttach(Activity activity) { super.onAttach(activity); // This makes sure that the container activity has implemented // the callback interface. If not, it throws an exception. try { mCallback = (OnHeadlineSelectedListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnHeadlineSelectedListener"); } }xxxactivity.java implement xxxFragment.OnHeadlineSelectedListener @Overridepublic void onArticleSelected(int position, long id) { // Capture the article fragment from the activity layout CountryDetailsFragment articleFrag = (CountryDetailsFragment) getFragmentManager().findFragmentById(R.id.details); if (articleFrag != null) { articleFrag.updateArticleView(id); } else { newDetailsFragment(id); }}// 找到管理器 FragmentTransaction ft = getFragmentManager().beginTransaction();// 将创建的details fragment替换原来的fragment。 ft.replace(R.id.details, details); 。。。。。。//提交 ft.commit();
//通过ID寻找的方法 CountryDetailsFragment details = (CountryDetailsFragment) getFragmentManager().findFragmentById(R.id.details);// 还有一种是通过Tag来查找的,我这里暂时没有实例。
Bundle bundle = new Bundle(); bundle.putLong("selectedId", id); bundle.putBoolean("mDualPane", mDualPane);// 将参数邦定fragment. details.setArguments(bundle);View v = inflater.inflate(R.layout.area_details, container, false); text1 = (TextView) v.findViewById(R.id.areaName); text2 = (TextView) v.findViewById(R.id.areaDetails);......
<resources xmlns:android="http://schemas.android.com/apk/res/android"> <item name="country_layout" type="layout">@layout/country_onepane</item> <bool name="has_two_panes">false</bool></resources>
<resources> <item name="country_layout" type="layout">@layout/country_twopane</item> <bool name="has_two_panes">true</bool></resources>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:layout_height="match_parent" /> </LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:layout_weight="1" android:layout_width="0px" android:layout_height="match_parent" /> <FrameLayout android:id="@+id/details" android:layout_weight="1" android:layout_width="0px" android:layout_height="match_parent" android:background="?android:attr/detailsElementBackground" /> </LinearLayout>
1) protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 根据屏幕判断是调用landscape的xml文件,还是portrait的xml文件。 [i]setContentView(R.layout.country_layout);[/i] // mIsDualPane是判断屏幕是否为landscape的关键,它将作为一个参数传递给其它fragment. View detailsFrame = findViewById(R.id.details); mIsDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE; } public void onArticleSelected(int position, long id) { // 判断是否存在details fragment, 如果不存在,需要创建 details fragment. CountryDetailsFragment articleFrag = (CountryDetailsFragment) getFragmentManager().findFragmentById(R.id.details); if (articleFrag != null) { articleFrag.updateArticleView(id); } else { newDetailsFragment(id); }}public interface OnHeadlineSelectedListener { /** Called by HeadlinesFragment when a list item is selected */ public void onArticleSelected(int position, long id); } @Override public void onAttach(Activity activity) { super.onAttach(activity); // This makes sure that the container activity has implemented // the callback interface. If not, it throws an exception. try { mCallback = (OnHeadlineSelectedListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnHeadlineSelectedListener"); } }@Override public void onListItemClick(ListView l, View v, int position, long id) { if(mDualPane){ mCallback.onArticleSelected(position, id); getListView().setItemChecked(position, true); } showDetails(id); }void showDetails(long id){ // 如果是landscape 屏幕, 直接显示detailsFragment, 如果是portrait屏幕,这显示detailsActivity. if (mDualPane) { // Check what fragment is currently shown, replace if needed. CountryDetailsFragment details = (CountryDetailsFragment) getFragmentManager().findFragmentById(R.id.details); if(details==null || id==0){ this.newDetailsFragment(details, id); }else details.updateArticleView(id); }else{ this.newDetailsActivity(id); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { ........ mDualPane = getArguments().getBoolean("mDualPane"); if(mDualPane){ // landscape时fragment调用UI组件的方法。 View v = inflater.inflate(R.layout.area_details, container, false); text1 = (TextView) v.findViewById(R.id.areaName); text2 = (TextView) v.findViewById(R.id.areaDetails); previous = (Button) v.findViewById(R.id.country_toprevious); delete = (Button) v.findViewById(R.id.country_delete); edit = (Button) v.findViewById(R.id.country_edit); if(previous!=null) previous.setVisibility(View.GONE); toggleButtons(View.INVISIBLE); if(delete!=null) delete.setOnClickListener(deleteMe); if(edit!=null) edit.setOnClickListener(editMe); long _id = getArguments().getLong("selectedId"); if(_id>0) this.displayDetails(_id); return v; }else{ // portrait时,activity调用UI组件的方法。(同intent调用方法) getActivity().setContentView(R.layout.area_details); extras = getActivity().getIntent().getExtras(); previous = (Button) getActivity().findViewById(R.id.country_toprevious);delete = (Button) getActivity().findViewById(R.id.country_delete);edit = (Button) getActivity().findViewById(R.id.country_edit);if(previous!=null) previous.setOnClickListener(previousMe);if(delete!=null) delete.setOnClickListener(deleteMe); if(edit!=null) edit.setOnClickListener(editMe); display(); return null; } }void showDialog(){ FragmentTransaction ft = getFragmentManager().beginTransaction(); Fragment prev = getFragmentManager().findFragmentByTag(COUNTRY_DIALOG_FRAGMENT); if (prev != null) { ft.remove(prev); } ft.addToBackStack(null); // Create and show the dialog. DialogFragment newFragment = CountryDialogFragment.newInstance(); Bundle bundle = new Bundle(); bundle.putLong("selectedId", id); bundle.putBoolean("mDualPane", mDualPane); newFragment.setArguments(bundle); newFragment.show(ft, COUNTRY_DIALOG_FRAGMENT); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 设置dialog的显示style和theme int style = DialogFragment.STYLE_NORMAL, theme = android.R.style.Theme_Holo_Light; setStyle(style, theme); Bundle args = getArguments(); if(args!=null) id = args.getLong("selectedId"); if(args!=null) mDualPane = args.getBoolean("mDualPane"); Log.i(TAG,"args is null:" + ((args==null)?true:false)); ...... }