Fragment生命周期
Fragment必须是依存与Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期。官网这张图很好的说明了两者生命周期的关系:
- onAttach(Activity)
当Fragment与Activity发生关联时调用。 - onCreateView(LayoutInflater, ViewGroup,Bundle)
创建该Fragment的视图 - onActivityCreated(Bundle)
当Activity的onCreate方法返回时调用 - onDestoryView()
与onCreateView想对应,当该Fragment的视图被移除时调用 - onDetach()
与onAttach相对应,当Fragment与Activity关联被取消时调用。
Fragment使用
静态使用
把Fragment当成普通的控件,直接写在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" >
<fragment
android:id="@+id/id_fragment_title"
android:name="com.demo.wong.TitleFragment"
android:layout_width="match_parent"
android:layout_height="45dp" />
<fragment
android:layout_below="@id/id_fragment_title"
android:id="@+id/id_fragment_content"
android:name="com.demo.wong..ContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
动态使用
为了动态使用Fragment,我们修改一下Actvity的布局文件,中间使用一个FrameLayout来放我们的动态添加的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" >
<fragment
android:id="@+id/id_fragment_title"
android:name="com.demo.wong.TitleFragment"
android:layout_width="match_parent"
android:layout_height="45dp" />
<FrameLayout
android:id="@+id/id_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/id_fragment_title" />
</RelativeLayout>
@Override
public void onClick(View v)
{
FragmentManager fm = getFragmentManager();
// 开启Fragment事务
FragmentTransaction transaction = fm.beginTransaction();
switch (v.getId())
{
case R.id.tab_bottom_weixin:
if (mWeixin == null)
{
mWeixin = new ContentFragment();
}
// 使用当前Fragment的布局替代id_content的控件
transaction.replace(R.id.id_content, mWeixin);
break;
case R.id.tab_bottom_friend:
if (mFriend == null)
{
mFriend = new FriendFragment();
}
transaction.replace(R.id.id_content, mFriend);
break;
}
// transaction.addToBackStack();
// 事务提交
transaction.commit();
}
Fragment常用的三个类:
1、android.app.Fragment 主要用于定义Fragment
2、android.app.FragmentManager 主要用于在Activity中操作Fragment
3、android.app.FragmentTransaction 保证一些列Fragment操作的原子性.
- 获取FragmentManage的方式:getFragmentManager()
- 主要的操作都是FragmentTransaction的方法:
FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务
transaction.add() //往Activity添加一个Fragment
transaction.remove() //从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈,这个Fragment实例将会被销毁。
transaction.replace() //使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体~
transaction.hide() //隐藏当前的Fragment,仅仅是设为不可见,并不会销毁
transaction.show() //显示之前隐藏的Fragment
transaction.detach() //会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。
remove和detach有一点细微的区别,在不考虑回退栈的情况下,remove会销毁整个Fragment实例,而detach则只是销毁其视图结构,实例并不会被销毁。
注意:使用Fragment的过程中可能会遇到Activity状态不一致:State loss这样的错误。主要是因为:commit方法一定要在Activity.onSaveInstance()之前调用。
管理Fragment回退栈
类似与Android系统为Activity维护一个任务栈,我们也可以通过Activity维护一个回退栈来保存每次Fragment事务发生的变化。如果将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。
把一个Fragment事务添加到回退栈很简单,只需在事务提交前transaction.addToBackStack(String)就好。
假如从FragmentOne跳到FragmentTwo时,addToBackStack,那么FragmentOne则会加入回退栈,当从FragmentTwo返回时会先回到FragmentOne
FragmentTwo fTwo = new FragmentTwo();
FragmentManager fm = getFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
transaction.replace(R.id.id_content, fTwo, "TWO");
transaction.addToBackStack(null); //添加到当前回退栈
transaction.commit();
Fragment如何与Activity交互
因为所有的Fragment都是依附于Activity的,所以通信起来并不复杂,大概归纳为:
Activity调用Fragment
-
如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法
-
如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作。
Fragment调用Activity
- 在Fragment中可以通过getActivity()得到当前绑定的Activity的实例,然后进行操作。
- 如果在Fragment中需要Context,可以通过调用getActivity(),如果该Context需要在Activity被销毁后还存在,则使用getActivity().getApplicationContext()。
由于要考虑Fragment的复用性,必须降低Fragment与Activity的耦合。Fragment更不应该直接操作别的Fragment,毕竟Fragment操作应该由它的管理者Activity来决定。
假如我们需要从一个fragment跳到另一个fragment,我们可以通过接口的方式来实现,通过activity去做跳转。
public class FragmentOne extends Fragment implements OnClickListener
{
private FTwoBtnClickListener fTwoBtnClickListener ;
public interface FTwoBtnClickListener
{
void onFTwoBtnClick();
}
//设置回调接口
public void setfTwoBtnClickListener(FTwoBtnClickListener fTwoBtnClickListener)
{
this.fTwoBtnClickListener = fTwoBtnClickListener;
}
@Override
public void onClick(View v)
{
if(fTwoBtnClickListener != null)
{
fTwoBtnClickListener.onFTwoBtnClick();
}
}
}
public class MainActivity extends Activity implements FTwoBtnClickListener
{
/**
* FragmentOne 按钮点击时的回调
*/
@Override
public void onFTwoBtnClick()
{
if (mFTwo == null)
{
mFTwo = new FragmentTwo();
}
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.hide(mFOne);
tx.add(R.id.id_content, mFTwo, "Two");
tx.addToBackStack(null);
tx.commit();
}
}
Fragment使用技巧
- 非空判断
public class MainActivity extends FragmentActivity
{
private ContentFragment mContentFragment ;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
mContentFragment = (ContentFragment) fm.findFragmentById(R.id.id_fragment_container);
if(mContentFragment == null )
{
mContentFragment = new ContentFragment();
fm.beginTransaction().add(R.id.id_fragment_container,mContentFragment).commit();
}
}
}
为什么要非空判断:
当Activity因为配置发生改变(屏幕旋转)或者内存不足被系统杀死,造成重新创建时,我们的fragment会被保存下来,但是会创建新的FragmentManager,新的FragmentManager会首先会去获取保存下来的fragment队列,重建fragment队列,从而恢复之前的状态。
- 使用arguments来创建Fragment
public class ContentFragment extends Fragment
{
private String mArgument;
public static final String ARGUMENT = "argument";
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
if (bundle != null)
mArgument = bundle.getString(ARGUMENT);
}
/**
* 传入需要的参数,设置给arguments
* @param argument
* @return
*/
public static ContentFragment newInstance(String argument)
{
Bundle bundle = new Bundle();
bundle.putString(ARGUMENT, argument);
ContentFragment contentFragment = new ContentFragment();
contentFragment.setArguments(bundle); //传参
return contentFragment;
}
这种方式主要是为了Fragment和Activity间的解耦。
- Fragment的startActivityForResult
考虑这种需求:从一个FragmentOne跳到另一个FragmentTwo,需要从FragmentTwo返回FragmentOne带回参数。
在Fragment中存在startActivityForResult()以及onActivityResult()方法,但是呢,没有setResult()方法,用于设置返回的intent。这样我们就需要通过调用getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent)。也说明了一个问题:fragment能够从Activity中接收返回结果,但是其自设无法产生返回结果,只有Activity拥有返回结果。
public class ListTitleFragment extends ListFragment
{
public static final int REQUEST_DETAIL = 0x110;
@Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
setListAdapter(mAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, mTitles));
}
@Override
public void onListItemClick(ListView l, View v, int position, long id)
{
mCurrentPos = position ;
Intent intent = new Intent(getActivity(),ContentActivity.class);
intent.putExtra(ContentFragment.ARGUMENT, mTitles.get(position));
startActivityForResult(intent, REQUEST_DETAIL); //注意
}
//接收数据
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
Log.e("TAG", "onActivityResult");
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == REQUEST_DETAIL)
{
mTitles.set(mCurrentPos, mTitles.get(mCurrentPos)+" -- "+data.getStringExtra(ContentFragment.RESPONSE));
mAdapter.notifyDataSetChanged();
}
}
}
public class ContentFragment extends Fragment
{
private String mArgument;
public static final String ARGUMENT = "argument";
public static final String RESPONSE = "response";
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
if (bundle != null)
{
mArgument = bundle.getString(ARGUMENT);
Intent intent = new Intent();
intent.putExtra(RESPONSE, "good");
getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent); //发送数据
}
}
}
Fragment中add与replace的区别
- add不会重新初始化fragment,replace每次都会。所以如果在fragment生命周期内获取获取数据,使用replace会重复获取;
- 添加相同的fragment时,replace不会有任何变化,add会报IllegalStateException异常;
- replace先remove掉相同id的所有fragment,然后在add当前的这个fragment,而add是覆盖前一个fragment。所以如果使用add一般会伴随hide()和show(),避免布局重叠;
- 使用add,如果应用放在后台,或以其他方式被系统销毁,再打开时,hide()中引用的fragment会销毁,所以依然会出现布局重叠bug,可以使用replace或使用add时,添加一个tag参数;(add前先判断是否已存在)
getFragmentManager、getSupportFragmentManager 、getChildFragmentManager之间的区别?
- getFragmentManager()所得到的是所在fragment 的父容器的管理器,
getChildFragmentManager()所得到的是在fragment 里面子容器的管理器,
如果是fragment嵌套fragment,那么就需要利用getChildFragmentManager(); - 因为Fragment是3.0 Android系统API版本才出现的组件,所以3.0以上系统可以直接调用getFragmentManager()来获取FragmentManager()对象,而3.0以下则需要调用getSupportFragmentManager() 来间接获取;
FragmentPagerAdapter与FragmentStatePagerAdapter的区别与使用场景
- 相同点 :二者都继承PagerAdapter
- 不同点 :FragmentPagerAdapter的每个Fragment会持久的保存在FragmentManager中,只要用户可以返回到页面中,它都不会被销毁。因此适用于那些数据相对静态的页,Fragment数量也比较少的那种;
FragmentStatePagerAdapter只保留当前页面,当页面不可见时,该Fragment就会被消除,释放其资源。因此适用于那些数据动态性较大、占用内存较多,多Fragment的情况;
参考链接:
https://blog.csdn.net/lmj623565791/article/details/37970961