zoukankan      html  css  js  c++  java
  • Android 进阶 Fragment 介绍和使用 (二)

    管理fragment

     因为FragmentManager的API是在Android 3.0,也即API level 11开始引入的,所以对于之前的版本,需要使用support library中的FragmentActivity,并且使用getSupportFragmentManager()方法。

    用FragmentManager可以做的工作有:

      得到Activity中存在的fragment:

      使用findFragmentById()或findFragmentByTag()方法。

      将fragment弹出back stack:

      popBackStack():将back stack中最后一次的fragment转换弹出。如果没有可以出栈的东西,返回false。

      这个函数是异步的:它将弹出栈的请求加入队列,但是这个动作直到应用回到事件循环才会执行。

      为back stack加上监听器:

      addOnBackStackChangedListener()

    Fragment Transactions 事务

      使用Fragment时,可以通过用户交互来执行一些动作,比如增加、移除、替换等。

      所有这些改变构成一个集合,这个集合被叫做一个transaction。

      可以调用FragmentTransaction中的方法来处理这个transaction,并且可以将transaction存进由activity管理的back stack中,这样用户就可以进行fragment变化的回退操作。

      可以这样得到FragmentTransaction类的实例: 

    FragmentManager fragmentManager = getFragmentManager();
    
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    每个transaction是一组同时执行的变化的集合。

      用add(), remove(), replace()方法,把所有需要的变化加进去,然后调用commit()方法,将这些变化应用。

      在commit()方法之前,你可以调用addToBackStack(),把这个transaction加入back stack中去,这个back stack是由activity管理的,当用户按返回键时,就会回到上一个fragment的状态。

      比如下面的代码就是用一个新的fragment取代之前的fragment,并且将前次的状态存储在back stack中。

    // Create new fragment and transaction
    Fragment newFragment = new ExampleFragment();
    FragmentTransaction transaction = getFragmentManager().beginTransaction();
    
    // Replace whatever is in the fragment_container view with this fragment,
    // and add the transaction to the back stack
    transaction.replace(R.id.fragment_container, newFragment);
    transaction.addToBackStack(null);
    
    // Commit the transaction
    transaction.commit();
    在这个例子中,newFragment将取代在R.id.fragment_container容器中的fragment,如果没有,将直接添加新的fragment。

      通过调用addToBackStack(),commit()的一系列转换作为一个transaction被存储在back stack中,用户按Back键可以返回上一个转换前的状态。

      当你移除一个fragment的时候,如果commit()之前没有调用addToBackStack(),那个fragment将会是destroyed;如果调用了addToBackStack(),这个fragment会是stopped,可以通过返回键来恢复。

    关于commit()方法

    1你必须最后调用commit()

    2如果你添加了多个fragment,那么它们的显示顺序跟添加顺序一至(后显示的覆盖前面的)。

    如果你在执行的事务中有删除fragment的动作,而且没有调用addToBackStack(),那么当事务提交时,那些被删除的fragment就被销毁了。反之,那些fragment就不会被销毁,而是处于停止状态。当用户返回时,它们会被恢复。

    密技:对于fragment事务,你可以应用动画。在commit()之前调用setTransition()就行。――一般银我不告诉他哦。

    但是,调用commit()后,事务并不会马上执行。它会在activityUI线程(其实就是主线程)中等待直到线程能执行的时候才执行(废话)。如果必要,你可以在UI线程中调用executePendingTransactions()方法来立即执行事务。但一般不需这样做,除非有其它线程在等待事务的执行。

    警告:你只能在activity处于可保存状态的状态时,比如running中,onPause()方法和onStop()方法中提交事务,否则会引发异常。这是因为fragment的状态会丢失。如果要在可能丢失状态的情况下提交事务,请使用commitAllowingStateLoss()

    实例程序

      写了个小程序实践了一下fragment的管理,程序不是很完善,就是试试基本用法,先按第一个按钮添加一个fragment,第二个按钮将添加第二个fragment,按删除按钮删除相应fragment。

     相关代码:

      第一个fragment:

    import android.os.Bundle;
    import android.app.Fragment;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    
    public class FirstFragment extends Fragment {
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            // Inflate the layout for this fragment
            return inflater.inflate(R.layout.fragment_first, container, false);
        }
    }
    

    布局:

    <FrameLayout 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="com.jcdh.jcli.myapplication.FirstFragment">
    
        <!-- TODO: Update blank fragment layout -->
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:textSize="20sp"
            android:gravity="center"
            android:background="@android:color/black"
            android:textColor="@android:color/white"
            android:text="我是第一个Fragment" />
    
    </FrameLayout>
    

    第二个Fragment

    import android.os.Bundle;
    import android.app.Fragment;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    
    public class SecondFragment extends Fragment {
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            // Inflate the layout for this fragment
            return inflater.inflate(R.layout.fragment_second, container, false);
        }
    }
    

    布局:

    <FrameLayout 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="com.jcdh.jcli.myapplication.FirstFragment">
    
        <!-- TODO: Update blank fragment layout -->
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:textSize="30sp"
            android:gravity="center"
            android:background="@android:color/darker_gray"
            android:text="我是第二个Fragment" />
    
    </FrameLayout>
    

    主Actiivty:

    import android.app.FragmentManager;
    import android.app.FragmentTransaction;
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Toast;
    
    public class MainActivity extends Activity {
    
        private FirstFragment fristFragment;
        private SecondFragment secondFragment;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        public void moveToFragment(View view) {
            // 开启Fragment事务
            FragmentManager fm = getFragmentManager();
            FragmentTransaction transaction = fm.beginTransaction();
            switch (view.getId()) {
                case R.id.first_btn:
                    if (fristFragment == null) {
                        fristFragment = new FirstFragment();
                        transaction.replace(R.id.first_fragment, fristFragment);
                    } else {
                        Toast.makeText(MainActivity.this, "已经添加过了fristFragment,不能重复添加", Toast.LENGTH_SHORT).show();
                        return;
                    }
    
                    break;
                case R.id.second_btn:
                    if (secondFragment == null) {
                        secondFragment = new SecondFragment();
    
                        transaction.replace(R.id.first_fragment, secondFragment);
    
                    } else {
                        Toast.makeText(MainActivity.this, "已经添加过了fristFragment,不能重复添加", Toast.LENGTH_SHORT).show();
                        return;
                    }
    
                    break;
            }
            transaction.addToBackStack(null);
            transaction.commit();
        }
    
        public void removeFragment(View v) {
            if (getFragmentManager().getBackStackEntryCount() == 0) {
                Toast.makeText(MainActivity.this, "没有  Fragment 可以被 删除, ", Toast.LENGTH_SHORT).show();
                return;
            }
            FragmentTransaction transaction = getFragmentManager().beginTransaction();
            switch (v.getId()) {
                case R.id.button:
    
                    if (null != fristFragment) {
                        transaction.remove(fristFragment);
                        fristFragment = null;
                        Toast.makeText(MainActivity.this, "删除fristFragment, ", Toast.LENGTH_SHORT).show();
                    } else {
    
                        Toast.makeText(MainActivity.this, "没有fristFragment可以被删除 ", Toast.LENGTH_SHORT).show();
                    }
    
                    break;
                case R.id.button2:
                    if (null != secondFragment) {
                        transaction.remove(secondFragment);
                        secondFragment = null;
                        Toast.makeText(MainActivity.this, "删除secondFragment, ", Toast.LENGTH_SHORT).show();
                    }
                    else
                    {
                        Toast.makeText(MainActivity.this, "没有删除secondFragment可以被删除 ", Toast.LENGTH_SHORT).show();
                    }
                    break;
            }
            transaction.commit();// 一定要提交才能生效
        }
    }

    Actiivity 布局:

    <?xml version="1.0" encoding="utf-8"?>
    <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="com.jcdh.jcli.myapplication.MainActivity">
    
        <Button
            android:id="@+id/first_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:onClick="moveToFragment"
            android:text="第一个Fragment" />
    
        <Button
            android:id="@+id/second_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@id/first_btn"
            android:onClick="moveToFragment"
            android:layout_alignParentTop="true"
            android:text="第二个Fragment" />
    
        <fragment
            android:id="@+id/first_fragment"
            android:name="com.jcdh.jcli.myapplication.FirstFragment"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:layout_below="@+id/first_btn">
        </fragment>
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="del 
    firstfragment"
            android:id="@+id/button"
            android:onClick="removeFragment"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
          />
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="del 
    secondfragment"
            android:id="@+id/button2"
            android:onClick="removeFragment"
            android:layout_alignBottom="@+id/button"
            android:layout_alignParentLeft="true"
            />
    </RelativeLayout>
    

    效果图:


    Fragment和Activity的交互

      一个Fragment的实例总是和包含它的Activity直接相关。

      fragment可以通过getActivity() 方法来获得Activity的实例,然后就可以调用一些例如findViewById()之类的方法。

      如:

      View listView =getActivity().findViewById(R.id.list);

      但是注意调用getActivity()时,fragment必须和activity关联(attached to an activity),否则将会返回一个null。

      相似的,activity也可以获得一个fragment的引用,从而调用fragment中的方法。

      获得fragment的引用要用FragmentManager,之后可以调用findFragmentById() 或者 findFragmentByTag().

      比如:

      ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);

    创建事件回调

      一些情况下,可能需要fragment和activity共享事件,一个比较好的做法是在fragment里面定义一个回调接口,然后要求宿主activity实现它。

      当activity通过这个接口接收到一个回调,它可以同布局中的其他fragment分享这个信息。

      例如,一个新闻显示应用在一个activity中有两个fragment,一个fragment A显示文章题目的列表,一个fragment B显示文章。

      所以当一个文章被选择的时候,fragment A必须通知activity,然后activity通知fragment B,让它显示这篇文章。

      这个情况下,在fragment A中声明一个这样的接口OnArticleSelectedListener:

    public static class FragmentA extends ListFragment {
        ...
        // Container Activity must implement this interface
        public interface OnArticleSelectedListener {
            public void onArticleSelected(Uri articleUri);
        }
        ...
    }
    之后包含这个fragment的activity实现这个OnArticleSelectedListener接口,用覆写的onArticleSelected()方法将fragment A中发生的事通知fragment B。

    为了确保宿主activity实现这个接口,fragment A的onAttach() 方法(这个方法在fragment 被加入到activity中时由系统调用)中通过将传入的activity强制类型转换,实例化一个OnArticleSelectedListener对象:

    public static class FragmentA extends ListFragment {
        OnArticleSelectedListener mListener;
        ...
        @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            try {
                mListener = (OnArticleSelectedListener) activity;
            } catch (ClassCastException e) {
                throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
            }
        }
        ...
    }
    如果activity没有实现这个接口,fragment将会抛出ClassCastException异常,如果成功了,mListener将会是activity实现OnArticleSelectedListener接口的一个引用,所以通过调用OnArticleSelectedListener接口的方法,fragment A可以和activity共享事件。

     比如,如果fragment A是ListFragment的子类,每一次用户点击一个列表项目,系统调用fragment中的onListItemClick() 方法,在这个方法中可以调用onArticleSelected()方法与activity共享事件。

    public static class FragmentA extends ListFragment {
        OnArticleSelectedListener mListener;
        ...
        @Override
        public void onListItemClick(ListView l, View v, int position, long id) {
            // Append the clicked item's row ID with the content provider Uri
            Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
            // Send the event and Uri to the host activity
            mListener.onArticleSelected(noteUri);
        }
        ...
    }

    效果图片:


    处理Fragment的生命周期

    三种停留状态

      管理fragment的生命周期和管理activity的生命周期类似,和activity一样,fragment可以在三种状态下停留:

      Resumed

      fragment在running的activity中可见。

      Paused

      另一个activity在前景运行,并且享有焦点,但是这个fragment所在的activity仍然可见(前景activity部分遮挡或者是半透明的)。

      Stopped

      fragment不可见。可能是因为宿主activity处于stopped状态,或者fragment被remove掉,然后加在了back stack中。

      一个处于stopped状态的activity还是存活状态的,所有的状态和成员信息会被系统保持。但是,它不再被用户可见,并且如果宿主activity被kill掉,它也会被kill掉。

    数据存储和恢复

      和Activity类似,可以用Bundle类对象保存fragment的状态,当activity的进程被kill之后,需要重建activity时,可以用于恢复fragment的状态。

      存储时利用onSaveInstanceState()回调函数,恢复时是在 onCreate()onCreateView(), 或者onActivityCreated()里。

    Back Stack

      activity和fragment生命周期最重要的不同之处是它们如何存储在各自的back stack中。

      Activity停止时,是存在一个由系统维护的back stack中,但是当fragment停止(被remove)时,需要程序员显示地调用addToBackStack() ,并且fragment是存在一个由宿主activity掌管的back stack中。

    Fragment和Activity的生命周期

      宿主activity的声明周期直接影响到fragment的生命周期,比如activity生命周期的回调函数调用时,所有在其中的fragment的相同的回调函数会同时被调用。

    Activity直接影响它所包含的fragment的生命周期,所以对activity的某个生命周期方法的调用也会产生对fragment相同方法的调用。例如:当activityonPause()方法被调用时,它所包含的所有的fragment们的onPause()方法都会被调用。

    Fragmentactivity还要多出几个生命周期回调方法,这些额外的方法是为了与activity的交互而设立,如下:

    onAttach()

    fragment被加入到activity时调用(在这个方法中可以获得所在的activity)。

    onCreateView()

    activity要得到fragmentlayout时,调用此方法,fragment在其中创建自己的layout(界面)

    onActivityCreated()

    activityonCreated()方法返回后调用此方法。

    onDestroyView()

    fragmentlayout被销毁时被调用。

    onDetach()

    fragment被从activity中删掉时被调用。

    一旦activity进入resumed状态(也就是running状态),你就可以自由地添加和删除fragment了。因此,只有当activityresumed状态时,fragment的生命周期才能独立的运转,其它时候是依赖于activity的生命周期变化的


    如图:

     

    第一个demo:

    http://download.csdn.net/detail/q610098308/9311169

    第二个demo activity 与 fragment 交互:

    http://download.csdn.net/detail/q610098308/9312377

  • 相关阅读:
    实战(三):对游戏的破解“木叶忍者”
    实战(一):对“钉钉”的逆向(实现打卡功能)
    实战(二):对“微信”的逆向(实现界面自定义)
    iOS签名机制
    AM64汇编
    动态调试(二)
    动态调试(一)
    theos(二)
    murmurhash
    虚继承
  • 原文地址:https://www.cnblogs.com/sharecenter/p/5621024.html
Copyright © 2011-2022 走看看