码迷,mamicode.com
首页 > 其他好文 > 详细

AT-Fragment

时间:2016-05-11 12:56:47      阅读:199      评论:0      收藏:0      [点我收藏+]

标签:

关于Fragment的粗略翻译

 

 英文版api地址:Fragment (自备梯子)

  Fragment

   类概述(Class Overview):

       Fragment是一种可以替换Activity的用户界面。与Fragment进行交互的话,得通过Fragmentmanager来实现,而要获得FragmentManager有两种方法:Activity.getFragmentManager()和Fragment.getFragmentManager().
你可以通过多种方式来使用Fragment,从而实现许多效果。Fragment的主要核心是:它代表的是一系列的运行在Activity中的特殊操作或接口。Fragment被紧紧绑定在Activity上,不可分开。尽管Fragment定义了自己的一套生命周期,但是它的生命周期还得要依赖于它绑定的那个Activity的生命周期:当一个Activity停止了(stopped),这个Activity里面不会有Fragment是启动(started)状态的;当一个Activity被销毁了(destroyed),它里面的所有Fragment都应该是被销毁(destroyed)的状态。
而对于所有的Fragment的子类来说,它们都必须包括一个公有的无参构造函数(public no-argument constructor)。因为Framework层会经常重新初始化一些有需要的Fragment,尤其是在Fragment实例状态恢复(restore)时,它需要去找到那个对应的无参构造函数去初始化构造实例。所以说在当没有找到那个无参构造函数时,有时候会在实例状态恢复时报一个运行时异常(a runtime exception)

开发引导(Developer Guides)

           如果你想获得更多的关于怎样使用Fragment的话,请阅读Fragment的开发引导

老版本平台(Older Platforms)
     因为Fragment的API是在 HONEYCOMB 版本开始被引进的,但是在跟老的版本上面,我们可以通过 FragmentActivity 来使用它的API。

  生命周期(Lifecycle)
            尽管Fragment的声明周期是绑定在它的Activity上的,但是在一个标准的Actvity生命周期上,里面的Fragment生命周期仍然是有规律可循的。Fragment的生命周期中包括了一些Activity生命周期已有的方法,比如:onResume(),这些方法与Activity、UI层的交互有着联系,所以显得十分重要。

下面是一系列方法,用来将fragment恢复状态

  1. onAttach(Activity) 当Fragment要绑定在Activity时调用
  2. onCreate(Bundle) 当初始化创建Fragment时会调用
  3. onCreateView(LayoutInflater,ViewGroup,Bundle) 创建并且返回和对应的Fragment有关的视图
  4. onActivityCreated(Bundle) 告诉Fragment,和它绑定的那个Activity已经执行完对应的Activity.onCreate()方法了。
  5. onViewStateRestored(Bundle) 告诉Fragment,它的视图里面的所有数据状态都得到恢复了,前提是先前储存了状态信息哦。
  6. onStart() 使Fragment对于用户变得可见(前提是和它绑定的Activity已经是被启动了的)
  7. onResume() 使Fragment开始变得可以处理用户的交互了(前提是和它绑定的Activity已经是处于恢复状态了的,即onResume())

当Fragment不再被使用时,它会执行相反的一系列回调

  1. onPause() Fragment不再接受用户交互,因为可能此时Activity已经被停止了(onPause()) ,或者的话就是在Activity里面执行一些Fragment操作。
  2. onStop() Fragment不再可见,因为可能此时Activity已经被停止了(onStop()),或者的话就是在Acytivity里面执行了一些Fragment操作。
  3. onDestroyView() 允许Fragment去清除那些和它的视图有关的的资源
  4. onDestroy() 最终清除Fragment的状态信息
  5. onDetach() 当Fragment不再和Activity有联系时,即解除了绑定时,会快速调用本方法。

该属性可以被用在<fragment>中来为一个Fragment提供特殊的标签名字该属性可以被用在<fragment>中来为一个Fragment提供特殊的标签名字

      Fragment可以被用来作为你的应用布局的一部分,它可以让你模块化你的代码,并且可以在应用运行时更加容易的调整你的界面布局。下面举一个简单例子,这个简单的例子中会包括一个ListView用来展示一系列的内容,并且可以在你点击了其中一个时展示所选选项的具体内容。

     你可以在一个Activity的布局文件中来嵌入一个Fragment实例,方法是通过使用<fragment>标签。比如,下面就是一个例子,展示了怎样在布局文件中嵌入Fragment实例

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent">
    <fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
            android:id="@+id/titles"
            android:layout_width="match_parent" android:layout_height="match_parent" />
</FrameLayout>

 

     接下来你可以通过常规的方法来将该布局设置到指定的Activity上面去

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.fragment_layout);
}

    接下来我们会创建一个TitleFragment,用来展示一系列标题,我们实现它的话非常简单,因为我们继承的是ListFragment,ListFragment为我们做了大部分的工作。并且实现点击事件的方法:关于点击后产生的效果,我们会针对不同的布局产生不同的点击效果,其中包括在原视图布局中来显示点击后对应的详细内容,也可以启动一个新的Activity来显示点击后对应的详细内容。

public static class TitlesFragment extends ListFragment {
    boolean mDualPane;
    int mCurCheckPosition = 0;

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        // Populate list with our static array of titles.
        setListAdapter(new ArrayAdapter<String>(getActivity(),
                android.R.layout.simple_list_item_activated_1, Shakespeare.TITLES));

        // Check to see if we have a frame in which to embed the details
        // fragment directly in the containing UI.
        View detailsFrame = getActivity().findViewById(R.id.details);
        mDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE;

        if (savedInstanceState != null) {
            // Restore last state for checked position.
            mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
        }

        if (mDualPane) {
            // In dual-pane mode, the list view highlights the selected item.
            getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
            // Make sure our UI is in the correct state.
            showDetails(mCurCheckPosition);
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("curChoice", mCurCheckPosition);
    }

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        showDetails(position);
    }

    /**
     * Helper function to show the details of a selected item, either by
     * displaying a fragment in-place in the current UI, or starting a
     * whole new activity in which it is displayed.
     */
    void showDetails(int index) {
        mCurCheckPosition = index;

        if (mDualPane) {
            // We can display everything in-place with fragments, so update
            // the list to highlight the selected item and show the data.
            getListView().setItemChecked(index, true);

            // Check what fragment is currently shown, replace if needed.
            DetailsFragment details = (DetailsFragment)
                    getFragmentManager().findFragmentById(R.id.details);
            if (details == null || details.getShownIndex() != index) {
                // Make new fragment to show this selection.
                details = DetailsFragment.newInstance(index);

                // Execute a transaction, replacing any existing fragment
                // with this one inside the frame.
                FragmentTransaction ft = getFragmentManager().beginTransaction();
                if (index == 0) {
                    ft.replace(R.id.details, details);
                } else {
                    ft.replace(R.id.a_item, details);
                }
                ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
                ft.commit();
            }

        } else {
            // Otherwise we need to launch a new activity to display
            // the dialog fragment with selected text.
            Intent intent = new Intent();
            intent.setClass(getActivity(), DetailsActivity.class);
            intent.putExtra("index", index);
            startActivity(intent);
        }
    }
}

   还有另一个DetailsFragment来显示对应点击项后对应的内容,显示的内容会因为点击项的不同而显示不同的内容

public static class DetailsFragment extends Fragment {
    /**
     * Create a new instance of DetailsFragment, initialized to
     * show the text at ‘index‘.
     */
    public static DetailsFragment newInstance(int index) {
        DetailsFragment f = new DetailsFragment();

        // Supply index input as an argument.
        Bundle args = new Bundle();
        args.putInt("index", index);
        f.setArguments(args);

        return f;
    }

    public int getShownIndex() {
        return getArguments().getInt("index", 0);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (container == null) {
            // We have different layouts, and in one of them this
            // fragment‘s containing frame doesn‘t exist.  The fragment
            // may still be created from its saved state, but there is
            // no reason to try to create its view hierarchy because it
            // won‘t be displayed.  Note this is not needed -- we could
            // just run the code below, where we would create and return
            // the view hierarchy; it would just never be used.
            return null;
        }

        ScrollView scroller = new ScrollView(getActivity());
        TextView text = new TextView(getActivity());
        int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                4, getActivity().getResources().getDisplayMetrics());
        text.setPadding(padding, padding, padding, padding);
        scroller.addView(text);
        text.setText(Shakespeare.DIALOGUE[getShownIndex()]);
        return scroller;
    }
}

    考虑一下当这种情况时:当用户点击了TitleFragment中的其中一个选项时,此时在当前布局中装不下DetailsFragment时,我们该怎么办?所以此时我们应该启动一个新的Activity来显示点击项对应的内容

public static class DetailsActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (getResources().getConfiguration().orientation
                == Configuration.ORIENTATION_LANDSCAPE) {
            // If the screen is now in landscape mode, we can show the
            // dialog in-line with the list so we don‘t need this activity.
            finish();
            return;
        }

        if (savedInstanceState == null) {
            // During initial setup, plug in the details fragment.
            DetailsFragment details = new DetailsFragment();
            details.setArguments(getIntent().getExtras());
            getFragmentManager().beginTransaction().add(android.R.id.content, details).commit();
        }
    }
}

  然而,当我们写的应用运行在屏幕特别大的设备上面时,此时屏幕大到可以同时显示TitleFragment和DetailsFragment时,我们就要考虑将这两个Fragment同时放在同一个视图中,而不是点击后弹出另一个新的视图来显示内容。所以我们此时可以提供另一个可供选择的布局文件,需要注意的是:你应该把它命名为和上面的布局相同的名称,并且放在layout-land文件夹下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent" android:layout_height="match_parent">

    <fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
            android:id="@+id/titles" android:layout_weight="1"
            android:layout_width="0px" android:layout_height="match_parent" />

    <FrameLayout android:id="@+id/details" android:layout_weight="1"
            android:layout_width="0px" android:layout_height="match_parent"
            android:background="?android:attr/detailsElementBackground" />

</LinearLayout>

   此时,现有的代码会调整可选择的UI布局:当应用在运行时,对应指定的参数变化时,对应的DetailsActivity会终止(finish()),而去显示嵌入了DetailsFragment的TitleFragment。

   当指定参数发生变化时,会导致Activity中的Fragment重新启动,此时重新启动后的新实例也许会使用一个不同于先前布局的新布局。在这种情况下时,先前所有的Fragment将会重新实例初始化,并且以新的实例来运行。然而此时,它将和原先的<fragment>不再有联系,也不会再创建内容视图,并且会在 isInLayout() 方法中返回 false (这里的代码也就展示了当一个Fragment 不再运行在布局中时,你怎样避免创建视图层级结构 )。PS:下划线部分我也理解不了,暂时这样翻译吧,有理解的大神在底下请指出来,谢谢。

     当Fragment视图被绑定到父容器布局中时,<fragment>属性标签将会被用来操控提供的LayoutParams。他们也可以作为 onInflate(Activity,AttributeSet,Bundle) 方法的参数而被解析。

    当与指定Fragment绑定的父Activity需要销毁或者重新创建时,对应的Fragment将被重新初始化创建,此时就要求有一些唯一的标示来将原先的实例和新创建的实例来联系起来。而这些唯一的标示可以这样来提供:

  • 当没有任何明确的使用时,放置这些Fragment的容器(父布局)的id将会被拿来使用
  • android:tag  该属性可以被用在<fragment>中来为一个Fragment提供特殊的标签名字
  • android:id    该属性可以被用在<fragment>中来为一个Fragment提供特殊的标签名字sds

 

   上面的例子Google官网只是提供了片段,具体的完整代码请戳这儿

 

后退栈(Back Stack)

     Fragment的事务操作可以被放在对应的Activity的后退栈中,当用户在Activity中点击返回键时,只要对应的Activity没有被结束(finished),则可以一直从栈里面弹出对应的事务操作。

    举个例子,考虑现在有一个简单的Fragment(CountFragment),它可以通过一个整数数字作为参数(Integer)来初始化,并且可以将对应的整数展示在Fragment创建好的视图中。

public static class CountingFragment extends Fragment {
    int mNum;

    /**
     * Create a new instance of CountingFragment, providing "num"
     * as an argument.
     */
    static CountingFragment newInstance(int num) {
        CountingFragment f = new CountingFragment();

        // Supply num input as an argument.
        Bundle args = new Bundle();
        args.putInt("num", num);
        f.setArguments(args);

        return f;
    }

    /**
     * When creating, retrieve this instance‘s number from its arguments.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mNum = getArguments() != null ? getArguments().getInt("num") : 1;
    }

    /**
     * The Fragment‘s UI is just a simple text view showing its
     * instance number.
     */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.hello_world, container, false);
        View tv = v.findViewById(R.id.text);
        ((TextView)tv).setText("Fragment #" + mNum);
        tv.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb));
        return v;
    }
}

     现在我们可以写一个方法,这个方法可以创建一个新的Fragment实例,可以替换任何现在正在展示给用户的、可见的Fragment实例,并且可以将这些替换变化压到对应的后退栈中去。具体怎样实现这个方法,如下所示:

void addFragmentToStack() {
    mStackLevel++;

    // Instantiate a new fragment.
    Fragment newFragment = CountingFragment.newInstance(mStackLevel);

    // Add the fragment to the activity, pushing this transaction
    // on to the back stack.
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    ft.replace(R.id.simple_fragment, newFragment);
    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
    ft.addToBackStack(null);
    ft.commit();
}

    在每次调用上面的方法后,一个新的条目会加到对应的栈里面,当用户点击返回的时候,会从栈里面弹出上一次保存的视图

AT-Fragment

标签:

原文地址:http://www.cnblogs.com/codekk/p/5468283.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!