android基礎學習-android篇day17-Android Fragment(碎片)基本使用

發條魚發表於2018-09-23

轉:https://www.cnblogs.com/purediy/p/3276545.html

轉:https://blog.csdn.net/ash_zheng/article/details/5140535 

   Fragment一般是宿主Activity UI的一部分或一種行為,作為Activity的整個View Hierarchy的一部分嵌入。我們可以將多個Fragment組合到一個Activity中建立一個多面介面,也可以在多個Activity中重用一個Fragment。

       Fragment概述

       我們可以把Fragment視為模組化的一段Activity,它具有自己的生命週期,接收它自己的事件,並可以在activity執行時被新增或刪除。

       Fragment不能獨立存在,它必須嵌入到activity中,而且Fragment的生命週期直接受所在的activity的影響

例如:當activity暫停時,它擁有的所有的Fragment們都暫停了,當activity銷燬時,它擁有的所有Fragment們都被銷燬。然而,當activity執行時(在onResume()之後,onPause()之前),你可以單獨地操作每個Fragment,比如新增或刪除它們。當你在執行上述針對Fragment的事務時,你可以將事務新增到一個棧中,這個棧被activity管理,棧中的每一條都是一個Fragment的一次事務。有了這個棧,就可以反向執行Fragment的事務,這樣就可以在Fragment級支援“返回”鍵(向後導航)。

       當向activity中新增一個Fragment時,它須置於ViewGroup控制元件中,並且需定義Fragment自己的介面。你可以在layoutxml檔案中宣告Fragment,元素為:<fragment>;也可以在程式碼中建立Fragment,然後把它加入到ViewGroup控制元件中。然而,Fragment不一定非要放在activity的介面中,它可以隱藏在後臺為actvitiy工作。

       如何使用Fragment

       接下來講如何使用fragment,包括fragment在加入activity的後退棧中時如何保持自己的狀態,如何與activity以及其它fragment們共享事件,如何顯示在activity的動作欄,等等。

       Android從3.0開始引入fragment,主要是為了支援更動態更靈活的介面設計,比如在平板上的應用。平板機上擁有比手機更大的螢幕空間來組合和互動介面元件們。Fragment使你在做那樣的設計時,不需應付view樹中複雜的變化。通過把activity的layout分成fragment,你可以在activity執行時改變它的樣子,並且可以在activity的後退棧中儲存這些改變。

       例如:寫一個讀新聞的程式,可以用一個fragment顯示標題列表,另一個fragment顯示選中標題的內容,這兩個fragment都在一個activity上,並排顯示。那麼這兩個fragment都有自己的生命週期並響應自己感興趣的事件。於是,不需再像手機上那樣用一個activity顯示標題列表,用另一個activity顯示新聞內容;現在可以把兩者放在一個activity上同時顯示出來。如下圖:

       Fragment必須被寫成可重用的模組。因為fragment有自己的layout,自己進行事件響應,擁有自己的生命週期和行為,所以你可以在多個activity中包含同一個Fragment的不同例項。這對於讓你的介面在不同的螢幕尺寸下都能給使用者完美的體驗尤其重要。比如你可以在程式執行於大螢幕中時啟動包含很多fragment的activity,而在執行於小螢幕時啟動一個包含少量fragment的activity。

       舉個例子--還是剛才那個讀新聞的程式-當你檢測到程式執行於大螢幕時,啟動activityA,你將標題列表和新聞內容這兩個fragment都放在activityA中;當檢測到程式執行於小螢幕時,還是啟動activityA,但此時A中只有標題列表fragment,當選中一個標題時,activityA啟動activityB,B中含有新聞內容fragment。

兩種方式加入Fragment

首先建立 Demo3Fragment 繼承 Fragment 類, 併為其建立xml佈局檔案fragment_demo3.xml

package com.ashzheng.studydemo.demo3;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.ashzheng.studydemo.R;

public class Demo3Fragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
                     //(你要載入的佈局,viewGrounp,是否附件到佈局檔案的根檢視)
        View view = inflater.inflate(R.layout.fragment_demo3, container, false);

        return view;
    }
}

MainActivity.java中 

 //按鈕點選彈出fragment
    public void fragmentDemo(View view){
        startActivity(new Intent(this,Demo3Fragment.class));

    }

1. 通過XML標籤

在activity的fragment_demo3.xml佈局檔案中新增節點

    <fragment
            android:id="@+id/demo3_fg"
            android:name="com.ashzheng.studydemo.demo3.Demo3Fragment"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"/>
<Button   
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="fragment的基本使用"
 android:onClick="fragmentDemo"/>

2. 通過程式碼動態建立

  1. 新建一個xml佈局fragment_demo4.xml,並繫結到一個新的Activity中(Demo3Activity),此布xml佈局檔案中新增容器檢視(可以是任意的ViewGroup),用來裝fragment

  2. 設定一個Button用來實現動態新增效果

  3. <FrameLayout
        android:id="@+id/demo3_layout"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>
    <Button   
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:text="add"
     android:onClick="add"/>
  4. 在activity中動態新增fragment 

    package com.ashzheng.studydemo.demo3;
    
    import android.app.Activity;
    import android.app.FragmentManager;
    import android.app.FragmentTransaction;
    import android.os.Bundle;
    import com.ashzheng.studydemo.R;
    public class Demo3Activity extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_demo3);
        }
    //給按鍵新增點選事件,每次點選新增一個fragment到容器裡面去
    public void add(View view){
     FragmentManager fragmentManager = getFragmentManager();
            //通過fragmentManager物件去開啟一個事務
            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
            //要將fragment新增到哪個容器ViewGroup中去,裝fragment的具體物件
            fragmentTransaction.add(R.id.demo3_layout, new Demo3Fragment(),"fragmentTag");
            fragmentTransaction.commit();//將操作提交
    }
    }
    

5. 在activity中動態remove移除fragment

private FragmentManager mfragmentManager;
//移除fragment操作
public void remove(View view){
        mfragmentManager = getFragmentManager();
        //通過fragmentManager物件去開啟一個事務
         FragmentTransaction mfragmentTransaction = mfragmentManager.beginTransaction();
        //remove有兩種一個根據findFragmentById 容器的id 另一種是findFragmentByTag  
        //在add方法中有第三個引數就是Tag     
        fragmentTransaction.remove(mfragmentTransaction.findFragmentByTag("fragment"));
        fragmentTransaction.commit();//將操作提交
}

替換(replace)fragment操作 

public void replace(View view){
        //通過fragmentManager物件去開啟一個事務
        mfragmentTransaction = fragmentManager.beginTransaction();
        //要將fragment新增到哪個容器ViewGroup中去,裝你要替換的新的fragment的具體物件 
        fragmentTransaction.replace(R.id.demo3_layout, new AnotherDemo3Fragment());
        fragmentTransaction.commit();//將操作提交
}

fragment隱藏(hide)和顯示(show)操作 :

public void hide(View view){
        //通過fragmentManager物件去開啟一個事務
        mfragmentTransaction = fragmentManager.beginTransaction();
        //mfragmentTransaction查詢你原先帶Tag的fragment進行隱藏
        fragmentTransaction.hide(mfragmentTransaction.findFragmentByTag("fragment"));
        fragmentTransaction.commit();//將操作提交
}

public void show(View view){
        //通過fragmentManager物件去開啟一個事務
        mfragmentTransaction = fragmentManager.beginTransaction();
        //mfragmentTransaction查詢你原先帶Tag的fragment進行顯示
        fragmentTransaction.show(mfragmentTransaction.findFragmentByTag("fragment"));
        fragmentTransaction.commit();//將操作提交
}

Fragment操作區別

  • Add:往容器中新增一個Fragment

  • Replace:替換容器中的一個Fragment(先移除後新增)

  • Hide/Show:純粹的隱藏與顯示,不移除

  • Attach/Detach:從佈局上移除,但是儲存到快取佇列中,不會被測量;但可重用

  • Remove:直接移除掉,並不快取 

Fragment的傳值 

新建一個Fragment1.java和fragment1_activity.xml用來做第一個fragment

Fragment1中實現在EditText中輸入資料,點選Button進行傳輸到第二個介面TextView中

public class Fragment1 extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view=inflater.inflate(R.layout.fragment1_activity,container,false);
        Button bt=view.findViewById(R.id.fragment1_bt);
       final EditText et=view.findViewById(R.id.fragment1_et);
        //新增監聽事件
        bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //新建Fragment物件
                Fragment2 fragment2=new Fragment2();
                Bundle bundle=new Bundle();
                bundle.putString("data",et.getText().toString());
                fragment2.setArguments(bundle);//傳遞資料
                //點選後替換掉原來的fragment
                getFragmentManager().beginTransaction().replace(R.id.fragment_viewGroup,fragment2).commit();
            }
        });
        return view;

    }
}

Fragment2.java和fragment2_activity.xml

public class Fragment2 extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
      View view=inflater.inflate(R.layout.fragment2_activity,container,false);
        TextView tv=view.findViewById(R.id.fragment2_tv);
        tv.setText(getArguments().getString("data"));//通過key值獲取從Fragment1傳來的資料

        return view;
    }
}

點選彈出第一個Fragment,用fragment_viewGroup作為container容器,來裝Fragment1,展示第一個Fragment 

//點選傳遞按鈕彈出第一個fragment
    public void send(View view){
        getFragmentManager().beginTransaction().add(R.id.fragment_viewGroup,new Fragment1()).commit();

 效果圖

使用介面回撥實現Fragment到Activity之間互相傳值

  1. 在Fragment1中定義 一個內部回撥介面,在包含Fragment1的Acitivity中實現這個回撥介面,然後這個fragment就可以呼叫介面的方法,將資料傳遞給activity
  2. (Activity實現完介面,怎麼傳給Fragment1呢)當fragment被新增到Activity當中,fragment會呼叫它的onAttch()方法,檢查相應的Activity是否實現了Fragment內部的介面,對Activity進行型別轉換,然後賦值給我們Fragment對應的介面物件
  3. 當一個Fragment從Activity剝離的時候,fragment就會走到它生命週期的最後onDetach方法,這個時候要將傳遞進來的Activity物件釋放掉(防止記憶體洩漏)
public class Fragment1 extends Fragment {

    private View view;
    private ImageView backIcon;

    //2.1 定義用來與外部activity互動,獲取宿主acitivity
    private InfoCallback infoCallback;
    int num=0;

    //1定義了所有activity必須實現的介面方法
    public interface InfoCallback{
        void upadateCount(int num);
    }

(Activity實現完介面,怎麼傳給Fragment1呢)當fragment被新增到Activity當中,fragment會呼叫它的onAttch()方法,檢查相應的Activity是否實現了Fragment內部的介面,對Activity進行型別轉換,然後賦值給我們Fragment對應的介面物件

    //當Fragment被載入到activity的時候會回撥
    //這是最重要的程式碼,目的就是為介面賦值,使其不為空
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        if(activity instanceof InfoCallback){//如果宿主Activity實現了該介面
            //Fragment attach到Activity時,將Activity強轉為Callbacks儲存
            infoCallback= (InfoCallback) activity; //infoCallback 指向該Activity
        }
        else//如果activty沒有實現該介面
            throw new  IllegalArgumentException("activity must implements InfoCallback");
    }

 當一個Fragment從Activity剝離的時候,fragment就會走到它生命週期的最後onDetach方法,這個時候要將傳遞進來的Activity物件釋放掉(防止記憶體洩漏)


    @Override
    public void onDetach() {
        super.onDetach();
        infoCallback=null;
    }

在Fragment使用介面傳值給Activity:(根據自己的實際效果) 

 backIcon.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//fragment不被銷燬的情況下每點選一次按鈕,Activity就會呼叫一次upadateCount(num)
                num++;
                // 執行回撥,呼叫的activity所實現的upadateCount方法
                // 實現了將num傳到activity當中
                //此時的infoCallback已經是指向Activity的物件
                infoCallback.upadateCount(num);
            }
        });

在Activity當中實現這個介面


    //實現介面的activity可以利用fragment傳進來的資料
    @Override
    public void upadateCount(int num) {
        Log.e("介面傳進來的num",num+"");
    }

 

Fragment使用介面回撥實現兩個fragment在同一個Activity中傳值 

使用介面回撥的原因:為了不想Fragment3和Fragment4有相互的耦合

 

  • 首先,建立兩個Fragment(Fragment3、Fragment4)類繼承Fragment,並繫結相應的xml檔案
  • 在主介面的xml佈局檔案中,直接用xml的方式新增兩個fragment
  • 實現功能:Fragment3中的xml檔案有三個Button,Fragment4中有1個textview顯示Fragment點選按鈕後的相應按鈕內容
  • Fragment3想控制Fragment4的textview顯示內容,不是直接控制,而是將這個請求傳送給主介面Activity
  • 然後主介面將這個請求轉發給Fragment4去設定textview設定文字;這樣的傳送請求和請求轉發是通過介面回撥來實現的
  • 在Fragment4中有獨立的顯示文字的方法
  • 在Fragment3中在通過onAattch()方法將Activity初始化的時候就與Fragment3進行繫結
  • 在主介面Activity中獲取Fragment4物件並呼叫它的獨立方法,實現設定Fragment4要顯示的文字。
  • 簡單的來說:Fragment3和Fragment4各自做各自的事情,由Activity進行請求轉發。

FragmentValues3.java

public class FragmentValues3 extends Fragment implements View.OnClickListener{

    private IButton mIButton;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment3, container, false);
        Button button1 = (Button) view.findViewById(R.id.btn_1);
        Button button2 = (Button) view.findViewById(R.id.btn_2);
        Button button3 = (Button) view.findViewById(R.id.btn_3);
        button1.setOnClickListener(this);
        button2.setOnClickListener(this);
        button3.setOnClickListener(this);
        return view;
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            mIButton = (IButton) context;//初始化就繫結Fragment4
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onClick(View v) {
        mIButton.onFragmentBtnClick(((Button) v).getText().toString());
    }
}

FragmentValue4.java 

public class FragmentValues4 extends Fragment {

    TextView mTextView;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment4, container, false);
        mTextView = (TextView) view.findViewById(R.id.tv_value);
        return view;
    }

    public void setFragmentTextValue(String textValue) {
        mTextView.setText(textValue);
    }
}

在主介面的xml佈局檔案中直接新增fragment佈局。省略fragment3.xml和fragment4.xml的程式碼 


    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

      <!--id不可省略--->
           <fragment 
            android:id="@+id/frg3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:name="com.imooc.fragmentdemo.FragmentValues3"
            tools:layout="@layout/fragment3"/>

        <fragment
            android:id="@+id/frg4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:name="com.imooc.fragmentdemo.FragmentValues4"
            tools:layout="@layout/fragment4"/>

    </LinearLayout>

建立一個介面IButton.java


public interface IButton {

    void onFragmentBtnClick(String text);
}

 在主介面中實現這個介面,並獲取FragmentValue4的物件呼叫設定tv的文字的方法

ublic class FragmentValueActivity extends AppCompatActivity implements IButton{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_value);
        getSupportFragmentManager().beginTransaction().add(R.id.container, new FragmentValues1()).commit();
    }

    @Override
    public void onFragmentBtnClick(String text) {
        FragmentValues4 fragmentValues4 = (FragmentValues4) getSupportFragmentManager().findFragmentById(R.id.frg4);
        fragmentValues4.setFragmentTextValue(text);//呼叫FragmentValue4的方法設定文字
    }
}

 

Fragment的生命週期

 

2   生命週期分析

1. 當一個fragment被建立的時候,它會經歷以下狀態.

  • onAttach()
  • onCreate()
  • onCreateView()
  • onActivityCreated()

2. 當這個fragment對使用者可見的時候,它會經歷以下狀態。

  • onStart()
  • onResume()

3. 當這個fragment進入“後臺模式”的時候,它會經歷以下狀態。

  • onPause()
  • onStop()

4. 當這個fragment被銷燬了(或者持有它的activity被銷燬了),它會經歷以下狀態。

  • onPause()
  • onStop()
  • onDestroyView()
  • onDestroy() // 本來漏掉類這個回撥,感謝xiangxue336提出。
  • onDetach()

5. 就像activitie一樣,在以下的狀態中,可以使用Bundle物件儲存一個fragment的物件。

  • onCreate()
  • onCreateView()
  • onActivityCreated()

6. fragments的大部分狀態都和activitie很相似,但fragment有一些新的狀態。

  • onAttached() —— 當fragment被加入到activity時呼叫(在這個方法中可以獲得所在的activity)。
  • onCreateView() —— 當activity要得到fragment的layout時,呼叫此方法,fragment在其中建立自己的layout(介面)。
  • onActivityCreated() —— 當activity的onCreated()方法返回後呼叫此方法
  • onDestroyView() —— 當fragment中的檢視被移除的時候,呼叫這個方法。
  • onDetach() —— 當fragment和activity分離的時候,呼叫這個方法。

一旦activity進入resumed狀態(也就是running狀態),你就可以自由地新增和刪除fragment了。因此,只有當activity在resumed狀態時,fragment的生命週期才能獨立的運轉,其它時候是依賴於activity的生命週期變化的。

相關文章