Android 知識點回顧之 Fragment

Fragment是在API 11(Android3.0)引入的,爲了可以支持適配寬屏設備,提供靈活多變的UI設計。php

Fragment是嵌在Activity裏面可以交互的用戶界面,它的存在必須依賴於Activity,不能獨立存在。java

多個Activity能夠複用同一個Fragment。一個Activity能夠嵌入多個Fragment。android

Fragment的生命週期

Fragment的生命週期

Fragment的生命週期如上圖所示。api

加入Activity的話則如上圖所示。app

若是不加入Activity的生命週期的話,則爲: onAttach()->onCreate()->onCreateView()->onStart()->onResume()->Fragment處於活動狀態 onPause()->onStop()->onDestroyView()->onDestroy()->onDetach()ide

其中onDestroyView()->onCreateView(),發生此種狀況的條件是當Fragment從回退棧(back stack)返回時纔會發生。例如:Activity啓動了Fragment A,而後又啓動了FragmentB替換了Fragment A並把A添加到回退棧,當B返回後,退回到A,A的生命週期從onDestroyView()->onCreateView()開始。佈局

幾個回調方法

onCreate(),能夠在這裏進行初始化this

onCreateView(),開始對Fragment界面進行繪製,須要返回一個View,一般爲Fragment的佈局。spa

onPause(),當調用到此方法時,說明用戶正在離開Fragment,在這裏須要對數據和狀態進行保存設計

啓動:

啓動
宿主BaseActivity切換到後臺(Activity不可見):
宿主BaseActivity切換到後臺(Activity不可見)
宿主BaseActivity切換回前臺:
宿主BaseActivity切換回前臺
宿主BaseActivity銷燬:
宿主BaseActivity銷燬

Fragment的建立

一般的只須要繼承實現Fragment類,爲了保證兼容性,通常使用support包的Fragment。

public class BaseFragment extends Fragment{
  onCreate(){
  
  }
  @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_article_list, container, false);
    }
}
複製代碼

Fragment添加到Activity的兩種方法

一、經過在Activity的佈局中聲明fragment

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent">
    <fragment android:name="com.example.news.ArticleListFragment" android:id="@+id/list" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent" />
    <fragment android:name="com.example.news.ArticleReaderFragment" android:id="@+id/viewer" android:layout_weight="2" android:layout_width="0dp" android:layout_height="match_parent" />
</LinearLayout>
複製代碼

其中中的android:name屬性的值爲Fragment的類全名。

此時Activity可直接加載佈局中的Fragment

爲了給Fragment設定一個惟一的標識,可使用以下兩種方式:

  • android:id,指定id屬性爲惟一的ID
  • android:tag,指定tag屬性做爲惟一的標識,爲字符串類型

設置惟一標識能夠對數據恢復有幫助。

二、經過代碼動態添加Fragment

能夠經過指定Activity佈局中的ViewGroup佈局來放置Fragment。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
     ...
    <FrameLayout android:id="@+id/fl_fragment" android:layout_width="match_parent" android:layout_height="wrap_content"/>
     ...
</LinearLayout>
複製代碼

Activity建議使用擴展包中的FragmentActivity的子類

public class BaseActivity extends FragmentActivity{
  ...
  //獲取FragmentTransaction
  FragmentManager fragmentManager = getSupportFragmentManager();
   FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
  ...
  //添加Fragment
  BaseFragment fragment = new BaseFragment();
  fragmentTransaction.add(R.id.fl_fragment, fragment);
  fragmentTransaction.commit();
  ...
}
複製代碼

此外,還能夠經過FragmentTransaction的add的重載方法add(Fragment fragment,String tag)添加無界面的Fragment。此時的tag參數爲Fragment的標識ID,要取惟一值。而且不須要實現onCreateView()。因爲此時Fragment無界面,因此不會對Activity的當前界面形成影響。

若是須要獲取到無界面Fragment,可使用fragmentManager.findFragmentByTag(tag)。

Fragment的管理

可使用FragmentTransaction對Fragment進行管理,進行

  • 添加 add(),
  • 刪除 remove(),
  • 替換 replace()
  • 加入回退棧 addToBackStack()

操做完後必須調用commit()纔會生效。

一次典型的操做:

Fragment newFragment = new BaseFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
//替換成新的Fragment
transaction.replace(R.id.fragment_container, newFragment);
//把當前的FragmentTransaction加入到回退棧
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
複製代碼

transaction.addToBackStack(null);的做用是把當前FragmentTransaction加入到回退棧,當執行此代碼並commit後,則點擊回退鍵Activity不會直接結束,而是返回到Activity界面

有幾個點須要注意:

  • 一個FragmentTransaction實例只能commit一次,當重複commit時會直接拋出異常。
  • 當調用addToBackStack();方法時,會把當前FragmentTransaction實例存入回退棧,能夠把Fragment所依賴的Activity看作棧的根。
  • addToBackStack(String name)中的參數name能夠爲null,主要做用是標識此FragmentTransaction實例,用來作區分。
  • add()和replace()的區別。add()會添加新的Fragment實例進去,不會對以前的Fragment實例進行銷燬,後面添加的會覆蓋前面添加的,不過若是add相同的Fragment實例,則會拋出異常。replace()會先把對應佈局上全部的Fragment實例銷燬掉,而後再把新的Fragment實例添加進去。

下圖爲Fragment的返回棧,當前的FragmentTransaction實例調用addToBackStack()並commit就會入棧:

Fragment的返回棧,當前的FragmentTransaction實例調用addToBackStack()並commit就會入棧

返回的話會從棧頂開始返回,最上層的會銷燬

Fragment之間,以及和Activity間的數據交互

一個Fragment的實例是和它所綁定的Activity實例牢牢關聯的。Fragment能夠經過getActivity()獲取它所關聯綁定的Activity。

和Activity交互

  • Activity能夠經過Fragment的構造方法把數據傳遞給
  • 可經過getActivity()獲取到Activity的實例
  • 經過回調接口能夠把Fragment的事件結果傳遞給Activity

Fragment間的交互

  • Fragment間的交互能夠經過Activity做中轉,
  • 能夠直接在Fragment經過getFragmentManager()獲取到FragmentManager

官網例子

寬屏佈局 res/layout-land/fragment_layout.xml,用於大屏幕設備,好比平板電腦等:

<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>
複製代碼

手機佈局 res/layout/fragment_layout.xml:

<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>
複製代碼

TitlesFragment 的實現

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

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

        // 添加文章列表
        setListAdapter(new ArrayAdapter<String>(getActivity(),
                android.R.layout.simple_list_item_activated_1, Shakespeare.TITLES));

        // 檢查是否上圖左邊的那種佈局(寬屏模式)
        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);
    }

    /** * 若是是寬屏模式的話,則直接在右邊的Fragment更新內容 * 若是不是寬屏模式,則跳轉到新的Activity進行展現 */
    void showDetails(int index) {
        mCurCheckPosition = index;

        if (mDualPane) {
            getListView().setItemChecked(index, true);

            DetailsFragment details = (DetailsFragment)
                    getSupportFragmentManager().findFragmentById(R.id.details);
            if (details == null || details.getShownIndex() != index) {               
                details = DetailsFragment.newInstance(index);
              
                FragmentTransaction ft = getSupportFragmentManager().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 {
            Intent intent = new Intent();
            intent.setClass(getActivity(), DetailsActivity.class);
            intent.putExtra("index", index);
            startActivity(intent);
        }
    }
}
複製代碼

DetailsFragment的實現:

public static class DetailsFragment extends Fragment {
  
    public static DetailsFragment newInstance(int index) {
        DetailsFragment f = new DetailsFragment();
       
        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;
    }
}
複製代碼

若是是手機的狀況,跳轉到新的Activity:

public static class DetailsActivity extends FragmentActivity {

    @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());
            getSupportFragmentManager().beginTransaction().add(android.R.id.content, details).commit();
        }
    }
}
複製代碼

--End--

相關文章
相關標籤/搜索