最近在做项目的时候发现用的activity太多,感觉太过泛滥了,但是用fragment时却让人头疼,不知道从何做起,于是我决定要深趴一下fragment,希望能够让activity和fragment完美的结合在一起。
在这里我要先讲解一下fragment导包的问题,以及导包之后存在的使用的区别,看下图
初识不同版本的fragment
由于app包中的fragment是兼容android3.0(API 11)以上版本的,以下就不会再兼容,如果3.0以下的版本需要fragment怎么办呢?所以这时候v4包的用处就来了,虽然现在市场上3.0以下的版本不多了,但还是有一定的市场的。所以建议用v4包的。以下是V4包和app包的api的主要区别。
v4包:
在导包上-->import android.support.v4.app.FragmentManager
在管理器创建上-->FragmentManager fm=getSupportFragmentManager
在主类中-->Activity extends FragmentActivity
在版本支持上-->android3.0以下版本(3.0以上版本也支持)
app包:
在导包上-->import android.app.FragmentManager
在管理器创建上-->FragmentManager fm=getFragmentManager
在主类中-->Activity extends Activity(因为Activity本身就属于app包中-->android.app.Activity)
在版本支持上-->android3.0以上版本-->不可以在低版本中用!
各位兄台看懂了吗?这就是区别,有时候做个对比能够让自己加深记忆,更好的理解,好了,下面讲解对fragment的具体用法,来体会一下它的独特之处吧。
2.对fragment管理的重要的两个类
2.1 fragmentManager
本人比较喜欢寻根究底,看了源代码后,发现
FragmentManager主要针对的是已经嵌套在activity中的单个fragment对象的操作
FragmentManager执行的如下操作------>主要是用来获取已经存在的fragment,否则在代码中需新建
1.通过findFragmentById()(用于在activity layout中提供一个UI的fragment)或findFragmentByTag()(使用于有或没有UI的fragment)获取activity中存在的fragment---->tag标签经常在java代码中使用,如convertView中的setTag()和getTag()
(如在布局中添加<fragment/>标签,必须为其指定Id或Tag,作为其唯一标识符)
2.将fragment从后台堆栈中弹出(相当于位于顶部的fragment被back后弹出内存),使用popBackStack()-->模拟用户按下back指令
3.使用addOnBackStackChangeListener()注册一个监听后台堆栈变化的监听器listener
以上标明蓝色是FragmentManager最重要的几个方法。
2.2 fragmentTransaction
FragmentTransaction主要针对的是对fragment进行添加,移除,替换操作。看源码
不用看都懂了吧,但是有一点很重要,相对应FragmentManager来说,它没有给出条件,只是提供了一系列的对Fragment操作的API.用处可不小哦。
FragmentTransaction提供的重要API如下
显示:add() replace() show() attach()
隐藏:remove() hide() detach()
3.fragment和activity之间的联系
Fragment:获取它所在的Activity--->可通过getActivity获得,在启动另一个activity中常用,如:
Intent intent=new Intent(getActivity, AnotherActivity);---->需用activity启动另一个activity
startActivity(intent);(但是建议跳转操作应该交给activity来管理,否则就失去了fragment的使用机制了)
Activity:获取它包含的Fragment--->调用Activity关联的FragmentManager的findFragmentById(int id)或findFragmentByTag(String tag)方法获得
4.fragment使用方法
fragment的产生主要是由于在大屏幕或电视等设备上使用app时,由于大屏幕设备空间大,显示一个activity时会浪费很多控件,造成视觉感官不好,因此fragment就应运而生,为了适应大屏幕设备显示activity的问题,fragment有效弥补了剩余空间的浪费,使得在一个大屏幕设备中能够很好的显示两个界面,fragment就像是activity中的一个控件,因为它能够像其他控件一样嵌套在activity中,控制其UI大小,和其他控件没太大区别,又像是一个完整的activity,因为它和activity一样有自己的生命周期,且能够对界面中的事件做出响应。而fragment的生命周期却和activity绑定在一起,跟着activity变化着。
底部导航栏制作(一) :新建一个项目fragmentOne,然后在layout下建立一个xml布局文件fragment_one:
<?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="match_parent" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="我是第一个fragment"
android:textSize="20sp"
/>
</RelativeLayout>
这个文件只有一个textView.现在我们再新建一个fragment_two,复制一份fragment_one再稍作修改即可:
<span style="font-size:12px;"><?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="match_parent" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="我是fragmentTwo"
android:textSize="20sp"
/>
</RelativeLayout></span>
再在fragmentOne写入以下代码,我加入了fragment的完整生命周期方法,等下通过操作看其如何变化
public class FragmentOne extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
Log.i("tedu", "fragmentoneon---createview");
return inflater.inflate(R.layout.fragment_one, null);
}
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
Log.i("tedu", "fragmentone----create");
}
@Override
public void onStart() {
// TODO Auto-generated method stub
super.onStart();
Log.i("tedu", "fragmentone----start");
}
@Override
public void onResume() {
// TODO Auto-generated method stub
super.onResume();
Log.i("tedu", "fragmentone----resume");
}
@Override
public void onPause() {
// TODO Auto-generated method stub
super.onPause();
Log.i("tedu", "fragmentone----pause");
}
@Override
public void onStop() {
// TODO Auto-generated method stub
super.onStop();
Log.i("tedu", "fragmentone---stop");
}
@Override
public void onDestroyView() {
// TODO Auto-generated method stub
super.onDestroyView();
Log.i("tedu", "fragmentone----destroyview");
}
@Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
Log.i("tedu", "fragmentone------onattach");
}
@Override
public void onDetach() {
// TODO Auto-generated method stub
super.onDetach();
Log.i("tedu", "fragmentoneDetach");
}
}
fragmentTwo和fragmentThree就不写
了,跟以上的代码一样,稍作修改即可
现在在MainActivity的xml布局文件中插入如下代码:
<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"
tools:context=".MainActivity" >
<FrameLayout
android:id="@+id/fl_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/rg_bottom" />
<RadioGroup
android:id="@+id/rg_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal" >
<RadioButton
android:id="@+id/rb_one"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_margin="3dp"
android:layout_weight="1"
android:background="#999999"
android:button="@null"
android:gravity="center"
android:text="one" />
<RadioButton
android:id="@+id/rb_two"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_margin="3dp"
android:layout_weight="1"
android:background="#999999"
android:button="@null"
android:gravity="center"
android:text="two" />
<RadioButton
android:id="@+id/rb_three"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_margin="3dp"
android:layout_weight="1"
android:background="#999999"
android:button="@null"
android:gravity="center"
android:text="three" />
</RadioGroup>
</RelativeLayout>
public class MainActivity extends FragmentActivity {
private FragmentOne fOne;
private FragmentTwo fTwo;
private FragmentThree fThree;
private Fragment currentFragment;
private RadioGroup rghome;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rghome=(RadioGroup) findViewById(R.id.rg_bottom);
setDefaultFragment();
setListeners();
}
private void setDefaultFragment() {
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
fOne = new FragmentOne();
transaction.add(R.id.fl_content, fOne);
currentFragment = fOne;
transaction.commit();
}
public void setListeners() {
rghome.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
public void onCheckedChanged(RadioGroup arg0, int checkedId) {
// 开启Fragment事务
switch (checkedId) {
case R.id.rb_one:
if(fOne==null) {
fOne = new FragmentOne();
}
switchFragment(fOne);
break;
case R.id.rb_two:
if(fTwo==null) {
fTwo = new FragmentTwo();
}
switchFragment(fTwo);
break;
case R.id.rb_three:
if(fThree==null) {
fThree = new FragmentThree();
}
switchFragment(fThree);
break;
}
}
});
}
private void switchFragment(Fragment fragment) {
// 判断当前显示的Fragment是不是切换的fragment
if (currentFragment != fragment) {
// 判断切换的fragment是否已经添加过
if (!fragment.isAdded()) {
// 如果没有添加,先把当前的fragment隐藏,把切换的fragment加上
getSupportFragmentManager().beginTransaction()
.hide(currentFragment).add(R.id.fl_content, fragment)
.commit();
} else {
// 如果已经添加过,则先把当前的fragment隐藏,把切换的fragment隐藏起来
getSupportFragmentManager().beginTransaction()
.hide(currentFragment).show(fragment).commit();
}
currentFragment = fragment;
}
}
观察第16行,我在这里添加了一个setDefaultFragment的方法,我将fragmentOne设置成默认的布局,并将其传入当前的对象currentFragment,在oncreate的时候首次进行加载,当对一个fragment进行点击的时候,我将对应的fragment传入第52行的switchFragment方法中,在55行的switchFragment方法中,设置了一个fragment参数,方便将传入的值向上转型为fragment,因为传入的值是不确定的,这里利用了多态。在switchFragment方法中,会先判断当前的传入的fragment是否是传入的fragment,如果不是,会再判断其是否已经添加进transaction事务中,调用isAdd()即可,如果没有则创建一个transaction,将当前的fragment隐藏再将传入的fragment添加起来,如果添加了,则直接隐藏当前fragment,再显示传入的fragment即可。
看下图演示
当对每个fragment第一次点击的时候,会执行每个fragment的生命周期方法,当第二次点击的时候就不再发生变化,因为fragment布局已经都加入到了mainActivity的布局中,只需 show()和hide()方法即可。在程序运行中,其实add方法只运行一次,只有当所有的fragment加入到了transaction之后,以后执行的就只有transaction的hide()和show()方法,因为fragment就如同一个个布局叠加在一起,相当于其他布局中的visible属性。这是对fragment的高效率的用法。、
底部导航栏制作(二):第二种方式使用transaction的replace方法,而不是add()
只需改变以下代码即可
private void setDefaultFragment() {
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
fOne = new FragmentOne();
transaction.replace(R.id.fl_content, fOne);
transaction.commit();
}
public void setListeners() {
rghome.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
public void onCheckedChanged(RadioGroup arg0, int checkedId) {
FragmentManager ff=getSupportFragmentManager();
FragmentTransaction transaction2 =ff.beginTransaction();
switch (checkedId) {
case R.id.rb_one:
if(fOne==null) {
fOne = new FragmentOne();
}
transaction2.replace(R.id.fl_content, fOne);
transaction2.commit();
break;
case R.id.rb_two:
if(fTwo==null) {
fTwo = new FragmentTwo();
}
transaction2.replace(R.id.fl_content, fTwo);
transaction2.commit();
break;
case R.id.rb_three:
if(fThree==null) {
fThree = new FragmentThree();
}
transaction2.replace(R.id.fl_content, fThree);
transaction2.commit();
break;
}
}
});
调用了transaction事务的replace方法,这样看起来显得更加简单,但是会有一个问题,看如下演示
如右图,当点击two的时候,第一个fragmentone会先执行pause-stop-destroyview销毁,再创建fragmenttwo,当点击three时,fragmenttwo即执行同样的生命周期方法销毁view再点击时又会再重新创建,即repalce方法相当于执行了,remove()和add()方法。所以相对来说,用add对程序更加优化。
对于fragment调用add的时候出现的叠加问题的解决
1.在fragment的布局文件中将fragment根节点的背景颜色改为白色(不推荐)--->这个简单你们可以自行尝试
2.使用transaction的show()和hide()的方法--->这些方法相当于其他控件的visible,看源码
Hides an existing fragment. This is only relevant for fragments whose views have been added to a container, as this will cause the view to be hidden.
隐藏的是已经加入containaer容器的fragments,而fragment其实还在容器中。相对于remove( )
Remove an existing fragment. If it was added to a container, its view is also removed from that container.
remove的话是从container容器中将fragment移除,等于销毁了。