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

  • 相关阅读:
    eclipse如何与git 配合工作。
    git托管代码(二)
    PPC2003 安装 CFNET 3.5成功
    我的Window Mobile WCF 項目 第三篇 WM窗体设计
    我的Window Mobile WCF 項目 第一篇Mobile开发和WinForm开发的区别
    我的Window Mobile WCF 項目 第七天
    我的Window Mobile WCF 項目 第二篇 WindowsMobile访问WCF
    WCF 用vs2010 和 vs2008的简单对比测试
    vs2010beta1 和 搜狗输入法 冲突,按下 Ctrl 键就报错,重装搜狗解决
    我的Window Mobile WCF 項目 第六天 (二)
  • 原文地址:https://www.cnblogs.com/sharecenter/p/5621024.html
Copyright © 2011-2022 走看看