fragment详解

来源:互联网 发布:matlab向量组成矩阵 编辑:程序博客网 时间:2024/06/16 17:56

最近在做项目的时候发现用的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 {@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// TODO Auto-generated method stubLog.i("tedu", "fragmentoneon---createview");return inflater.inflate(R.layout.fragment_one, null);}@Overridepublic void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);Log.i("tedu", "fragmentone----create");}@Overridepublic void onStart() {// TODO Auto-generated method stubsuper.onStart();Log.i("tedu", "fragmentone----start");}@Overridepublic void onResume() {// TODO Auto-generated method stubsuper.onResume();Log.i("tedu", "fragmentone----resume");}@Overridepublic void onPause() {// TODO Auto-generated method stubsuper.onPause();Log.i("tedu", "fragmentone----pause");}@Overridepublic void onStop() {// TODO Auto-generated method stubsuper.onStop();Log.i("tedu", "fragmentone---stop");}@Overridepublic void onDestroyView() {// TODO Auto-generated method stubsuper.onDestroyView();Log.i("tedu", "fragmentone----destroyview");}@Overridepublic void onAttach(Activity activity) {// TODO Auto-generated method stubsuper.onAttach(activity);Log.i("tedu", "fragmentone------onattach");}@Overridepublic void onDetach() {// TODO Auto-generated method stubsuper.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>


设置了三个RadioButton按钮,查看其变化

public class MainActivity extends FragmentActivity {private FragmentOne fOne;private FragmentTwo fTwo;private FragmentThree fThree;private Fragment currentFragment;private RadioGroup rghome;@Overrideprotected 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是不是切换的fragmentif (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移除,等于销毁了。



   



















0 0
原创粉丝点击