• 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();  
            }
        }); 
    }  
    
  • 相关阅读:
    发布全文检索类库外包
    给即将面试的人
    实验四 Web服务器2
    电子公文传输系统验收2功能测试
    整数范围与类型转换
    实验三电子公文传输系统1个人贡献
    socket测试
    电子公文传输系统验收4开发基础
    Web服务器1socket编程
    算法测试(课上测试)
  • 原文地址:https://www.cnblogs.com/pengjingya/p/5507733.html
Copyright © 2020-2023  润新知