Frame是Android3.0才出来的一个控件,在过去我们显示的都彩Activity然后进行设计,而Frame出来是为了使一个窗口里面能够具有多个Activity。
通过下图下分析,Frame可以在窗口内嵌入多个fragments,接下来的会给出源码来实现下面的功能,,主要完成的任务是当你点击左侧的listView项,右边的fragments内容是随着你的更新而更新的。
Frame生命周期
OnCreate:当你需要在创建frame时对一些组件进行相应的初始化,就可以在这里面写。
onCreateView:当你需要绘制一个界面的时候就需要调用这个回调的方法来返回一个view对象,如果你不想使用任何的界面时可以返回null,还有注意的一件事情是当你横竖屏切换的时候会多次回调这个方法。
onPause():当用户离开这个fragment的时候会回调这个方法,而离开并不代表这个应用的结束
DialogFragment:这个方法是用于绘制一个对话框,这个对话框采用栈的方法,当你结束后可以返回前一个窗体。
ListFragment:这个类似listView,显示一个列表,列表中的项可以通过adapter(比如一个
SimpleCursorAdapter)
来指定列表的项内容,当你单击的时候同样跟ListView
一样会有一个onListItemClick()
回调的函数,来进行单击列表项时的操作。
PreferenceFragment
:这个fragment
的作用类似于
PreferenceActivity
,
显示一个
Preference
对象的层次结构的列表
,就是用户设置界面来理解就可以了。
增加界面:
通常Fragment
的界面采用interface
的方式来选择一个以有的layoutXML
文件来设计界面,通过前面的知识我们可以了解到,Fragments
有一个oncreateView()
的回调方法来决定显示的界面,那么就可以在里面用infalter
来选择,而且返回一个View
对象。
传入
onCreateView()
的
container
参数是你的
fragmentlayout
将要插入的父
ViewGroup
(来自
activity
的
layout
)。
savedInstanceState
参数是一个
Bundle
,如果
fragment
是被恢复的,它提供关于
fragment
的之前的实例的数据。
inflate()
方法有
3
个参数:
想要加载的layout的resourceID。
加载的layout的父ViewGroup。 传入container是很重要的,目的是为了让系统接受所要加载的layout的根view的layout参数, 由它将挂靠的父view指定。
布尔值指示在加载期间,展开的layout是否应当附着到ViewGroup(第二个参数)。 (在这个例子中,指定了false,因为系统已经把展开的layout插入到container– 传入true会在最后的layout中创建一个多余的viewgroup。)
public
static
class
ExampleFragment
extends
Fragment
{
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.example_fragment, container, false); } }
添加一个Fragment
到activity
中:
我们可以把Fragment
加入到一个Activity
的Layout
布局文件中,需要注意的是name
属性的作用就是指定一个fragmentClass
到布局文件中。
有
3
种方法来为一个
fragment
提供一个标识:
为android:id属性提供一个唯一ID。
为android:tag属性提供一个唯一字符串。
如果以上2个你都没有提供,系统使用容器view的ID。
<?
xml version
=
"1.0"
encoding
=
"utf-8"
?>
<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:name="com.example.news.ArticleListFragment" android:id="@+id/list" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent" /> <fragment android:name="com.example.news.ArticleReaderFragment" android:id="@+id/viewer" android:layout_weight="2" android:layout_width="0dp" android:layout_height="match_parent" /> </LinearLayout>
并且fragment
可以添加到viewgroup
里面。
FragmentManager
fragmentManager
=
getFragmentManager
()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction
()
; 此时你就可以彩Add方法来添加一个view来插入到viewgrou中了ExampleFragment
fragment
=
new
ExampleFragment
();
fragmentTransaction.add(R.id.fragment_container, fragment); fragmentTransaction.commit();
还有,
当你添加完成后一定要调用commit
()方法,这个方法的作用类似于Sqllite
中的事务,只要你调用这个方法才会提交你所做的操作。
下图是不同时期会回调的函数
例子:
那么我们还是采用官方的案例来讲解吧,那么最终完成的功能如下图所示,左边是标题列表,右边是根据你的选择来显示相应的内容。
首先我们需要设计一个layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment" android:id="@+id/titles" 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>
当完成XML的设计后我们还需要在Activity中的oncreate应用这个局面文件
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_layout); } TitleFragment是继承listFragment主要用于标题的显示。 public static class TitlesFragment extends ListFragment { boolean mDualPane; int mCurCheckPosition = 0; @Override //下面的回调方法是用来设计你的帘布 public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // 通过setListAdapter来进行绑定列表项,simple_list_item_activated_1是系统自定义的,采用的是text来显示列表项。 setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_activated_1, Shakespeare.TITLES)); View detailsFrame = getActivity().findViewById(R.id.details); mDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE; if (savedInstanceState != null) { // 恢复你最后选择的状态。 mCurCheckPosition = savedInstanceState.getInt("curChoice", 0); } if (mDualPane) { // In dual-pane mode, the list view highlights the selected item. getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); // Make sure our UI is in the correct state. showDetails(mCurCheckPosition); } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("curChoice", mCurCheckPosition); } @Override //当你单击列表项就会回调这个方法,其中position是你单击项的下标,showDetails函数的作用是通过你传送的标题项来进行内容的选择。 public void onListItemClick(ListView l, View v, int position, long id) { showDetails(position); } /** * Helper function to show the details of a selected item, either by * displaying a fragment in-place in the current UI, or starting a * whole new activity in which it is displayed. */ void showDetails(int index) { mCurCheckPosition = index; if (mDualPane) { // We can display everything in-place with fragments, so update // the list to highlight the selected item and show the data. getListView().setItemChecked(index, true); // Check what fragment is currently shown, replace if needed. DetailsFragment details = (DetailsFragment) getFragmentManager().findFragmentById(R.id.details); if (details == null || details.getShownIndex() != index) { //重新设计一张新的视图来显示你所选择标题的正文内容。 details = DetailsFragment.newInstance(index); // 执行一个事务来替代以前存在的fragment FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.replace(R.id.details, details); ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); ft.commit(); } } else { //否则用一个新的DetailsActivity的来进行显示。 // the dialog fragment with selected text. Intent intent = new Intent(); intent.setClass(getActivity(), DetailsActivity.class); intent.putExtra("index", index); startActivity(intent); } } } DetailsFragment类:通过TitlesFragment来显示相应的正文。 public static class DetailsFragment extends Fragment { /** * Create a new instance of DetailsFragment, initialized to *通过传送过来的Index来选择相应的内容 */ public static DetailsFragment newInstance(int index) { DetailsFragment f = new DetailsFragment(); // Supply index input as an argument. Bundle args = new Bundle(); args.putInt("index", index); f.setArguments(args); return f; } public int getShownIndex() { return getArguments().getInt("index", 0); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (container == null) { // We have different layouts, and in one of them this // fragment's containing frame doesn't exist. The fragment // may still be created from its saved state, but there is // no reason to try to create its view hierarchy because it // won't be displayed. Note this is not needed -- we could // just run the code below, where we would create and return // the view hierarchy; it would just never be used. return null; } ScrollView scroller = new ScrollView(getActivity()); TextView text = new TextView(getActivity()); int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getActivity().getResources().getDisplayMetrics()); text.setPadding(padding, padding, padding, padding); scroller.addView(text); text.setText(Shakespeare.DIALOGUE[getShownIndex()]); return scroller; } }