zoukankan      html  css  js  c++  java
  • Fragment的基本应用

    转载请注明出处:http://blog.csdn.net/crazy1235/article/details/50933621


    Fragment

    还是先来基本介绍。

    Fragment –> 片段
    在Android3.0的时候被引入,它的出现主要是给大屏幕设备提供更加灵活的UI支持。通过对Activity布局进行分片。更加方便的对每块进行独立控制。这些片段能够被不同的activity复用。


    fragment生命周期

    每一个fragment拥有自己的生命周期,可是fragment要依赖于activity存在,生命周期受到包含它的activity的生命周期控制。

    来两张神图~~

    这里写图片描写叙述 这里写图片描写叙述

    左图就是fragment的生命周期图。右图是fragment与activity各自生命周期的对比。

    介绍一下经常使用的几个生命周期函数:

    • onAttach(Context) –> 当fragment被绑定到activity上时回调

    • onCreate(Bundle) –> 当fragment对象创建的时候回调。一般在此方法里做參数接收。

    • onCreateView(LayoutInflater, ViewGroup, Bundle) –> 创建fragment视图时回调

    • onDestoryView –> 视图销毁时回调

    • onDestory –> 销毁fragment时回调

    • onDetech() –> fragment与activity失去关联时回调


    fragment的使用

    使用fragment能够当成一个控件,直接放到activity布局文件中。也能够在代码里面动态的加入、更新或者删除。

    以下的activity布局文件中定义了一个fragment和一个frameLayout。

    使用标签能够称之为静态的Fragment。在activity创建的时候也会去创建并显示它,而framelayout是一个容器,我们在代码中能够动态的加入一个fragment进去。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout 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"
        android:orientation="horizontal"
        tools:context=".fragments.ArticleActivity">
        <!--headlines-->
        <fragment
            android:id="@+id/headline_fragment"
            android:name="com.jacksen.demo.view.fragments.HeadlinesFragment"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            tools:layout="@layout/fragment_item_list" />
        <!--article-->
        <FrameLayout
            android:id="@+id/article_frame_layout"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="2" />
    </LinearLayout>

    注意:

    使用标签显示Fragment的时候,须要对这个fragment设置一个id或者tag。否则会出现”Error inflating class fragment”错误。

    这里写图片描写叙述

    Caused by: java.lang.IllegalArgumentException: Binary XML file line #10: Must specify unique android:id, android:tag, or have a parent with an id for com.jacksen.demo.view.fragments.HeadlinesFragment

    Fragment的子类继承的时候。假设你的minSdkVersion <= 11,须要引入V4包,然后倒入android.support.v4.app.Fragment包。

    假设是大于11。直接导入android.app.Fragment包就可以。

    package com.jacksen.demo.view.fragments;
    import android.content.Context;
    import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.support.v7.widget.GridLayoutManager;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import com.jacksen.demo.view.R;
    import com.jacksen.demo.view.fragments.dummy.ArticleBean;
    import com.jacksen.demo.view.fragments.dummy.ArticleBean.ArticleItem;
    
    public class HeadlinesFragment extends Fragment {
        private static final String ARG_COLUMN_COUNT = "column-count";
        private int mColumnCount = 2;
        private OnChangeArticleListener mListener;
    
        public HeadlinesFragment() {
        }
        public static HeadlinesFragment newInstance(int columnCount) {
            HeadlinesFragment fragment = new HeadlinesFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_COLUMN_COUNT, columnCount);
            fragment.setArguments(args);
            return fragment;
        }
        @Override
        public void onAttach(Context context) {
            super.onAttach(context);
            Log.d("HeadlinesFragment", "onAttach");
            if (context instanceof OnChangeArticleListener) {
                mListener = (OnChangeArticleListener) context;
            } else {
                throw new RuntimeException(context.toString()
                        + " must implement OnChangeArticleListener");
            }
        }
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Log.d("HeadlinesFragment", "onCreate");
            if (getArguments() != null) {
                mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT);
            }
        }
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            Log.d("HeadlinesFragment", "onCreateView");
            View view = inflater.inflate(R.layout.fragment_item_list, container, false);
            // Set the adapter
            if (view instanceof RecyclerView) {
                Context context = view.getContext();
                RecyclerView recyclerView = (RecyclerView) view;
                if (mColumnCount <= 1) {
                    recyclerView.setLayoutManager(new LinearLayoutManager(context));
                } else {
                    recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount));
                }
                recyclerView.setAdapter(new HeadlinesRecyclerViewAdapter(ArticleBean.ITEMS, mListener));
            }
            return view;
        }
        @Override
        public void onDetach() {
            super.onDetach();
            Log.d("HeadlinesFragment", "onDetach");
            mListener = null;
        }
        public interface OnChangeArticleListener {
            void onChangeArticle(ArticleItem item);
        }
    }

    动态的加入fragment就须要在代码里面通过FragmentManager等类进行操作:

    frameLayout = (FrameLayout) findViewById(R.id.article_frame_layout);
    fragmentManager = getSupportFragmentManager();
    articleFragment = ArticleFragment.newInstance();
    fragmentManager.beginTransaction().replace(R.id.article_frame_layout, articleFragment).commit();

    Fragment的事务管理

    android里通过FragmentManager类进行管理fragment。一组对fragment的操作(加入、删除、替换等)称为一个事务,通过FragmentTransaction类来提交运行。

    也能够把事务加入到回退栈中,进行回滚。这有点相似于数据库的事务机制。

    注意:

    • 事物操作最后必须调用commit()才干运行。

    • 调用commit()方法之后,也不是立马运行;假设须要立马运行,能够使用executePendingTransactions()方法。

    • 一次性add多个fragment,显示的是最后一个。

    • 任务栈回退针对的是事务。而不是fragment。一次事务操作过程中能够有非常多个对fragment的操作。

    • 仅仅能在activity处于可保存状态的情况下。进行事务操作。

      否则引发例如以下异常:

    java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

    比方。在onPause()或者onStop()中提交事务,就会出现以上问题,假设非要在这些生命周期里面进行事务提交。请使用FragmentTransaction类的commitAllowingStateLoss()方法,同意状态丢失。

    • 假设activity继承的是AppCompatActivity。onBackPressed()回调函数里面是利用的V4包的getSupportFragmentManager()进行的栈回退,所以做fragment回退的时候须要注意引用的是不是V4包的Fragment类
    /**
         * Take care of popping the fragment back stack or finishing the activity
         * as appropriate.
         */
        public void onBackPressed() {
            if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
                supportFinishAfterTransition();
            }
        }

    Fragment之间的切换

    Fragment的切换就是基本就是利用add()hide()show()replace()这四个方法。

    情况一:採用add方式切换fragment

    activity:

    @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test_fragments2);
            ButterKnife.bind(this);
            FragmentManager fragmentManager = getSupportFragmentManager();
            TabFragment1 tabFragment1 = TabFragment1.newInstance();
            fragmentManager.beginTransaction().add(R.id.test_fragments_layout, tabFragment1).addToBackStack("tab1").commit();
        }

    TabFragment1代码:

    @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            Log.d("TabFragment1", "onCreateView");
            View view = inflater.inflate(R.layout.fragment_tab_fragment1, container, false);
            ButterKnife.bind(this, view);
            openNextBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    FragmentManager fragmentManager = getFragmentManager();
                    TabFragment2 tabFragment2 = TabFragment2.newInstance();
                    fragmentManager.beginTransaction().add(R.id.test_fragments_layout, tabFragment2).addToBackStack(null).commit();
                }
            });
            return view;
        }

    TabFragment2代码:

    /**
     * Tab Fragment 2
     */
    public class TabFragment2 extends Fragment {
        public TabFragment2() {
        }
        public static TabFragment2 newInstance() {
            TabFragment2 fragment = new TabFragment2();
            return fragment;
        }
        @Override
        public void onAttach(Context context) {
            super.onAttach(context);
            Log.d("TabFragment2", "onAttach");
        }
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Log.d("TabFragment2", "onCreate");
            if (getArguments() != null) {
            }
        }
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            Log.d("TabFragment2", "onCreateView");
            return inflater.inflate(R.layout.fragment_tab_fragment2, container, false);
        }
        @Override
        public void onDestroyView() {
            super.onDestroyView();
            Log.d("TabFragment2", "onDestroyView");
        }
        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.d("TabFragment2", "onDestroy");
        }
        @Override
        public void onDetach() {
            super.onDetach();
            Log.d("TabFragment2", "onDetach");
        }
    }

    此时都是採用add的方式进行显示fragment。每次都会把加入fragment的事务叠加到回退栈上面。

    在TabFragment1界面点击button加入TabFragment2界面,然后在按返回键回退,打印出生命周期例如以下:

    这里写图片描写叙述

    情况二:採用replace的方式切换fragment

    activity代码不变。

    TabFragment1的代码例如以下:

    @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            Log.d("TabFragment1", "onCreateView");
            View view = inflater.inflate(R.layout.fragment_tab_fragment1, container, false);
            ButterKnife.bind(this, view);
            openNextBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    FragmentManager fragmentManager = getFragmentManager();
                    TabFragment2 tabFragment2 = TabFragment2.newInstance();
                    fragmentManager.beginTransaction().replace(R.id.test_fragments_layout, tabFragment2).addToBackStack(null).commit();
                }
            });
            return view;
        }

    TabFragment1切换到TabFragment2的时候,使用的replace方法。replace方法的作用是remove掉全部加入到同样id的容器里的fragment。然后加入參数里的fragment。所以会回调TabFragment1的onDestoryView()方法,等到从TabFragment2返回的时候,去运行replace事务的相反操作,也就会又一次创建TabFragment1的视图,回调onCreateView()方法。

    图片名称

    这里写图片描写叙述

    remove:

    我们在TabFragment2界面中调用remove方法,把TabFragment1移除掉。

    TabFragment1.java:

    @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            Log.d("TabFragment1", "onCreateView");
            View view = inflater.inflate(R.layout.fragment_tab_fragment1, container, false);
            ButterKnife.bind(this, view);
            openNextBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    FragmentManager fragmentManager = getFragmentManager();
                    TabFragment2 tabFragment2 = TabFragment2.newInstance();
                    fragmentManager.beginTransaction().add(R.id.test_fragments_layout, tabFragment2).addToBackStack(null).commit();
                }
            });
            return view;
        }

    TabFragment2.java:

    private onBtnClickListener onBtnClickListener;
        public TabFragment2() {
        }
        public static TabFragment2 newInstance() {
            TabFragment2 fragment = new TabFragment2();
            return fragment;
        }
        @Override
        public void onAttach(Context context) {
            super.onAttach(context);
            Log.d("TabFragment2", "onAttach");
            if (context instanceof onBtnClickListener){
                onBtnClickListener = (TabFragment2.onBtnClickListener) context;
            }else{
                throw new RuntimeException(context.toString()
                        + " must implement onBtnClickListener");
            }
        }
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Log.d("TabFragment2", "onCreate");
            if (getArguments() != null) {
            }
        }
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            Log.d("TabFragment2", "onCreateView");
            View view = inflater.inflate(R.layout.fragment_tab_fragment2, container, false);
            ButterKnife.bind(this, view);
            removeFragmentBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onBtnClickListener.removeFragment1();
                }
            });
            return view;
        }
        public interface onBtnClickListener{
            void removeFragment1();
        }

    activity:

     @Override
        public void removeFragment1() {
            fragmentManager.beginTransaction().remove(tabFragment1).commit();
        }

    从代码里看到,remove操作的事务没有加入到回退栈中。所以从TabFragment2中返回的时候,直接退到了activity界面。

    图片名称

    可是从界面来看,从TabFragment2会退到activity之后。再次按返回键并没有退出activity,然后再按返回键的时候才会退出activity。原因就是。尽管remove了TabFragment1,可是仅仅是回调了onDestoryView()方法销毁了视图,此时TabFragment1的对象资源和与activity的关联还没有断开。所以点击返回键的时候会有一个没有“响应”。

    以下看看我们把reomve()操作加入到回退栈的情况:

    @Override
        public void removeFragment1() {
            fragmentManager.beginTransaction().remove(tabFragment1).addToBackStack("remove").commit();
        }

    图片名称

    与上面的情况相比,多了TabFragment1的onCreateView()这一步。这是由于把remove事务加入到了任务栈,回退的时候逆向运行该操作。

    使用replace()方法和remove()方法会导致视图销毁,所以。切换fragment的时候,假设须要视图保留视图。就不能用这两个方法。


    Fragment与Activity之间传值

    • fragment –> activity 99%的做法都是通过接口回调来做的。定义一个接口。activity中实现此接口方法,在fragment里面调用接口方法

    • activity –> fragment 通过findFragmentById()后者findFragmentByTag()方法获取fragment实例调用fragment里的public方法就可以。

    接着往下看~~

    TabFragment2调用接口方法:

     @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            Log.d("TabFragment2", "onCreateView");
            View view = inflater.inflate(R.layout.fragment_tab_fragment2, container, false);
            ButterKnife.bind(this, view);
            removeFragmentBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onBtnClickListener.removeFragment1();
                }
            });
            tellSthBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onBtnClickListener.tellSth("这是我要对你说的话!

    "); } }); return view; } /** * 回调接口 */ public interface onBtnClickListener { void removeFragment1(); void tellSth(String str); }

    activity实现该接口的方法:

    public class TestFragments extends AppCompatActivity implements TabFragment2.onBtnClickListener {
        @Bind(R.id.test_fragments_layout)
        FrameLayout testFragmentsLayout;
        private FragmentManager fragmentManager;
        private TabFragment1 tabFragment1;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test_fragments2);
            ButterKnife.bind(this);
            fragmentManager = getSupportFragmentManager();
            tabFragment1 = TabFragment1.newInstance();
            fragmentManager.beginTransaction().add(R.id.test_fragments_layout, tabFragment1, "tab1").addToBackStack("tab1").commit();
        }
        @Override
        public void onBackPressed() {
            super.onBackPressed();
        }
        @Override
        public void removeFragment1() {
            fragmentManager.beginTransaction().remove(tabFragment1).addToBackStack("remove").commit();
        }
        @Override
        public void tellSth(String str) {
            ((TabFragment1)fragmentManager.findFragmentByTag("tab1")).showSth("hello: " + str);
        }
    }

    在TabFragment1中定义方法供activity调用:

        public void showSth(String str) {
            getSthEt.setText(str);
        }

    这里写图片描写叙述


    Fragment与Fragment传值

    activity给fragment传值你回了,fragment给activity传值你也会了。那么这个问题就不要问我了!。!


    Fragment切换动画

    Fragment的切换动画能够使用系统的标准动画,也能够自己定义动画。
    使用系统的须要用到setTransition(),可是仅仅能设置系统提供的有限的动画效果。

    • FragmentTransaction.TRANSIT_FRAGMENT_OPEN

    • FragmentTransaction.TRANSIT_FRAGMENT_CLOSE

    • FragmentTransaction.TRANSIT_FRAGMENT_FADE

    自己定义动画须要用到的类:

    /**
         * Set specific animation resources to run for the fragments that are
         * entering and exiting in this transaction. These animations will not be
         * played when popping the back stack.
         */
        public abstract FragmentTransaction setCustomAnimations(@AnimRes int enter,
                @AnimRes int exit);
    /**
         * Set specific animation resources to run for the fragments that are
         * entering and exiting in this transaction. The <code>popEnter</code>
         * and <code>popExit</code> animations will be played for enter/exit
         * operations specifically when popping the back stack.
         */
        public abstract FragmentTransaction setCustomAnimations(@AnimRes int enter,
                @AnimRes int exit, @AnimRes int popEnter, @AnimRes int popExit);

    注意:

    setCustomAnimations()必须在add()、remove()、replace()调用之前设置。否则不起作用。

    比方:有两个fragment A和B,从A切换到B的时候

    • @AnimRes int enter
      表示Fragment B的进入动画

    • @AnimRes int exit
      表示Fragment A的退出动画

    • @AnimRes int popEnter
      表示当从B界面pop回到A时,Fragment A的进入动画

    • @AnimRes int popExit
      表示当从B界面pop回到A是,Fragment B的退出动画

    假设使用add的方式显示下一个fragment。则仅仅会触发enter 和 popExit动画。由于这样的情况下A并没有被移除,仅仅是触发了与B相关的动画。

    比方:
    从TabFragment1使用add()方式显示TabFragment2:

    FragmentManager fragmentManager = getFragmentManager();
    TabFragment2 tabFragment2 = TabFragment2.newInstance();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    fragmentTransaction.setCustomAnimations(R.anim.slide_in_from_right, R.anim.slide_out_to_left, R.anim.slide_in_from_top, R.anim.slide_out_to_bottom);
    fragmentTransaction.add(R.id.test_fragments_layout, tabFragment2, "tab2");
    fragmentTransaction.addToBackStack("tab2");
    fragmentTransaction.commit();

    4个动画都是使用的简单的view动画,用属性动画能够做出更加绚丽的动画:

    slide_in_from_right.xml

    <?xml version="1.0" encoding="utf-8"?

    > <translate xmlns:android="http://schemas.android.com/apk/res/android" android:duration="800" android:fromXDelta="100.0%" android:interpolator="@android:interpolator/accelerate_decelerate_interpolator" android:toXDelta="0.0" />

    slide_out_to_left.xml

    <?xml version="1.0" encoding="utf-8"?

    > <translate xmlns:android="http://schemas.android.com/apk/res/android" android:duration="800" android:fromXDelta="0.0" android:interpolator="@android:interpolator/accelerate_decelerate_interpolator" android:toXDelta="-100%" />

    slide_in_from_top.xml

    <?xml version="1.0" encoding="utf-8"?>
    <translate xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:fromYDelta="-100.0%"
        android:interpolator="@android:interpolator/accelerate_decelerate"
        android:toYDelta="0.0" />

    slide_out_to_bottom.xml

    <?xml version="1.0" encoding="utf-8"?

    > <translate xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:fromYDelta="0" android:interpolator="@android:interpolator/accelerate_decelerate" android:toYDelta="100%p" />

    效果图:

    这里写图片描写叙述

    假设採用的replace的方式,则会正常的触发4个动画。

    FragmentManager fragmentManager = getFragmentManager();
    TabFragment2 tabFragment2 = TabFragment2.newInstance();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    fragmentTransaction.setCustomAnimations(R.anim.slide_in_from_right, R.anim.slide_out_to_left, R.anim.slide_in_from_top, R.anim.slide_out_to_bottom);
    fragmentTransaction.replace(R.id.test_fragments_layout, tabFragment2, "tab2");
    fragmentTransaction.addToBackStack("tab2");
    fragmentTransaction.commit();

    这里写图片描写叙述

    这里仅仅介绍了setCustomAnimations()的使用方法,setTransition()方式的动画非常easy,就不介绍了。


    本篇blog到此结束。
    如有错误,欢迎留言。
    谢谢~~~

  • 相关阅读:
    XML指南——XML 瀏覽器(Netscape、Explorer)
    XML指南——察看 XML 文件
    ASP中Dictionary的使用
    SQL Mobile的RDA数据同步开发
    XML指南——XML 確認
    简单的js分页脚本
    浏览器语种检测,适合于多语言版本的站点
    Com/Dcom/Com+的思考
    XML指南——XML數據島
    MOSS 2007 功能概述
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/7160058.html
Copyright © 2011-2022 走看看