前言
一、fragment简介
简单谈一下我对fragment的理解,由于手机屏幕较小,一个Activity能放的东西是有限的,比如一个列表一般就占满了整个屏幕,再查看详情则只能通过跳转到新的Activity去实现。而平板电脑的出现解决了屏幕小的问题,那么需求也就暴露了出来,平板的宽屏幕可以展示的东西更多了,而且在一个Activity放置过多的组件也不易于管理,所以fragment出现了,它可以代表Activity的子模块,并且有自己独立的生命周期。
在web开发中有一种经典的页面模型,上左右分三块,一般上面占满屏幕宽度,放Logo之类的,左边是树状菜单,而右边是用于展示菜单的内容,点击菜单、刷新右边的页面,类似于这样:
在web中实现这种布局很简单,我们都知道使用框架,比如frameset或者iframe,分别写3个子html页面用于显示上面的3块内容,最后通过<iframe>或者<frameset>标签把它们放在一个容器html页面中即可,通过点击左侧菜单的超链接来改变右边的内容页面。
其实Android也是类似的,如果要在平板电脑上实现上面的效果,那就需要使用fragment来完成了,上面可能是一个titlebar或者是一个放TextView的fragment,左边可能是一个放ListView的fragment,而右边也就是放内容的fragment,没错,fragment相当于frameset或iframe,它只是一个容器。还有一点很重要,就是fragment必须被“嵌入”Activity中使用,并且它有自己的生命周期和响应事件,但fragment的生命周期直接被其所属的activity的生命周期控制。
二、创建fragment
与创建Activity类似,创建一个fragment首先要继承android.app.Fragment,并且要重写onCreatView方法,为什么要重写这个方法,因为上面说了fragment只是容器,那么只有装了东西这个容器才完整,这个所谓的“东西”也就是各种各样的组件了,onCreatView方法的返回值是View,这个View也就是fragment所要显示的View,可以参照官方文档:
这句话说的很清楚了,为fragment提供一个布局的话,你必须实现onCreatView()这个回调方法。
清楚了这两点,那么我们写一个简单的自定义Fragment类。
package com.xw.fragment;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class MyFragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(
com.example.fragmentdemo.R.layout.fragment1,null);
return view;
}
}
可以看到是通过LayoutInflater的inflate方法去加载布局的,而这个布局也就是这个fragment所要显示的布局。关于inflate方法初学者可能不是很清楚,其实我本人也不是很清楚,以前实例化布局我都是用setContentView这个方法,那这个inflate方法和setContentView方法又有什么区别和联系呢?
从表面上看:
setContentView方法没有返回值,并且是Activity的一个实例方法。
那么既然没有返回值,并且在当前的Activity的中被调用,那么可想而知,那么这个方法应该就是为当前的Activity加载布局了,而fragment里面并没有setContentView方法,所以开发fragment的时候我们没有可选择性,目前我们只需要知道inflate方法就是用来实例化布局的就可以了,关于具体的细节和原理我们在后续的blog中再做记录。
对了,为了看到后面的简单效果再show一下fragment1.xml,很简单,只是一个红色的TextView:
<?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"
android:padding="10dp"
>
<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#FF0000"
android:text="fragment one"
android:gravity="center"
/>
</LinearLayout>
三、在Activity中显示Fragment
上面我们已经知道了如何创建一个fragment,但是fragment终究还是要被“嵌入”到Activity中才能使用的,那么我们就具体看一下如何在Activity中引用fragment,有两种实现方式:
1.在Activity中的布局文件里引入<fragment></fragment>标签,并通过android:name=""属性去指定Fragment的类名。
2.在Activity中定义一个容器控件,然后在Activity中用通过FragmentManager的add方法将Fragment添加到指定的容器控件中去。
下面就分别演示一下这两种方法怎么写,首先是第一种:
Step 1 创建FragmentOne
a.创建Fragment类
package com.example.fragmentbasedemo;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class FragmentOne extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.fragment_one, null);
return view;
}
@Override
public void onPause() {
// TODO Auto-generated method stub
super.onPause();
}
}<strong style="color: rgb(51, 102, 255);">
</strong>
b.创建Fragment布局
<?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="#cccccc"
android:gravity="center"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="这里是fragment one!"
android:textColor="#ff0000"
android:textSize="30sp" />
</LinearLayout>
Step 2 在Activity的配置文件中引入Fragment
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.fragmentbasedemo.MainActivity" >
<fragment
android:id="@+id/fragment_one"
android:name="com.example.fragmentbasedemo.FragmentOne"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
运行之后就可以看到已嵌入Activity中的FragmentOne了,很简单吧:
第二种方法是通过FragmentTransaction的add()方法去添加一个Fragment:
package com.example.fragmentbasedemo;
import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
private FragmentManager fm;
private FragmentTransaction ftx;
private FragmentOne fOne;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate");
setContentView(R.layout.activity_main);
fm = getFragmentManager();
fOne = new FragmentOne();
ftx = fm.beginTransaction();
ftx.add(R.id.fl_container, fOne, "fragment_one");
ftx.commit();
}
}
在Activity的布局文件中只需要放一个容器即可:
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.fragmentbasedemo.MainActivity" >
<FrameLayout
android:id="@+id/fl_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
</RelativeLayout>
效果和上面的第一种方法完全一样,这就是Fragment在实际应用中的最简单的一个例子。
四、常用方法和API
在fragment中获取它所在的Activity:Activity activity=getActivity();
在Activity中获取fragment:getFragmentManager().findFragmentById()或findFragmentByTag();
在上面可以看到getFragmentManager()方法,它返回的是一个FragmentManager对象:
FragmentManager fragmentManager=getFragmentManager();
如果要添加、删除或替换fragment,那么就需要FragmentTransaction对象的一系列方法来处理了:
FragmentTransaction ftx=fragmentManager.beginTransaction();
向Activity中添加一个fragment:
ftx.add(Fragment fragment,String tag);
从Activity中移除一个fragment:
ftx.remove(Fragment fragment);
使用一个fragment替换当前的fragment:
ftx.replace(int containerViewId,Fragment fragment);
隐藏当前的fragment:
ftx.hide(Fragment fragment);
显示之前隐藏的fragment:
ftx.show(Fragment fragment);
FragmentTransaction与DatabaseTransaction类似,后者一般代表对底层数据库的多个更新操作,而前者表示Activity对Fragment执行的多个改变操作,相同的是,最后都要调用commit()方法进行提交操作。
五、Tab导航的例子
其实文字的东西也没啥写的,无非就是那点东西,下面通过一个实例来练习一下fragment的具体用法,这个例子就是经常会用到的Tab导航。
Layout代码(activity_main.xml):
<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:background="@drawable/bg_main_day"
android:orientation="vertical"
tools:context=".MainActivity" >
<RadioGroup
android:id="@+id/rg_select_main"
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center_vertical"
android:orientation="horizontal" >
<RadioButton
android:id="@+id/rb1"
style="@style/SelectTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:background="@drawable/iv_selectbar_selector"
android:button="@null"
android:gravity="center"
android:text="当前推荐" />
<RadioButton
android:id="@+id/rb2"
style="@style/SelectTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:background="@drawable/iv_selectbar_selector"
android:button="@null"
android:gravity="center"
android:text="景点" />
<RadioButton
android:id="@+id/rb3"
style="@style/SelectTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:background="@drawable/iv_selectbar_selector"
android:button="@null"
android:gravity="center"
android:text="美食" />
<RadioButton
android:id="@+id/rb4"
style="@style/SelectTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:background="@drawable/iv_selectbar_selector"
android:button="@null"
android:gravity="center"
android:text="文化" />
<RadioButton
android:id="@+id/rb5"
style="@style/SelectTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:background="@drawable/iv_selectbar_selector"
android:button="@null"
android:gravity="center"
android:text="娱乐" />
</RadioGroup>
<FrameLayout
android:id="@+id/main_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
</LinearLayout>
package com.xw.activity;
import android.app.Activity;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;
import com.example.fragmenttabdemo.R;
import com.xw.fragment.FragmentFive;
import com.xw.fragment.FragmentFour;
import com.xw.fragment.FragmentOne;
import com.xw.fragment.FragmentThree;
import com.xw.fragment.FragmentTwo;
public class MainActivity extends Activity implements OnCheckedChangeListener {
private RadioGroup rg;
private RadioButton rb;
private FragmentOne fone;
private FragmentTwo ftwo;
private FragmentThree fthree;
private FragmentFour ffour;
private FragmentFive ffive;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupView();
addListener();
}
private void setupView() {
rg = (RadioGroup) findViewById(R.id.rg_select_main);
rb = (RadioButton) findViewById(R.id.rb1);
// 实例化fragment
fone = new FragmentOne();
ftwo = new FragmentTwo();
fthree = new FragmentThree();
ffour = new FragmentFour();
ffive = new FragmentFive();
// 初始化activity显示的fragment
FragmentTransaction ftx = getFragmentManager().beginTransaction();
ftx.add(R.id.main_frame, fone);
ftx.commit();
rb.setChecked(true); // 初始化选中当前推荐
}
private void addListener() {
rg.setOnCheckedChangeListener(this);
}
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
FragmentTransaction ftx = getFragmentManager().beginTransaction();
switch (checkedId) {
case R.id.rb1:
ftx.replace(R.id.main_frame, fone);
break;
case R.id.rb2:
ftx.replace(R.id.main_frame, ftwo);
break;
case R.id.rb3:
ftx.replace(R.id.main_frame, fthree);
break;
case R.id.rb4:
ftx.replace(R.id.main_frame, ffour);
break;
case R.id.rb5:
ftx.replace(R.id.main_frame, ffive);
break;
default:
break;
}
ftx.commit();
}
}
Fragment的代码就不贴了,都很简单,和上面基本一样,下面看看运行效果:
很简单吧!通过点击不同的radiobutton,根据id去判断并替换containerView中的组件,这应该是fragment最简单的一种应用,也许还有潜在的bug和不合理的地方,欢迎各位批评指正。
六、总结
本篇记录了我认识fragment的过程,也许只是一个最简单的demo,但对于我来说也算是一点一滴的进步,关于fragment的生命周期、back stack、与activity之间的通信、fragment管理与fragment事务等等暂且还没有去详细了解,只是尽快熟悉了API和用法,然后立刻上手敲一个小demo,不知道这种学习方法对不对,但是我更倾向于这样,先会用,再研究原理,因为做出东西就会有成就感,为继续学习提供了动力。一般菜鸟废话都比较多,呵呵。我还是会继续努力的,要抓紧了,在Android上不能耽误太多时间,要学的东西还有很多很多,加油。