Fragment 簡介 基礎知識 總結 MD


Markdown版本筆記 我的GitHub首頁 我的博客 我的微信 我的郵箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

目錄

Fragment 知識點總結

FragmentActivity 簡介

Fragment是3.0以后的東西,為了在低版本中使用Fragment就要用到android-support-v4兼容包,而Fragmentactivity就是這個兼容包里面的,它提供了操作Fragment的一些方法,其功能跟3.0及以后的版本的Activity的功能一樣。

下面是API中的原話:

FragmentActivity is a special activity provided in the Support Library to handle fragments on system versions older than API level 11. If the lowest system version you support is API level 11 or higher, then you can use a regular Activity.

一些區別

  • FragmentActivity繼承自Activity,用來解決android3.0之前沒有fragment的問題,所以在3.0前使用Fragment時需要導入support包,同時讓Activity繼承FragmentActivity,這樣就能在Activity中使用Fragment了。
  • 在3.0之后你直接繼承自Activity就可以使用android.app.Fragment,但目前Google推薦使用support包中的Fragment。
  • 兩者獲得FragmentManager的方式不同,3.0之前使用FragmentActivity時:getSupportFragmentManager(),3.0以后使用Activity時:getFragmentManager()
  • 一定要保證你的Activity中和你的Fragment中導的包是一致的!

Fragment 和 Activity 的通訊

因為Fragment都是依附於Activity的,所以兩者通信並不復雜,大概歸納為:

  • 如果你Activity中包含此Fragment的引用,可以通過此引用直接訪問Fragment所有的public方法
  • 如果Activity中不保存此Fragment的引用,由於每個Fragment都有唯一的一個TAG或者ID,可以通過findFragmentByTag或者findFragmentById獲得任何Fragment實例
  • 在Fragment中可以通過getActivity得到當前綁定的Activity的實例

如果在Fragment中需要Context,可以通過調用getContext()或getActivity()或onAttach中的參數context獲取,如果該Context需要在Activity被銷毀后還存在,則使用getActivity().getApplicationContext()。

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    Log.i("bqt", "【onAttach】" + (getContext() == getActivity()) + "-" + (context == getActivity())); //true-true
}

使用靜態 Fragment

雖然簡單,但有兩點必須滿足:

  • 要通過android:name屬性指定所引用的fragment的全類名
  • 必須給fragment設置android:id,即使完全用不到
<fragment
    android:id="@+id/fragment"
    android:name="com.bqt.fragment.MyFragment"
    android:layout_width="match_parent"
    android:layout_height="45dp" />

FragmentManager簡介

FragmentManager 可以做的事情:

  • 使用 findFragmentById 或 findFragmentByTag 獲取 activity 中存在的 fragment
  • 使用 popBackStack() 將 fragment 從后台堆棧中彈出 (模擬用戶按下BACK 命令)
  • 使用 addOnBackStackChangeListener() 注冊一個監聽后台堆棧變化的 listener
  • 開啟一個事務 fm.benginTransatcion()

Fragment嵌套Fragment時,里面的Fragment要用getChildFragmentManager獲取到的FragmentManager

帶 RecyclerView 的 Fragment 模板代碼

public class MainBeaAppStoreFragment extends BaseFragment implements BaseQuickAdapter.OnItemClickListener {
    private Context mContext;

    private RecyclerView mRecyclerView;
    private BeaAppStoreAdapter mAdapter;
    private ArrayList<BeaAppItemInfo> list;
    private static int REQUESTCODE_LOGIN_TONEWPAGE = 20094;

    public static MainBeaAppStoreFragment newInstance(ArrayList<BeaAppItemInfo> list) {
        MainBeaAppStoreFragment officeSimpleFragment = new MainBeaAppStoreFragment();
        Bundle args = new Bundle();
        args.putParcelableArrayList("list", list);
        officeSimpleFragment.setArguments(args);

        return officeSimpleFragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mContext = getActivity();
        list = getArguments() != null ? getArguments().getParcelableArrayList("list") : new ArrayList<>();
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        View view = inflater.inflate(R.layout.bea_fragment_main_appstore, container, false);
        mRecyclerView = view.findViewById(R.id.rv_group);
        return view;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        int spanCount = list.size() >= 4 ? 4 : list.size();
        mRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), spanCount));
        //mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.HORIZONTAL));
        mRecyclerView.addItemDecoration(new GridItemDecoration.Builder()
                .spanCount(spanCount)//行數或列數
                .spaceSize(SystemUtil.dp2px(15f))//行列間距大小
                .mDivider(new ColorDrawable(Color.TRANSPARENT))
                .includeLREdge(false).includeTBEdge(false)//是否包含上下左右邊界
                .drawLREdge(false).drawTBEdge(false)//是否繪制上下左右邊界
                .build());
        mAdapter = new BeaAppStoreAdapter(list);
        mRecyclerView.setAdapter(mAdapter);
        mAdapter.setOnItemClickListener(this);
    }

    @Override
    public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        CherryCodeLogUtil.i("bqt", "【onActivityResult】" + requestCode);
        //登錄成功進行跳轉
        if (requestCode == REQUESTCODE_LOGIN_TONEWPAGE && resultCode == Activity.RESULT_OK) {
            WebviewActivity.launche(mContext, true, "index.html", toNewPageSuffixUrl);
        }
    }
}

子 Fragment 的 onActivityResult 方法不被調用問題的解決方法

出現此問題的原因:程序bug,沒有處理嵌套Fragment的情況,也就是說回調只到第一級Fragment,就沒有繼續分發。
我們可以實現一個自己的FragmentActiviy,來實現繼續分發,如下:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   super.onActivityResult(requestCode, resultCode, data);
   CherryCodeLogUtil.i("bqt", "【onActivityResult】" + requestCode);
   List<Fragment> fragments = getSupportFragmentManager().getFragments();
   if (fragments != null && fragments.size() > 0) {
      for (Fragment fragment : fragments) {
         handleChildFragmentResult(fragment, requestCode, resultCode, data);
      }
   }
}

/**
 * 遞歸調用,對所有的子Fragment生效
 */
private void handleChildFragmentResult(Fragment fragment, int requestCode, int resultCode, Intent data) {
   fragment.onActivityResult(requestCode, resultCode, data);//調用每個Fragment的onActivityResult
   List<Fragment> childFragments = fragment.getChildFragmentManager().getFragments(); //找到第二層Fragment
   if (childFragments != null && childFragments.size() > 0) {
      for (Fragment childFragment : childFragments) {
         handleChildFragmentResult(childFragment, requestCode, resultCode, data);
      }
   }
}

通過動態添加子 Fragment 的方式簡化復雜的 Activity 或 Fragment

根據情況使用【getFragmentManager】【getSupportFragmentManager】【getChildFragmentManager】

private void addFragment() {
   Fragment fragment1 = fragmentManager.findFragmentById(R.id.fl_search);
   Fragment fragment2 = fragmentManager.findFragmentById(R.id.fl_appstore);
   Fragment fragment3 = fragmentManager.findFragmentById(R.id.fl_pic);

   FragmentTransaction transaction = fragmentManager.beginTransaction();
   if (fragment1 == null) transaction.add(R.id.fl_search, SearchFragment.newInstance(name));
   if (fragment2 == null) transaction.add(R.id.fl_appstore, AppStoreFragment.newInstance());
   if (fragment3 == null) transaction.add(R.id.fl_pic, PicFragment.newInstance(picList));
   transaction.commit();
}

DialogFragment 使用簡介

在 DialogFragment 產生之前,我們創建對話框一般采用 AlertDialog 和 Dialog,DialogFragment 在 android 3.0 時被引入,是一種特殊的Fragment,用於在 Activity 的內容之上展示一個模態的對話框。

public class DialogFragment extends Fragment implements OnCancelListener, OnDismissListener {}
interface OnCancelListener { void onCancel(DialogInterface dialog); }
interface OnDismissListener { void onDismiss(DialogInterface dialog); }

傳統的 AlertDialog 在屏幕旋轉時,既不會保存用戶輸入的值,對話框也不會重建,還可能會報異常。而使用 DialogFragment 來管理對話框,當旋轉屏幕和按下后退鍵時可以更好的管理其生命周期(自動重建),它和 Fragment 有着基本一致的生命周期。

使用 DialogFragment 需要實現onCreateView或者onCreateDIalog方法,onCreateView即使用自定義的 xml 布局文件展示 Dialog,onCreateDialog 則可利用 AlertDialog 或者 Dialog 創建出 Dialog。

DialogFragment 也允許開發者把 Dialog 作為內嵌的組件進行重用,類似 Fragment 可以在大屏幕和小屏幕顯示出不同的效果(需要向Fragment那樣,在onCreateView中返回Fragment的視圖,而不應在onCreateDialog中創建Dialog的視圖)。

DialogFragment 使用案例

調用方式:

MyDialogFragment.newInstance(position).show(fragmentManager, titles[position]);
fragmentManager.beginTransaction().add(R.id.id_container, MyDialogFragment.newInstance(position), titles[position]).commit();

DialogFragment實現類:

public class MyDialogFragment extends DialogFragment {
    public static final String ARGUMENT = "returnType";
    private int returnType = 0;

    public static MyDialogFragment newInstance(int returnType) {
        MyDialogFragment fragment = new MyDialogFragment();
        Bundle bundle = new Bundle();
        bundle.putInt(ARGUMENT, returnType);
        fragment.setArguments(bundle);
        return fragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle bundle = getArguments();
        returnType = bundle != null ? bundle.getInt(ARGUMENT) : 0;
        Log.i("bqt", "【onCreate】" + returnType);
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        Log.i("bqt", "【*********************onCreateDialog*********************】");//在 onCreateView 之前回調
        if (returnType == 0) {
            return super.onCreateDialog(savedInstanceState);
        } else {
            View rootView = LayoutInflater.from(getContext()).inflate(R.layout.fragment_tab, null);
            TextView tv = rootView.findViewById(R.id.tv);
            tv.setText(returnType + ":" + new SimpleDateFormat("HH:mm:ss SSS", Locale.getDefault()).format(new Date()));
            tv.setBackgroundColor(0xFF000000 + new Random().nextInt(0xFFFFFF));

            if (returnType == 1) {
                AlertDialog alertDialog = new AlertDialog.Builder(getContext()).setView(rootView).create();
                alertDialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);//設置AlertDialog背景透明效果
                return alertDialog;
            } else {
                Dialog dialog = new Dialog(getContext());
                dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);//設置Dialog沒有標題。需在setContentView之前設置,在之后設置會報錯
                dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);//設置Dialog背景透明效果
                dialog.setContentView(rootView);
                return dialog;
            }
        }
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Log.i("bqt", "【onCreateView】Dialog是否為null:" + (getDialog() == null));
        if (returnType == 0) {
            View rootView = inflater.inflate(R.layout.fragment_tab, container, false);
            TextView tv = rootView.findViewById(R.id.tv);
            tv.setText(returnType + ":" + new SimpleDateFormat("HH:mm:ss SSS", Locale.getDefault()).format(new Date()));
            tv.setBackgroundColor(0xFF000000 + new Random().nextInt(0xFFFFFF));
            return rootView;
        } else {
            return null;
        }
    }

    @Override
    public void onCancel(DialogInterface dialog) {
        super.onCancel(dialog);
        Log.i("bqt", "【onCancel】");//在 onDismiss 之前回調
    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        Log.i("bqt", "【*********************onDismiss*********************】"); //在 onPause 之前回調
    }
}

如何正確的判斷當前的Fragment是否對用戶可見

如何正確的判斷當前的Fragment是否對用戶可見

  • onResume:適用於單一Activity嵌套單一Fragment的場景,跟隨載體Activity的生命周期,能得出正確的判斷
  • onHiddenChanged:適用於通過FragmentManager添加多個Fragment並且在點擊切換Fragment的場景,通過FragmentManager的hide和show方法觸發,能得出正確的判斷
  • setUserVisibleHint:適用於ViewPager中嵌套Fragment的場景,在PagerAdapter中左右滑動時通過設置當前顯示的Fragment會觸發該方法,能得出正確的判斷


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM