fragment详解

1 篇文章 0 订阅
1 篇文章 0 订阅

最近在做项目的时候发现用的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>


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

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移除,等于销毁了。



   



















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值