概述

Fragment是 Android 3.0(API 11)引入的一种设计,用于大屏幕的设备。

  • Fragment依托于Activity,受宿主Activity生命周期的影响。但它也有自己的生命周期。
  • Fragment可重复使用,一个Activity可以有多个Fragment。一个Fragment可以被多个Acitivy使用。
  • Fragment在Acitivity运行时可以动态的加载或删除。在不同分辨率设备或者横竖屏时 调用对应的Fragment布局就能很好的实现设备的适配,提升用户体验。

注:

  • AndroidX出来后,使用的Fragment库就在androidx中,下面的例子都是androidx的。
  • Fragment添加到Activity,一种通过<fragment>元素插入到布局中,另一种通过代码插入到布局中的<FrameLayout>。下面的例子就包含这两种。
  • savedInstanceState这个参数在很多时候是很有用的,在例子中的AnimeDetailFragment中简单的演示了它的使用。
  • 注意不同的设备适配合适的布局,能够很好的提升用户体验。

生命周期

如图,比较详细,稍微了解点或者熟悉Activity的都能直接看懂,下面例子中也通过log大致显示了这一过程。

基本使用

先看下例子的效果,这个例子只有一个Activity 和 两个Fragment组成:

上述效果,Activity布局中 是两个fragment,左侧的标题栏和右侧的详情界面。左侧的标题使用的<fragment>元素直接插入的,右侧的详情界面 通过点击动态加载的。

Activity布局文件activity_main.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" > <fragment android:name="com.flx.testfragment.AnimeTitleFragment"
android:id="@+id/anime_title_fragment"
android:layout_width="120dp"
android:layout_height="match_parent" /> <FrameLayout android:id="@+id/anime_detail_fragment_layout"
android:layout_toRightOf="@id/anime_title_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00BCD4" />
</RelativeLayout>

Activity很简单

这里继承了androidx.fragment.app.FragmentActivity。

package com.flx.testfragment;

import android.os.Bundle;
import android.util.Log;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentTransaction; public class MainActivity extends FragmentActivity implements AnimeTitleFragment.OnAmimeSelectedListener { private static final String TAG = "flx_fragment"; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
} @Override
public void onAnimeSelected(int position) {
Log.d( TAG, "onArticleSelected: position=" + position );
//创建一个详情界面(detailFragment),并出入参数position
AnimeDetailFragment detailFragment = new AnimeDetailFragment();
Bundle args = new Bundle();
args.putInt(AnimeDetailFragment.ARG_POSITION, position);
detailFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); //用detailFragment替换anime_detail_fragment_layout中的内容
transaction.replace(R.id.anime_detail_fragment_layout, detailFragment);
//将事务添加到返回栈中,允许用户通过按返回按钮返回上个fragment状态。该返回栈由Activity管理。
transaction.addToBackStack(null); //提交事务
transaction.commit();
}
}

AnimeTitleFragment.java:

在布局文件中通过<fragment>元素插入的,在Activity创建时即生成,即效果图中的左侧标题栏。这里通过继承ListFragment实现,数据和布局使用ArrayAdapter实现关联(Adapter不太了解的,可以参考我对应的其他文章),同时这里的layout考虑了一点兼容性。具体代码如下:

package com.flx.testfragment;

import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView; import androidx.annotation.Nullable;
import androidx.fragment.app.ListFragment; public class AnimeTitleFragment extends ListFragment {
AnimeTitleFragment.OnAmimeSelectedListener mCallback; //Activity实现这个接口
public interface OnAmimeSelectedListener {
void onAnimeSelected(int position);
} @Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// We need to use a different list item layout for devices older than Honeycomb
int layout = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
android.R.layout.simple_list_item_activated_1 : android.R.layout.simple_list_item_1; String[] anime_names = this.getResources().getStringArray( R.array.anime_name );
//使用ArrayAdapter显示所有标题数据
setListAdapter(new ArrayAdapter<String>(getActivity(), layout, anime_names));
} @Override
public void onAttach(Context context) {
super.onAttach(context);
// 确保Activity实现该接口
try {
mCallback = (AnimeTitleFragment.OnAmimeSelectedListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement OnAmimeSelectedListener");
}
} @Override
public void onListItemClick(ListView l, View v, int position, long id) {
//传入position数据,即选中了那个
mCallback.onAnimeSelected(position); getListView().setItemChecked(position, true);
}
}

AnimeDetailFragment.java:

效果图中的详情界面,根据点击不同的标题显示不同的内容。这里简单使用了savedInstanceState,合理的使用对于某些场景是很有帮助的。具体代码:

package com.flx.testfragment;

import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; public class AnimeDetailFragment extends Fragment {
private static final String TAG = "flx_AnimeDetailFragment"; public static final String ARG_POSITION = "position";
private int mCurrentPosition = -1; public static String[] ANIME_NAMES;
public static String[] ANIME_AUTHORS;
public static int[] COVER_IMGS = {R.drawable.hzw1, R.drawable.jjdjr1, R.drawable.hyrz1,
R.drawable.zchzt1, R.drawable.qsmy1, R.drawable.xyj1, R.drawable.hlw1};
private TextView mAnimeName;
private TextView mAnimeAuthor;
private ImageView mCoverImg; @Override
public void onAttach(Context context) {
Log.d( TAG, "onAttach: " + this );
super.onAttach( context );
} @Override
public void onCreate(@Nullable Bundle savedInstanceState) {
Log.d( TAG, "onCreate: " + this );
super.onCreate( savedInstanceState );
} @Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Log.d( TAG, "onCreateView: " + this );
ANIME_NAMES = getActivity().getResources().getStringArray( R.array.anime_name );
ANIME_AUTHORS = getActivity().getResources().getStringArray( R.array.anime_author ); //savedInstanceState,这个参数很有用,在activity被重新创建时(如旋转屏幕)等,恢复之前的状态。
if (savedInstanceState != null) {
mCurrentPosition = savedInstanceState.getInt(ARG_POSITION);
} View view = inflater.inflate( R.layout.anime_detail_view, container, false );
mAnimeName = view.findViewById( R.id.anime_name_txt );
mAnimeAuthor = view.findViewById( R.id.anime_author_txt );
mCoverImg = view.findViewById( R.id.anime_cover_img );
return view;
} @Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
Log.d( TAG, "onActivityCreated: " + this);
super.onActivityCreated( savedInstanceState );
} @Override
public void onStart() {
Log.d( TAG, "onStart: " + this );
super.onStart(); Bundle args = getArguments();
if (args != null) {
//根据传入的参数ARG_POSITION 更新
updateAnimeDetailView(args.getInt(ARG_POSITION));
} else if (mCurrentPosition != -1) {
// Set article based on saved instance state defined during onCreateView
//根据保存的状态中信息 更新
updateAnimeDetailView(mCurrentPosition);
}
} @Override
public void onResume() {
Log.d( TAG, "onResume: " + this );
super.onResume();
} @Override
public void onSaveInstanceState(Bundle outState) {
Log.d( TAG, "onSaveInstanceState: " + this );
super.onSaveInstanceState(outState); //保存fragment当前状态的信息。
outState.putInt(ARG_POSITION, mCurrentPosition);
} public void updateAnimeDetailView(int position) {
mAnimeName.setText(ANIME_NAMES[position]);
mAnimeAuthor.setText(ANIME_AUTHORS[position]);
mCoverImg.setImageResource(COVER_IMGS[position]);
mCurrentPosition = position;
} @Override
public void onPause() {
Log.d( TAG, "onPause: " + this );
super.onPause();
} @Override
public void onStop() {
Log.d( TAG, "onStop: " + this );
super.onStop();
} @Override
public void onDestroyView() {
Log.d( TAG, "onDestroyView: " + this );
super.onDestroyView();
} @Override
public void onDestroy() {
Log.d( TAG, "onDestroy: " + this );
super.onDestroy();
} @Override
public void onDetach() {
Log.d( TAG, "onDetach: " + this );
super.onDetach();
}
}

一些资源文件:

AnimeDetailFragment的布局文件:anime_detail_view.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"> <ImageView android:id="@+id/anime_cover_img"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_margin="10dp"
android:scaleType="fitXY" /> <TextView android:id="@+id/anime_name_txt"
android:layout_width="match_parent"
android:layout_height="30dp"
android:gravity="center"
android:textStyle="bold"
android:textSize="20sp"/> <TextView android:id="@+id/anime_author_txt"
android:layout_width="match_parent"
android:layout_height="30dp"
android:gravity="center"
android:textStyle="italic"
android:textSize="15sp"/>
</LinearLayout>

strings.xml:

<array name="anime_name">
<item>海贼王</item>
<item>进击的巨人</item>
<item>火影忍者</item>
<item>斩赤红之瞳</item>
<item>秦时明月</item>
<item>西游记</item>
<item>葫芦娃</item>
</array> <array name="anime_author">
<item>尾田荣一郎</item>
<item>谏山创</item>
<item>岸本齐史</item>
<item>タカヒロ</item>
<item>玄机科技</item>
<item>央视</item>
<item>上海美术电影</item>
</array>

 生命周期流程

最后,看下log,看看fragment的生命周期

操作:点击第一个标题(海贼王),然后点击第二个标题(进击的巨人),最后点击返回键,下面log的过程 与 生命周期那个图 是一致的,不太明白的,可以仔细看下如下log。

//点击第一个标题,创建了fragment1(52888e9)
2020-03-03 16:22:48.316 20442-20442/com.flx.testfragment D/flx_fragment: onArticleSelected: position=0
2020-03-03 16:22:48.344 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onAttach: AnimeDetailFragment{52888e9 #1 id=0x7f090020}
2020-03-03 16:22:48.344 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onCreate: AnimeDetailFragment{52888e9 #1 id=0x7f090020}
2020-03-03 16:22:48.347 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onCreateView: AnimeDetailFragment{52888e9 #1 id=0x7f090020}
2020-03-03 16:22:48.350 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onActivityCreated: AnimeDetailFragment{52888e9 #1 id=0x7f090020}
2020-03-03 16:22:48.350 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onStart: AnimeDetailFragment{52888e9 #1 id=0x7f090020}
2020-03-03 16:22:48.357 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onResume: AnimeDetailFragment{52888e9 #1 id=0x7f090020}
//点击第二个标题,创建了fragment2(371a2d2), fragment1(52888e9)进入生命周期到onDestroyView
2020-03-03 16:22:51.346 20442-20442/com.flx.testfragment D/flx_fragment: onArticleSelected: position=1
2020-03-03 16:22:51.349 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onAttach: AnimeDetailFragment{371a2d2 #2 id=0x7f090020}
2020-03-03 16:22:51.349 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onCreate: AnimeDetailFragment{371a2d2 #2 id=0x7f090020}
2020-03-03 16:22:51.350 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onPause: AnimeDetailFragment{52888e9 #1 id=0x7f090020}
2020-03-03 16:22:51.350 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onStop: AnimeDetailFragment{52888e9 #1 id=0x7f090020}
2020-03-03 16:22:51.350 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onDestroyView: AnimeDetailFragment{52888e9 #1 id=0x7f090020}
2020-03-03 16:22:51.365 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onCreateView: AnimeDetailFragment{371a2d2 #2 id=0x7f090020}
2020-03-03 16:22:51.369 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onActivityCreated: AnimeDetailFragment{371a2d2 #2 id=0x7f090020}
2020-03-03 16:22:51.369 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onStart: AnimeDetailFragment{371a2d2 #2 id=0x7f090020}
2020-03-03 16:22:51.391 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onResume: AnimeDetailFragment{371a2d2 #2 id=0x7f090020}
//按了返回键,移除了fragment2(371a2d2), fragment1(52888e9)重新恢复。fragment2(371a2d2)执行到onDetach 被销毁。
2020-03-03 16:22:53.144 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onPause: AnimeDetailFragment{371a2d2 #2 id=0x7f090020}
2020-03-03 16:22:53.144 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onStop: AnimeDetailFragment{371a2d2 #2 id=0x7f090020}
2020-03-03 16:22:53.144 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onDestroyView: AnimeDetailFragment{371a2d2 #2 id=0x7f090020}
2020-03-03 16:22:53.148 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onDestroy: AnimeDetailFragment{371a2d2 #2 id=0x7f090020}
2020-03-03 16:22:53.148 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onDetach: AnimeDetailFragment{371a2d2 #2 id=0x7f090020}
2020-03-03 16:22:53.148 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onCreateView: AnimeDetailFragment{52888e9 #1 id=0x7f090020}
2020-03-03 16:22:53.150 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onActivityCreated: AnimeDetailFragment{52888e9 #1 id=0x7f090020}
2020-03-03 16:22:53.150 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onStart: AnimeDetailFragment{52888e9 #1 id=0x7f090020}
2020-03-03 16:22:53.151 20442-20442/com.flx.testfragment D/flx_AnimeDetailFragment: onResume: AnimeDetailFragment{52888e9 #1 id=0x7f090020}

Fragment使用并不难,用好了,能构建很灵活的界面,做到很好的适配,提升用户体验。

Fragment简介及使用的更多相关文章

  1. TabLayout ViewPager Fragment 简介 案例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  2. Fragment 简介 基础知识 总结 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  3. Android学习笔记(五)Fragment简介

    Fragment是在Android 3.0 (API level 11)中引入的Activity的子模块.初衷是为了适应大屏幕的平板电脑,我们只需要使用Fragment对UI组件进行分组.模块化管理, ...

  4. Android-fragment简介-fragment的简单使用

    1.fragment简介 在Android3.0版本之前Google还没有推出fragment,在Android3.0版本之后Google推出了fragment,由于Android3.0版本是过渡期版 ...

  5. 安卓第十天笔记-fragment

    安卓第十天笔记-fragment Fragment(片段) 一.Fragment简介 *Fragment是3.0引入的API,主要为了解决平板,大屏幕手机显示问题 *Fragment代表了Activi ...

  6. Fragment详解

    1 Fragment简介 1.1 Fragment的设计初衷 Android3.0引入Fragment的初衷是为大屏幕的设备提供更加灵活的动态UI设计,由于大屏设备可以容纳更多的UI组件,且这些UI组 ...

  7. Android开发之Fragment的介绍、使用及生命周期

    Fragment官网介绍-http://developer.android.com/guide/components/fragments.html 郭大神的使用实例文章:http://blog.csd ...

  8. Android06-Fragment碎片

    ¨Fragment简介 ¨Fragment生命周期 ¨动态加载碎片Fragment Manager的使用   1.Fragment表示Activity中的一种行为或者一部分用户界面. 可以将Fragm ...

  9. Fragment生命周期及实现点击导航图片切换fragment,Demo

    PS:Fragment简介 Fragment是Android3.0后引入的一个新的API,他出现的初衷是为了适应大屏幕的平板电脑, 当然现在他仍然是平板APP UI设计的宠儿,而且我们普通手机开发也会 ...

随机推荐

  1. Codeforce 1251C. Minimize The Integer

    C. Minimize The Integer time limit per test2 seconds memory limit per test256 megabytes inputstandar ...

  2. Mybatis时间范围查询,亲测有效

    Md2All export document .output_wrapper pre code{font-family: Consolas, Inconsolata, Courier, monospa ...

  3. 集训模拟赛-1-T1

    最近学校网课跟得紧没时间写知识点,就拿题解凑个数(bushi 而且前两天我打着打着题解电脑就突然死机 幸运的是 我没有保存(微笑) 废话不多说 上题目! 城市攻击 (city) (256MB,1s) ...

  4. Linux中的程序和进程,PID和PPID

    环境:Vmware Workstation:CentOS-6.4-x86_64 程序和进程: 1.程序:程序是静止的,程序就是磁盘上的一个文件. 2.进程:进程是一个正在执行的程序的实例. 3.进程是 ...

  5. HDU1214圆桌会议

    一个环,从1编号到n. 每次可以交换相邻的两个人, 问最少交换几次,使得每个数字的左右数字交换. 转载自:https://blog.csdn.net/yin_zongming/article/deta ...

  6. 单调队列+二分 G - Queue 小阳买水果

    B. Queue 这个题目会做的很偶然,突然想到的,因为我们要求离这只海象的最远的比他年轻的海象,这个年轻的海象可以用单调栈维护. 就是从前往后遍历一遍,单调栈里面存年龄从小往大的海象,这个为什么这么 ...

  7. Spring mvc的基本配置及工作原理

    1.spring mvc框架搭建 需求:在浏览器输入一个请求login.do,跳转到登录成功界面. 第一步,创建web项目,导入jar包 注意: 第二步,在web.xml中配置spring的核心监听器 ...

  8. 使用天祥TX-1C调试DS18B20温度传感器的收获

    翻查DS18B20的DataSheet编写操作函数,其过程遇到了不少坎,记下来备查. 对于单总线的DS18B20芯片,首先严格按照时序图写出正确的“写0”.“写1”和“读0.1”的基础函数,再以此写出 ...

  9. python统计英文文本中的回文单词数

    1. 要求: 给定一篇纯英文的文本,统计其中回文单词的比列,并输出其中的回文单词,文本数据如下: This is Everyday Grammar. I am Madam Lucija And I a ...

  10. imos-累积和法

    在解AOJ 0531 Paint Color时,学到了一个累积和的妙用--imos法,由于原文是日语,所以特意翻译过来.值得一提的是,作者Kentaro Imajo跟鄙人同龄,却已取得如此多的成就,而 ...