09、Android--Fragment

Fragment

fragment的出现是为了同时适应手机和平板,可以将其看做Activity的组成部分,甚至Activity界面完全由不同的Fragment组成,它拥有自己的生命周期和

接收、处理用户的事件,更为重要的是,可以动态的添加、替换和移除某个Fragment。

fragment是在 3.0 后才有的,要使用 Fragment SDK 版本需要 大于 11; 由于Fragment的广泛使用,google 后期在V4包中提供了 Fragment的支持,在实际

开发过程中,V4 包中Fragment 得到广泛使用 。

// 3.0之前,通用
android.support.v4.app.Fragment
// 3.0之后
android.app.Fragment

创建一个fragment的方法如下所示:

public class MyFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater,
            @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        /**Inflate the layout for this fragment*/
        return inflater.inflate(R.layout.left_fragment, container, false);
    }
}  

注意:inflate()方法第三个参数是ViewGroup布局,必须为false。如果在Activity中使用V4包下的fragment的话,Activity必须继承FragmentActivity。

生命周期

Fragment必须是依存于Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期。

除了onCreateView()方法外,其他方法都必须调用父类的实现super.xxx。

名称 描述
onAttach(Activity) 当Fragment与Activity发生关联时调用。
onCreateView(LayoutInflater, ViewGroup,Bundle) 创建该Fragment的视图
onActivityCreated(Bundle) 当Activity的onCreate方法返回时调用
onDestoryView()与onCreateView相对应 当该Fragment的视图被移除时调用
onDetach()与onAttach相对应 当Fragment与Activity关联被取消时调用

基本生命周期

Fragment在使用过程中,会出现如下几种生命周期的情况:

1、当一个fragment被创建的时候:
onAttach() -> onCreate() -> onCreateView() -> onActivityCreated()
2、当这个fragment对用户可见的时候,它会经历以下状态。
onStart() -> onResume()
3、当这个fragment进入“后台模式”的时候,它会经历以下状态
onPause() -> onStop()
4、当这个fragment被销毁了(或者持有它的activity被销毁)
onPause() -> onStop() -> onDestroyView() -> onDestroy() -> onDetach()
5、就像Activity一样,在以下的状态中,可以使用Bundle对象保存一个fragment的对象
onCreate() -> onCreateView() -> onActivityCreated()

其他生命周期

Fragment在其他场景中生命周期的调用如下:

屏幕灭掉,也就是锁屏:onPause() -> onSaveInstanceState() -> onStop()
屏幕解锁:onStart() -> onResume()
切换到其他Fragment:onPause() -> onStop() -> onDestroyView()
切换回本身的Fragment:onCreateView() -> onActivityCreated() -> onStart() -> onResume()
回到桌面:onPause() -> onSaveInstanceState() -> onStop()
回到应用:onStart() -> onResume()
退出应用:onPause() -> onStop() -> onDestroyView() -> onDestroy() -> onDetach()

基本使用

静态加载

1、在activity的layout.xml文件中声明fragment

<fragment
    android:id="@+id/fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    class="com.legend.fragment.CustomFragment" />

配置过程中,Activity的布局文件内必须指定fragment的class或android:name为对应的Fragment的全包名类名。(通用性和灵活性不强)

android:name="com.legend.fragment.CustomFragment" 

2、创建Fragment,Android 3.0之前需要导入的是v4包中的fragment:

public class CustomFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_custom, container, false);
    }
}  

动态加载

动态加载可以非常灵活的对Activity的视图进行增加和移除等操作,开发中推荐使用动态加载。

1、在布局文件中使用FrameLayout创建占位符,然后在代码中通过动态的方式添加。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <FrameLayout
        android:id="@+id/fl_fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

2、创建Fragment,Android 3.0之前需要导入的是v4包中的fragment:

public class CustomFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        /** Inflate the layout for this fragment */
        return inflater.inflate(R.layout.fragment_custom, container, false);
    }
}  

3、通过Fragment事务管理来动态加载Fragment:

// 获取FragmentManager
FragmentManager fragmentManager = getFragmentManager();
// 获取FragmentTransaction        
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// 获取CustomFragment
CustomFragment fragment = new CustomFragment();
// 将fragment添加到容器fl_fragment_container中
fragmentTransaction.add(R.id.fl_fragment_container, fragment);
fragmentTransaction.commit();

FragmentManager

在Android中使用FragmentManager来管理Fragment,FragmentManager的获取方式有如下几种情况:

getSupportFragmentManager():在Activity中使用Fragment的管理器,对所有Fragment进行管理。
getFragmentManager():与 getSupportFragmentManager()功能是一样的,只是是在Fragment中使用
getChildFragmentManager():在嵌套的Fragment中,内部的fragment创建,需要使用getChildFragmentManager()

FragmentManager常用的api如下所示:

名称 描述
getFragments() 可以获取所有创建时添加的所有Fragment,通常可以通过这个api来获取需要指定操作的fragment对象。
findFragmentByTag(String tag) 通过TAG获取指定的Fragment,该TAG在添加的时候设置。
popBackStack() 弹出栈顶fragment
popBackStack(String tag, int flags) tag可以为null或者相对应的tag。 flags有0和1(POP_BACK_STACK_INCLUSIVE)两种情况:
1、如果tag为null,flags为0时,弹出回退栈中最上层的那个fragment。
2、如果tag为null ,flags为1时,弹出回退栈中所有fragment。
3、如果tag不为null,那就会找到这个tag所对应的fragment:
(1)flags为0时,弹出该fragment以上的Fragment,
(2)flags为1时,弹出该fragment(包括该fragment)以上的fragment。

popBackStackImmediate相关的方法与上面逻辑是一样的与上面不同的是,在调用的时候会立即执行弹出。

FragmentTransaction

在Android中通过FragmentTransaction实现在Activity运行时可动态地加入、移除、交换等操作。

针对在一个Activity中的某个Layout中切换Fragment,无非两种方法:

使用replace方法创建新实例,销毁旧的,无法复用.
使用hide和show方法,最终是让Fragment的setVisibility(true还是false),不会调用生命周期,可复> 用,会调用onHiddenChanged.

FragmentTransaction常用的api如下所示:

名称 描述
add(id, fragment) 增加framgent到队列中,并显示该fragment到指定布局中。
remove(fragment) 销毁队列中指定的fragment。
replace(id, fragment) 先检查队列中是否已经存在,不存在就会进入队列并把其他fragment清出队列,最后显示该fragment到指定布局中。
show(fragment) 显示队列中的指定framgent,当队列中存在该fragment时并被调用过hide(fragment)时,回调onHiddenChange(boolean)。
hide(fragment) 隐藏队列中指定的fragment,当队列中存在该fragment时,回调onHiddenChange(boolen)。
detach() 会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。
attach() 重建view视图,附加到UI上并显示。
addToBackStack() 当移除或替换一个片段并向返回栈添加事务时,系统会停止(而非销毁)移除的片段。 如果用户执行回退操作进行片段恢复,该片段将重新启动。
如果是替换一个片段,这个替换片段相当于add() 移除或替换一个片段并向返回栈添加事务时,被移除片段将被detach() 执行回退操作进行片段恢复,
这个片段将被attach(),而上一个片段有两种情况:
1、如果是回退栈中还有这个片段那么将被detach()。
2、如果回退栈没有这个片段将被remove()。
commit() 提交本次事务,可在非主线程中被调用。主要用于多线程处理情况。在onSaveInstanceState之后提交会出现IllegalStateException,可以使用commitAllowingStateLoss代替。
commitAllowingStateLoss() 可能会丢掉FragmentManager的状态, 即onSaveInstanceState之后任何被添加或被移除的Fragments.
commitNow() 提交本次事务,只在主线程中被调用。 这时候addToBackStack(string)不可用。

触摸事件

fragment默认是没有提供onTouchEvent事件的,如果我们想在fragment中做手势识别则需要用到宿主Activity来相应事件,具体操作如下:

1、在Activity中创建内部接口,并对外提供注册touch事件的方法,由于可能存在多个fragment需要相应touch事件,所以将touch事件放入ArrayList中

/**实现监听触摸事的接口,让fragment去实现*/
private ArrayList<MyOnTouchListener> onTouchListeners = new ArrayList<MyOnTouchListener>(10);
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    for (MyOnTouchListener listener : onTouchListeners) {
        listener.onTouch(ev);
    }
    return super.dispatchTouchEvent(ev);
}
public void registerMyOnTouchListener(MyOnTouchListener myOnTouchListener) {
    onTouchListeners.add(myOnTouchListener);
}
public void unregisterMyOnTouchListener(MyOnTouchListener myOnTouchListener) {
    onTouchListeners.remove(myOnTouchListener);
}
public interface MyOnTouchListener {
    public boolean onTouch(MotionEvent ev);
}  

2、让需要响应touch事件的fragment来实现上方的activity中所提供的内部接口即可。

mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
     //手指在屏幕上滑动
     //e1,e2:手指的事件:手指第一次触摸屏幕触发->手指离开屏幕触发
     //vX vY:水平和垂直方向的速度
    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2,float velocityX, float velocityY) {
        //过滤掉Y的移动
        if(Math.abs(e1.getRawY()-e2.getRawY())>150){
            Toast.makeText(mActivity,"手势无效", Toast.LENGTH_SHORT).show();
            return false;
        }
        if(e1.getRawX()-e2.getRawX()>30){
            //从右向左滑,显示下一个界面
            showState();
            return true;
        }else if (e2.getRawX()-e1.getRawX()>30) {
            //从左向右滑,显示上一个界面
            showOrder();
            return true;
        }
        return super.onFling(e1, e2, velocityX, velocityY);
    }
});
mMyTouchListener = new NewFrameActivity.MyOnTouchListener() {
    @Override
    public boolean onTouch(MotionEvent ev) {
        return mGestureDetector.onTouchEvent(ev);
    }
};
mParentActivity = (NewFrameActivity)getActivity();
mParentActivity.registerMyOnTouchListener(mMyTouchListener);  

上述所使用到的动画效果,在下方会继续进行讲解。

切换动画

Fragment的转场动画实现分为使用v4包和不使用v4包两种情况,不使用v4包的话,最低API Level需要是11。

标准切换动画

可以给Fragment指定标准的转场动画,通过setTransition(int transit)方法。

该方法可传入的三个参数是:

TRANSIT_NONE,
TRANSIT_FRAGMENT_OPEN,
TRANSIT_FRAGMENT_CLOSE

分别对应无动画、打开形式的动画和关闭形式的动画。标准动画设置好后,在Fragment添加和移除的时候都会有。

自定义切换动画

 自定义转场动画是通过setCustomAnimations()方法,因为Fragment添加时可以指定加入到Back Stack中,所以转场动画有添加、移除、从Backstack中pop出来,还有进入四种情况。

注意:setCustomAnimations()方法必须在add、remove、replace调用之前被设置,否则不起作用。

不使用v4包的情况下(min API >=11)所对应的动画类型是Property Animation。即动画资源文件需要放在resanimator目录下,且根标签是, , or 三者之一。
自定义转场动画时,四个参数的形式setCustomAnimations (int enter, int exit, int popEnter, int popExit)是API Level 13才有的,11只引入了两个动画的形式,即无法指定Back Stack栈操作时的转场动画

private void addFragment() {
    if (mFragmentManager == null) {
        mFragmentManager = getFragmentManager();
    }
    mTextFragmentOne = new MyFragmentOne();
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
    /**标准动画*/
    // fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
    // fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
    // fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE);
    /**自定义动画*/
    // API LEVEL 11
    fragmentTransaction.setCustomAnimations(R.animator.fragment_slide_left_enter,R.animator.fragment_slide_right_exit);
    // API LEVEL 13
    // fragmentTransaction.setCustomAnimations(R.animator.fragment_slide_left_enter,R.animator.fragment_slide_left_exit,
    // R.animator.fragment_slide_right_enter,R.animator.fragment_slide_right_exit);
    fragmentTransaction.add(R.id.container, mTextFragmentOne);
    // 加入到BackStack中
    fragmentTransaction.addToBackStack(null);
    fragmentTransaction.commit();
}  

其中下面四个动画是从apiDemos中提取的,3.0之后的动画的根标签是set,而3.0之前的动画是不一样的。

fragment_slide_left_enter:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:interpolator="@android:interpolator/decelerate_quint"
        android:valueFrom="100dp" android:valueTo="0dp"
        android:valueType="floatType"
        android:propertyName="translationX"
        android:duration="@android:integer/config_mediumAnimTime" />
    <objectAnimator
        android:interpolator="@android:interpolator/decelerate_quint"
        android:valueFrom="0.0" android:valueTo="1.0"
        android:valueType="floatType"
        android:propertyName="alpha"
        android:duration="@android:integer/config_mediumAnimTime" />
</set>  

fragment_slide_left_exit:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:interpolator="@android:interpolator/decelerate_quint"
        android:valueFrom="0dp" android:valueTo="-100dp"
        android:valueType="floatType"
        android:propertyName="translationX"
        android:duration="@android:integer/config_mediumAnimTime" />
    <objectAnimator
        android:interpolator="@android:interpolator/decelerate_quint"
        android:valueFrom="1.0" android:valueTo="0.0"
        android:valueType="floatType"
        android:propertyName="alpha"
        android:duration="@android:integer/config_mediumAnimTime" />
</set>  

fragment_slide_right_enter:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:interpolator="@android:interpolator/decelerate_quint"
        android:valueFrom="-100dp" android:valueTo="0dp"
        android:valueType="floatType"
        android:propertyName="translationX"
        android:duration="@android:integer/config_mediumAnimTime" />
    <objectAnimator
        android:interpolator="@android:interpolator/decelerate_quint"
        android:valueFrom="0.0" android:valueTo="1.0"
        android:valueType="floatType"
        android:propertyName="alpha"
        android:duration="@android:integer/config_mediumAnimTime" />
</set> 

fragment_slide_right_exit:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:interpolator="@android:interpolator/decelerate_quint"
        android:valueFrom="0dp" android:valueTo="100dp"
        android:valueType="floatType"
        android:propertyName="translationX"
        android:duration="@android:integer/config_mediumAnimTime" />
    <objectAnimator
        android:interpolator="@android:interpolator/decelerate_quint"
        android:valueFrom="1.0" android:valueTo="0.0"
        android:valueType="floatType"
        android:propertyName="alpha"
        android:duration="@android:integer/config_mediumAnimTime" />
</set>  

使用v4包,Fragment的使用不再局限于API Level 11之上,低等级的API也可以使用,但是这时候转场动画的类型是View Animation。

Fragment中的方法:onCreateAnimation(int transit, boolean enter, int nextAnim)返回值Animation。
FragmentTransaction中的setCustomAnimations()方法,两参数类型和四参数类型都可用。

所以一般还是用v4包的这个版本,一是兼容性比较好,另外View Animation其实基本可以满足转场动画的需要。

private void addFragment() {
    if (null == mFragmentManager) {
        mFragmentManager = getSupportFragmentManager();
    }
    mTextFragmentOne = new MyFragmentOne();
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
    fragmentTransaction.setCustomAnimations(
            R.anim.push_left_in,
            R.anim.push_left_out,
            R.anim.push_left_in,
            R.anim.push_left_out);
    fragmentTransaction.add(R.id.container, mTextFragmentOne);
    fragmentTransaction.addToBackStack(null);
    fragmentTransaction.commit();
}  

早期版本所使用到的转场动画请参考手势识别篇的动画。

数据传递

在Android中,Fragment数据的传递的如下图所示:

组件获取

Fragment获得Activity中的组件: getActivity().findViewById(R.id.list);
Activity获得Fragment中的组件:getFragmentManager.findFragmentByid(R.id.fragment1)

数据传递

在Android中,Activity和Fragment数据传递的方式如下:

Activit -> Fragment:使用setArguments(Bundle args)传递,在onCreate中使用getArguments()取出。
Fragment ->Activity:在Fragment中定义回调接口,再让包含该Fragment的Activity实现该回调接口来进行数据通信。

1、定义一个回调接口(Fragment中)

public interface CallBack{  
    /*定义一个获取信息的方法*/  
    public void getResult(String result);  
}   

2、接口回调(Fragment中)

/*接口回调*/  
public void getData(CallBack callBack){  
    /*获取文本框的信息,当然你也可以传其他类型的参数,看需求咯*/  
    String msg = editText.getText().toString();  
    callBack.getResult(msg);  
}   

3、使用接口回调方法读数据(Activity中)

customFragment.getData(new CallBack() {  
    @Override  
    public void getResult(String result) {
            Toast.makeText(MainActivity.this, "-->>" + result, 1).show();  
        }
    }); 
}  
原文地址:https://www.cnblogs.com/pengjingya/p/5507733.html