Chapter 12. Fragment
12.1 Fragment简介
是从Android3.0开始新增的概念, 意为碎片。为了更好的运用越来越大的屏幕空间而生,目前已经成为流行的开发方式。
Fragment是一种可以嵌入在Activity当中的UI片段.用来组建Activity界面的局部模块, 也可以说一个Actiivty界面可以由多个Fragment组成;其行为与Activity很相似, 有自己对应的布局(包含具体的View), 它有自己的生命周期,接收自己的输入事件,并且可以从运行中的activity中添加或移除.
一个fragment必须总是嵌入在一个activity中,同时fragment的生命周期受activity的影响;本质上会产生一个FrameLayout,它加载的布局为其子布局
Fragment需要包含在Activity中,一个Activity里面可以包含一个或者多个Fragment,而且一个Activity可以同时展示多个Fragment。同时,Fragment也具有自己的布局。
12.2 相关API
Fragment
View onCreateView()
onActivityCreated()//当Activity创建好
getActivity()//宿主
setArguments(), getArgument()
ListFragment
setListAdapter(adapter)
onListItemClick()
getFragmentManager()
FragmentActivity
getSupportFragmentManager()
FragmentManager
beginTransaction()
FragmentTransaction
add(), replace(), remove()
show() <-->hide()
commit()
addToBackStack()
12. 3 使用
Fragment的静态加载
1. 定义Fragment的子类, 并在onCreateView()中加载一个布局文件
2. 在布局文件中通过<fragment>指定自定义Fragment
3. 我们的Activity必须继承于FragmentActivity
Fragment的动态加载
1. 定义Fragment的子类, 并在onCreateView()中加载一个布局文件
2. 我们的Activity必须继承于FragmentActivity
3. 在代码中, 动态创建Fragment对象, 并添加到指定的ViewGroup中
add,replace,remove,hide,show
12.4 Fragment的生命周期
image
12.4.1 生命周期状态
-
运行状态:当一个Fragment可见的,并且与它关联的Activity正处于运行状态,那么该Fragment也处于运行状态(可见可操作)
-
暂停状态:当一个Activity进入暂停状态,与它相关联的可见Fragment也会进入暂停转态(可见,但不可操作)
-
停止状态:当一个Activity进入停止状态时,与它关联的Fragment就会进入到停止状态。或者通过调用FragmentTranscation的remove()、replace()方法将Fragment从Activity中移除。如果事务提交之前调用addToBackStack()方法,这时Fragment也会进入到停止状态(存在,但不可见)
-
死亡状态//对象不存在
12.4.2 生命周期方法
-
onAttach()//当fragment与Activity建立关联时调用
-
onCreate()
-
onCreateView()//为fragment创建视图(加载布局)时调用
-
onActivityCreated()//与fragment相关联的Activity已经创建完毕时候调用
-
onStart()
-
onResume()
-
onPause()
-
onStop()
-
onDestroyView()//当与fragment相关联的视图被移除的时候调用
-
onDestroy()
-
onDetach()//当fragment与Activity解除关联的时候调用
12.5 创建Fragment
与创建Activity类似,要创建一个Fragment必须创建一个类继承自Fragment。需要注意的是,Android系统提供了两个Fragment类,分别是android.app.Fragment和android.support.v4.app.Fragment。继承android.app.Fragment类则程序只能兼容Android4.0上的系统,继承android.support.v4.app.Fragment类可以兼容低版本的Android系统。
12.5.1 创建Fragment
重写Fragment的onCreateView()方法,并在该方法中调用了LayoutInflater的inflate()方法将Fragment布局动态加载进来
public class NewsListFragment extends Fragment
{
@override
public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstaceState)
{
View v=inflater.inflate(R.layout.fragment,container,false);
return v;
}
}
12.5.2 添加Fragment
Fragment创建完成不能单独使用,还需要将Fragment添加到Activity中。在Activity中添加Fragment有两种方式,一种是直接在布局文件中添加,将Fragment作为整个Activity布局的一部分;另一种是当程序运行时,动态地将Fragment添加到Activity中。
布局文件中添加Fragment时,可以使用<fragment></fragment>标签,该标签与其他控件的标签类似,但必须要指定android:name属性,其属性值为Fragment的全路径名称。布局文件fragment.xml中的代码如下所示:
...
<fragment
android:name="cn.itcast.fragment.NewsListFragment"
android:id="@+id/newslist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
....
当Activity运行时,也可以将Fragment动态加载到Activity的布局中,这种添加方式首先要获取Fragment的实例对象,然后获取FragmentManager对象,调用FragmentManager的beginTransaction()方法开启事务并得到FragmentTransaction对象,最后调用FragmentTransaction的add()方法将Fragment添加到Activity,并通过commit()方法提交事务。动态添加Fragment的示例代码如下:
public class MainActivity extends Activity
{
@SuppressLInt("NewApi")
@override
protected void onCreate(BUndle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//实例化Fragment对象
NewsListFragment fragment=new NewsListFragment();
//获取FragmentManager实例
FragmentTranscation beginTransaction=fm.beginTransaction();
//添加一个Fragment
beginTransaction.add(R.id.ll,fragment);
//提交
beginTransaction.commit();
}
}
需要注意的是FragmentTransaction的add(int containerViewId,Fragment fragment)方法,它的第一个参数表示Fragment要放入ViewGroup的资源id,第二个参数是要添加的Fragment。例如,上述代码中的R.id.ll是一个线性布局,那么beginTransaction.add(R.id.ll,fragment);的意思就是将Fragment添加到该线性布局中。
12.5.3 Fragment与Activity间通信
虽然Fragment是嵌套在Activity中显示的,但是Fragment和Activity都是各自存在于一个独立的类中,它们之间并没有明显的方式来直接进行通信。在实际开发中,经常会遇到需要在Activity中获取Fragment实例和FragmentActivity实例的情况。
1. 在Activity中获取Fragment实例
FragmentManager提供了一个findFragmentById()方法,该方法是专门用于从布局文件中获取Fragment实例的。
findFragmentById()方法有一个参数,该参数代表Fragment在Activity布局中的id。例如在Activity的布局中添加了SettingListFragment,并在布局中指定SettingListFragment的id为R.id.settingcontent,这时就可以使用getFragmentManager().findFragmentById(R.id.settingcontent);得到SettingListFragment实例。具体代码如下所示:
SettingListFragment fListFragemnt=(SettingListFragment)getFragmentManager().findFragmentById(R.id.settingcontent);
2. 在Fragment中获取Activity实例
在Fragment中调用getActivity()方法可以获取到当前Fragment相关联的Activity实例对象。例如,在MainAcivity中添加了SettingListFragment,那么在SettingListFragment中可以调用getActivity()方法得到MainActiviy实例,具体代码如下所示:
MainActivity activtiy=(MainActivity)getActivity();
获取到Activity实例之后,在Fragment中就可以通过该实例调用Activity中的方法了。另外,当Fragment需要Context对象时,也可以使用该方法。
掌握了Fragment与Activity间的通信后,Fragment与Fragment间的通信就变得简单了。Fragment之间的通信,首先需要在Fragment中获取Activity的实例对象,然后通过Activity的实例获取另一个Fragment的实例,这样就可以实现Fragment与Fragment的通信了。
12.6 案例-----这设置界面
1. activity_main.xml 布局
在activity_main.xml布局文件中添加两个FrameLayout,这两个FrameLayout将会被相应的Fragment替换掉。
<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="horizontal"
tools:context=".MainActivity" >
<FrameLayout
android:id="@+id/settinglist"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" >
</FrameLayout>
<FrameLayout
android:id="@+id/settingcontent"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3" >
</FrameLayout>
</LinearLayout>
2. 创建两个Fragment布局文件
由于需要实现一个Activity中展现两个Fragment,因此需要创建相应的Fragment的布局。用来展示设置图标的布局fragment_icons.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="vertical" >
<ListView
android:id="@+id/settingicon"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
接下来创建一个用于展示内容的布局fragment_settinglist.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:background="@drawable/bg_welcome"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_margin="10dp"
android:background="@android:color/white" >
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:textSize="18sp" />
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:background="@drawable/arrow_group_right" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="@android:color/white" >
<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:textSize="18sp" />
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:background="@drawable/arrow_group_right" />
</RelativeLayout>
</LinearLayout>
3. 创建ListView布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ImageView
android:id="@+id/settingicon_imgv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="10dp" />
</RelativeLayout>
4. 创建两个Fragment
SettingListFragment的代码如下:
@SuppressLint("NewApi")
public class SettingListFragment extends Fragment
{
private View view;
private TextView mTextView1;
private TextView mTextView2;
@Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// 将布局文件解析出来
view = inflater.inflate(R.layout.fragment_settinglist, container, false);
if (view != null)
{ // 如果view不为空
initView();
}
setText(((MainActivity) getActivity()).getSettingText()[0]);
return view;
}
public void initView()
{
mTextView1 = (TextView) view.findViewById(R.id.tv);
mTextView2 = (TextView) view.findViewById(R.id.tv1);
}
public void setText(String[] text)
{
mTextView1.setText(text[0]);
mTextView2.setText(text[1]);
}
}
还需要定义一个Fragment用来展示设置图标,在这里创建一个SettingiconFragment继承自Fragment,由于需要在该类中实现用ListView展示设置图片,并且点击图标时还需要改变SettingListFragment中的文字,因此需要用到Fragment与Fragment的通信。使用getActivity()获取Activity的实例对象,并通过该实例对象获取到FragmentManager,然后调用findFragmentById()方法获取到相应的Fragment对象,从而实现了Fragment与Fragment间的通信
@SuppressLint("NewApi")
public class SettingiconFragment extends Fragment
{
private View view;
private int[] settingicon;
private String[][] settingText;
private ListView mListView;
@SuppressLint("NewApi")
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// 解析布局
view = inflater.inflate(R.layout.fragment_settingicon, container, false);
// 获取Acitivty实例对象
MainActivity activity = (MainActivity) getActivity();
// 获取Activity中的图标数组
settingicon = activity.getIcons();
// 获取Activity中的设置文字数组
settingText = activity.getSettingText();
if (view != null)
{ // 如果view不为空
initView();
}
// 为ListView设置条目监听
mListView.setOnItemClickListener(new OnItemClickListener()
{
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
// 通过Activity实例获取另一个Fragment实例
SettingListFragment listFragment = (SettingListFragment) ((MainActivity) getActivity())
.getFragmentManager().findFragmentById(R.id.settingcontent);
// 设置其他Fragment的文字
listFragment.setText(settingText[position]);
}
});
return view;
}
// 初始化控件的方法
private void initView()
{
mListView = (ListView) view.findViewById(R.id.settingicon);
if (settingicon != null)
{
mListView.setAdapter(new MyAdapter());
}
}
// 适配器
class MyAdapter extends BaseAdapter
{
@Override
public int getCount()
{
return settingicon.length;
}
@Override
public Object getItem(int position)
{
// TODO Auto-generated method stub
return settingicon[position];
}
@Override
public long getItemId(int position)
{
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
convertView = View.inflate(getActivity(), R.layout.item_list, null);
ImageView mNameTV = (ImageView) convertView.findViewById(R.id.settingicon_imgv);
mNameTV.setBackgroundResource(settingicon[position]);
return convertView;
}
}
}
5. MainActivity
public class MainActivity extends Activity
{
private FragmentTransaction beginTransaction;
// 设置文字
private String[][] settingText = { { "主题", "系统壁纸" }, { "云账户", "百度云账户" }, { "通知", "通知栏推送" }, { "移动数据", "便携式WIFI热点" },
{ "WLAN", "更多" }, { "蓝牙", "可被发现" }, { "天气", "温度" }, { "通话音量", "媒体音量" }, { "密码锁定", "定位服务" },
{ "语言", "输入法设置" }, { "设置快捷手势", "触摸反馈" }, { "设备名称", "存储" } };
// 设置图标
private int[] settingicons = { R.drawable.theme, R.drawable.clound, R.drawable.notifycation, R.drawable.internet,
R.drawable.wifi, R.drawable.bluetooth, R.drawable.wether, R.drawable.volume, R.drawable.gps,
R.drawable.language, R.drawable.gesture, R.drawable.info };
// 获取图标数组的方法
public int[] getIcons()
{
return settingicons;
}
// 获取设置文字的方法
public String[][] getSettingText()
{
return settingText;
}
@SuppressLint("NewApi")
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 创建Fragment
SettingListFragment fragment = new SettingListFragment();
// 创建Fragment
SettingiconFragment icFragment = new SettingiconFragment();
// 获取事务
beginTransaction = getFragmentManager().beginTransaction();
// 添加Fragment
beginTransaction.replace(R.id.settingcontent, fragment);
beginTransaction.replace(R.id.settinglist, icFragment);
// 提交事务
beginTransaction.commit();
}
}
6. 小技术:如何让选择项变色
android:background="?android:attr/activatedBackgroundIndicator"
//设置ListView为单选模式
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE)
//设置默认选中第一个
getListView().setItemChecked(0, true)