Android之 Fragment
什么是Fragment:
Android是在Android 3.0 (API level 11)开始引入Fragment的。
可以把Fragment想成Activity中的模块,这个模块有自己的布局,有自己的生命周期,单独处理自己的输入,在Activity运行的时候可以加载或者移除Fragment模块。
可以把Fragment设计成可以在多个Activity中复用的模块。
当开发的应用程序同时适用于平板电脑和手机时,可以利用Fragment实现灵活的布局,改善用户体验。
Fragment的意义:
Android在3.0中引入了fragments的概念,主要目的是用在大屏幕设备上--例如平板电脑上,支持更加动态和灵活的UI设计。平板电脑的屏幕要比手机的大得多,有更多的空间来放更多的UI组件,并且这些组件之间会产生更多的交互。Fragment允许这样的一种设计,而不需要你亲自来管理 viewhierarchy的复杂变化。 通过将activity的布局分散到fragment中, 你可以在运行时修改activity的外观,并在由activity管理的back stack中保存那些变化.
例如, 一个新闻应用可以在屏幕左侧使用一个fragment来展示一个文章的列表,然后在屏幕右侧使用另一个fragment来展示一篇文章--2个fragment并排显示在相同的一个activity中,并且每一个fragment拥有它自己的一套生命周期回调方法,并且处理它们自己的用户输入事件。 因此, 取代使用一个activity来选择一篇文章而另一个activity来阅读文章的方式,用户可以在同一个activity中选择一篇文章并且阅读, 如图所示:
fragment在你的应用中应当是一个模块化和可重用的组件.即,因为fragment定义了它自己的布局, 以及通过使用它自己的生命周期回调方法定义了它自己的行为,你可以将fragment包含到多个activity中. 这点特别重要, 因为这允许你将你的用户体验适配到不同的屏幕尺寸.举个例子,你可能会仅当在屏幕尺寸足够大时,在一个activity中包含多个fragment,并且,当不属于这种情况时,会启动另一个单独的,使用不同fragment的activity.
继续之前那个新闻的例子 -- 当运行在一个特别大的屏幕时(例如平板电脑),应用可以在Activity A中嵌入2个fragment。然而,在一个正常尺寸的屏幕(例如手机)上,没有足够的空间同时供2个fragment用, 因此, Activity A会仅包含文章列表的fragment, 而当用户选择一篇文章时, 它会启动ActivityB,它包含阅读文章的fragment.因此, 应用可以同时支持上图中的2种设计模式。
Fragment的生命周期:
因为Fragment必须嵌入在Acitivity中使用,所以Fragment的生命周期和它所在的Activity是密切相关的。
如果Activity是暂停状态,其中所有的Fragment都是暂停状态;如果Activity是stopped状态,这个Activity中所有的Fragment都不能被启动;如果Activity被销毁,那么它其中的所有Fragment都会被销毁。(对Activity不熟悉的话,请看另一篇文章《Android四大组件之Activity》)
但是,当Activity在活动状态,可以独立控制Fragment的状态,比如加上或者移除Fragment。
当这样进行fragment transaction(转换)的时候,可以把fragment放入Activity的back stack中,这样用户就可以进行返回操作。
Fragment的使用:
创建Fragment
通常, 应当至少实现如下的生命周期方法:
- onCreate()
当创建fragment时, 系统调用该方法.
在实现代码中,应当初始化想要在fragment中保持的必要组件, 当fragment被暂停或者停止后可以恢复. - onCreateView()
fragment第一次绘制它的用户界面的时候, 系统会调用此方法. 为了绘制fragment的UI,此方法必须返回一个View, 这个view是你的fragment布局的根view. 如果fragment不提供UI, 可以返回null. - onPause()
用户将要离开fragment时,系统调用这个方法作为第一个指示(然而它不总是意味着fragment将被销毁.) 在当前用户会话结束之前,通常应当在这里提交任何应该持久化的变化(因为用户有可能不会返回).
大多数应用应当为每一个fragment实现至少这3个方法,但是还有一些其他回调方法你也应当用来去处理fragment生命周期的各种阶段.全部的生命周期回调方法将会在后面章节 Handlingthe Fragment Lifecycle 中讨论.
除了继承基类 Fragment , 还有一些子类你可能会继承:
- DialogFragment
显示一个浮动的对话框.
用这个类来创建一个对话框,是使用在Activity类的对话框工具方法之外的一个好的选择,
因为你可以将一个fragment对话框合并到activity管理的fragment back stack中,允许用户返回到一个之前曾被摒弃的fragment. - ListFragment
显示一个由一个adapter(例如 SimpleCursorAdapter)管理的项目的列表, 类似于ListActivity.
它提供一些方法来管理一个list view, 例如 onListItemClick()回调来处理点击事件. - PreferenceFragment
显示一个 Preference对象的层次结构的列表, 类似于PreferenceActivity.
这在为你的应用创建一个"设置"activity时有用处.
实现Fragment的UI
提供Fragment的UI,必须实现onCreateView()方法。
假设Fragment的布局设置写在example_fragment.xml资源文件中,那么onCreateView()方法可以如下写:
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);
}
}
onCreateView()中container参数代表该Fragment在Activity中的父控件;savedInstanceState提供了上一个实例的数据。
inflate()方法的三个参数:
第一个是resource ID,指明了当前的Fragment对应的资源文件;
第二个参数是父容器控件;
第三个布尔值参数表明是否连接该布局和其父容器控件,在这里的情况设置为false,因为系统已经插入了这个布局到父控件,设置为true将会产生多余的一个View Group。
把Fragment加入Activity
当Fragment被加入Activity中时,它会处在对应的View Group中。
Fragment有两种加载方式:一种是在Activity的layout中使用标签<fragment>声明;另一种方法是在代码中把它加入到一个指定的ViewGroup中。
另外,Fragment它可以并不是Activity布局中的任何一部分,它可以是一个不可见的部分。这部分内容先略过。
加载方式1:通过Activity的布局文件将Fragment加入Activity
在Activity的布局文件中,将Fragment作为一个子标签加入即可。
如:
<?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>
其中android:name属性填上你自己创建的fragment的完整类名。
当系统创建这个Activity的布局文件时,系统会实例化每一个fragment,并且调用它们的onCreateView()方法,来获得相应fragment的布局,并将返回值插入fragment标签所在的地方。
有三种方法为Fragment提供ID:
android:id属性:唯一的id
android:tag属性:唯一的字符串
如果上面两个都没提供,系统使用容器view的ID。
加载方式2:通过编程的方式将Fragment加入到一个ViewGroup中
当Activity处于Running状态下的时候,可以在Activity的布局中动态地加入Fragment,只需要指定加入这个Fragment的父View Group即可。
首先,需要一个FragmentTransaction实例:
FragmentManager fragmentManager = getFragmentManager() FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
(注,如果import android.support.v4.app.FragmentManager;那么使用的是:FragmentManager fragmentManager = getSupportFragmentManager();)
之后,用add()方法加上Fragment的对象:
ExampleFragment fragment = new ExampleFragment(); fragmentTransaction.add(R.id.fragment_container, fragment); fragmentTransaction.commit();
其中第一个参数是这个fragment的容器,即父控件组。
最后需要调用commit()方法使得FragmentTransaction实例的改变生效。
Fragment对menu菜单的操作
android4.0之后引入了fragment的概念,它的生命周期函数和activity几乎一样。对菜单的操作也是通过onCreateOptionMenu()实现的。
例子:
onCreate() 期间调用 setHasOptionsMenu() 来指出fragment愿意添加item到选项菜单
public static class DetailsFragment extends Fragment { @Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
}
操作菜单
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// TODO Auto-generated method stub
super.onCreateOptionsMenu(menu, inflater);
menu.add("Menu 1a").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add("Menu 1b").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
Toast.makeText(getActivity(), "index is"+getShownIndex()+" && menu text is "+item.getTitle(), 1000).show();
return super.onOptionsItemSelected(item);
}
还记得文章上面提到过的阅读新闻的例子吗?(讲Fragment意义那里)下面,我就来实现这个功能:
我们先贴出效果图:
(竖屏)
点击第二条新闻后,如下图:
(横屏状态)
下面贴上代码:
先要创建两个布局文件,一个用于横屏、一个用于竖屏:
其代码分别为:
layout/main.xml:
<?xml version="1.0" encoding="utf-8"?>
<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.topcsa.test_fragment.ListFragment"
android:id="@+id/titles"
android:layout_weight="1"
android:layout_width="0px"
android:layout_height="match_parent"/> </LinearLayout>
layout-land/main.xml:
<?xml version="1.0" encoding="utf-8"?>
<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.topcsa.test_fragment.ListFragment"
android:id="@+id/titles"
android:layout_weight="1"
android:layout_width="0px"
android:layout_height="match_parent"/>
<FrameLayout
android:id="@+id/detail"
android:layout_weight="2"
android:layout_width="0px"
android:layout_height="match_parent"
android:background="?android:attr/detailsElementBackground"></FrameLayout> </LinearLayout>
包下的文件如下:
下面依次贴上代码(代码有详细注释):
package com.topcsa.test_fragment; public final class Data {
public static final String[] titles = { "日本买两栖舰欲圆航母梦 最后得鸡肋",
"中将:中国坦克兵素质优异 96A凸显四大质量问题", "台湾政坛又炸锅:与大陆首席谈判代表是“共谍”" }; public static final String[] DETAIL = {
"日本防卫相小野寺五典8月4日在东京都发表演讲又一次强调了两栖攻击舰的重要性,指出日本将从美国购买黄蜂级两栖攻击舰。若发展顺利,新型两栖攻击舰将于2019年服役日本海上自卫队,成为其最大舰艇。",
"首先,这主要是一场坦克乘员素质的比赛,比技能、比体能、比心理素质。应该说中国坦克兵表现堪称完美。射击比赛第一,除了装备因素外,娴熟的操作技能和全车乘员协调一致的动作,是获胜的关键。装备性能可以提供高命中率的客观条件,但在高速行进中(在视频中看,96A坦克行进间射击的时速应在20-25千米/小时)能发发命中目标,则主要取决于人的因素。而T-72坦克行进间射击时速都不超过10千米/小时,甚至是短停射击,差距就大了。96A坦克上反式稳像火控的反应速度、精度和在复杂工况条件的稳定性,大大超过了T-72下反式火控。96坦克初期型号也是下反式稳像火控,远不至于在这次比赛中T-72坦克表现得这么差,这就是坦克乘员的素质在起作用了。只能说中国坦克兵的素质高于国外同行。另外,我军坦克兵射击训练的难度大大超过了这次竞赛条件。譬如射击跑道是起伏的、弯曲的,目标间夹角不小于17密位(这次比赛也就1-2密位),打完一个目标后需要大角度、高速度调炮瞄向下一个目标;目标不仅是隐显的,还是隐蔽的,周围不能有明显方位物(这次比赛在靶标附近设立了一个独立家屋,便于搜索和指示目标);96A坦克在训练中以打运动目标为主,目标时速不低于12千米/小时(这次目标是固定的,目标色彩与背景反差也较大),如果换成运动目标,估计T-72坦克脱靶的更多。装备也是重要因素。在视频中看到,T-72坦克弹迹和弹着点都能看到,说明它的炮口初速不大于1000米/秒,而96A坦克根本看不到弹迹,弹着点烟尘也小很多,贯穿布靶时形成一个小洞,说明我炮口初速和钨芯脱壳穿甲弹的侵彻力远高于T-72坦克。我命中部位大多进入目标9区(井字格,四周8个区,中心为9区),说明我坦克炮千米立靶密集度集中,精度高、弹道稳定。",
"两岸发展关系,台湾的政治稳定至关重要。台湾社会很多人或许没有意识到,台湾政治的一些深层无序已经相当严重。大陆与世界很多经济体谈自贸区或类似协定,但唯有同台湾的这一份冒出遭学生抗议并搁置的离奇周折,张显耀也是第一位被疑遭大陆“策反”的首席谈判代表。", };
}
package com.topcsa.test_fragment; import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ScrollView;
import android.widget.TextView; public class DetailFragment extends Fragment {
public static DetailFragment newInstance(int index){
DetailFragment f=new DetailFragment();
Bundle bundle=new Bundle();
bundle.putInt("index", index);
f.setArguments(bundle);//将bundle对象作为Fragment的参数保存
return f;
} public int getShownIndex(){
//获取要显示的列表项索引
return getArguments().getInt("index",0);
} @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if(container==null){
return null;
}
//创建一个滚动视图
ScrollView sl=new ScrollView(getActivity());
TextView text=new TextView(getActivity());
text.setPadding(10, 10, 10, 10);
sl.addView(text);
//设置文本框中要显示的文本
text.setText(Data.DETAIL[getShownIndex()]);
return sl;
}
}
package com.topcsa.test_fragment; import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView; public class ListFragment extends android.app.ListFragment {
boolean dualPane;// 是否在同一界面上显示列表和内容
int curCheckPosition = 0;// 当前选择的索引位置 @Override
public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState);
//为列表设置适配器
setListAdapter(new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_checked, Data.titles));
//获取布局文件中添加的帧布局管理器
View detailFrame=getActivity().findViewById(R.id.detail);
//判断是否在一屏上同时显示列表和详细内容
dualPane=detailFrame!=null&&detailFrame.getVisibility()==View.VISIBLE; if(savedInstanceState!=null){
//更新当前的索引位置
curCheckPosition=savedInstanceState.getInt("curChoice",0);
}
if(dualPane)
{
//设置列表为单选模式
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
showDetails(curCheckPosition);//显示详细内容
}
}
//该方法在STOP()之前执行,用于保存当前选中项的列表项的索引值
@Override
public void onSaveInstanceState(Bundle outState) {
// TODO Auto-generated method stub
super.onSaveInstanceState(outState);
outState.putInt("curChoice", curCheckPosition);
} @Override
public void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
showDetails(position);//显示详细内容
} private void showDetails(int index) {
curCheckPosition=index;
if(dualPane){
getListView().setItemChecked(index, true);//设置选中状态
//获取用于显示详细信息的Fragment
DetailFragment df=(DetailFragment) getFragmentManager().findFragmentById(R.id.detail);
if(df==null||df.getShownIndex()!=index){
//创建一个新的DetailFragment实例,用于显示当前选项对应的详细内容
df=DetailFragment.newInstance(index);
//在Activity中管理fragment,需要使用FragmentManager
//获得一个FragmentTransaction实例
FragmentTransaction ft=getFragmentManager().beginTransaction();
//替换原来显示的详细内容
ft.replace(R.id.detail, df);
//设置转换效果
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();//提交事务
}
}else{
//竖屏
Intent intent=new Intent(getActivity(),MainActivity.DetailActivity.class);
intent.putExtra("index", index);
startActivity(intent);
} } }
package com.topcsa.test_fragment; import android.app.Activity;
import android.app.ActionBar;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.os.Build; public class MainActivity extends Activity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main); } public static class DetailActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
// 判断是否为横屏
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
finish();
return;
}
if (savedInstanceState == null) {
//在初始化时,插入一个显示详细内容的Fragment
//实例化DetailFragment对象
DetailFragment detail = new DetailFragment();
//设置传递的参数
detail.setArguments(getIntent().getExtras());
//添加一个显示详细内容的Fragment
getFragmentManager().beginTransaction()
.add(android.R.id.content, detail).commit();
}
}
} }
最后,别忘了清单文件的配置:(内部Activity的注册)
<activity android:name="com.topcsa.test_fragment.MainActivity$DetailActivity"
android:label="详细内容"></activity>
新闻阅读Demo下载:http://download.csdn.net/detail/af74776/7806353
本文重点参考了的文章(基本上算是大汇总吧):http://blog.csdn.net/lilu_leo/article/details/7671533
http://www.cnblogs.com/mengdd/archive/2013/01/08/2851368.html
http://www.cnblogs.com/yydcdut/p/3921297.html
Android之 Fragment的更多相关文章
- Android:Activity+Fragment及它们之间的数据交换.
Android:Activity+Fragment及它们之间的数据交换 关于Fragment与Fragment.Activity通信的四种方式 比较好一点的Activity+Fragment及它们之间 ...
- Android中Fragment和ViewPager那点事儿(仿微信APP)
在之前的博文<Android中使用ViewPager实现屏幕页面切换和引导页效果实现>和<Android中Fragment的两种创建方式>以及<Android中Fragm ...
- Android中Fragment与Activity之间的交互(两种实现方式)
(未给Fragment的布局设置BackGound) 之前关于Android中Fragment的概念以及创建方式,我专门写了一篇博文<Android中Fragment的两种创建方式>,就如 ...
- Android中Fragment的两种创建方式
fragment是Activity中用户界面的一个行为或者是一部分.你可以在一个单独的Activity上把多个Fragment组合成为一个多区域的UI,并且可以在多个Activity中再使用.你可以认 ...
- android之Fragment基础详解(一)
一.Fragment的设计哲学 Android在3.0中引入了fragments的概念,主要目的是用在大屏幕设备上--例如平板电脑上,支持更加动态和灵活的UI设计.平板电脑的屏幕比手机的大得多,有 ...
- Android使用Fragment来实现ViewPager的功能(解决切换Fragment状态不保存)以及各个Fragment之间的通信
以下内容为原创,转载请注明:http://www.cnblogs.com/tiantianbyconan/p/3364728.html 我前两天写过一篇博客<Android使用Fragment来 ...
- android之fragment的使用
android中的fragment与html中的div很类似,下图中通过左边的按键可以控制右边的显示内容.右边的内容就是一个fragment,通过点击按键来控制fragment的实现. 工程目录 需要 ...
- Android使用Fragment定义弹出数字键盘
fragment主布局文件 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmln ...
- Android ViewPager Fragment使用懒加载提升性能
Android ViewPager Fragment使用懒加载提升性能 Fragment在如今的Android开发中越来越普遍,但是当ViewPager结合Fragment时候,由于Androi ...
- 33.Android之Fragment学习
Fragment Android是在Android 3.0 (API level 11)开始引入Fragment的. 可以把Fragment想成Activity中的模块,这个模块有自己的布局,有自己的 ...
随机推荐
- Sort--快速排序
快速排序 1 public class QuickSort{ 2 3 public static int Partition(int[] a,int low,int high){ 4 int pivo ...
- HDU-4690 EBCDIC 映射,模拟,沙茶
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4690 纯沙茶模拟题... //STATUS:C++_AC_93MS_228KB #include &l ...
- rpi good tutorial
http://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/quick-start/
- 【现代程序设计】【Homework-01】
1维的最大子数组之和 对于1维的最大子数组之和 假设f[i]表示:对于1..i这个序列中,包含i这个元素的最大序列的值 则对于f[i],0<i<=n; 应该有 f[i]=max(a[i], ...
- elecworks 图框管理器
图框管理器中存储的是图纸模板(图框),新建图框的步骤如下: 1 数据库---图框管理器----新建 2 打开图框属性设置窗口,设置图框属性,设置好之后点击确定 3 右击图框图标---打开(进入图框绘制 ...
- 【Stage3D学习笔记续】山寨Starling(三):Starling核心渲染流程
这篇文章我们剔除Starling的Touch事件体系和动画体系,专门来看看Starling中的渲染流程实现,以及其搭建的显示列表结构. 由于Starling是模仿Flash的原生显示列表,所以我们可以 ...
- 教你50招提升ASP.NET性能(十三):精选技巧集合
(19)A selection of tips 招数19: 精选技巧集合 Including height and width in <img /> tags will allow you ...
- Genymotion与本地电脑共享文件夹的方法
首先打开vbox的界面,左侧列表应该可以看到Genymotion添加的虚拟机,如图:对要设置的虚拟机单击右键,在弹出的菜单中点击“设置...”,弹出设置页面后点击左侧最后一个标签“共享文件夹”,点击右 ...
- 远程重启IIS服务
方法一: $UserName = "administrator" $serverpass = "pass" $server = "10.4.19.60 ...
- SQLyog MySQL GUI 11.13 Ultimate 中文破解版【转载】
SQLyog是一个易于使用的.快速而简洁的图形化管理MYSQL数据库的工具,它能够在任何地点有效地管理你的数据库! SQLyog MySQL GUI是我常用的一个桌面工具,功能强大,让你有使用MSSQ ...