Fragment简介及使用
概述
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简介及使用的更多相关文章
- TabLayout ViewPager Fragment 简介 案例 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- Fragment 简介 基础知识 总结 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- Android学习笔记(五)Fragment简介
Fragment是在Android 3.0 (API level 11)中引入的Activity的子模块.初衷是为了适应大屏幕的平板电脑,我们只需要使用Fragment对UI组件进行分组.模块化管理, ...
- Android-fragment简介-fragment的简单使用
1.fragment简介 在Android3.0版本之前Google还没有推出fragment,在Android3.0版本之后Google推出了fragment,由于Android3.0版本是过渡期版 ...
- 安卓第十天笔记-fragment
安卓第十天笔记-fragment Fragment(片段) 一.Fragment简介 *Fragment是3.0引入的API,主要为了解决平板,大屏幕手机显示问题 *Fragment代表了Activi ...
- Fragment详解
1 Fragment简介 1.1 Fragment的设计初衷 Android3.0引入Fragment的初衷是为大屏幕的设备提供更加灵活的动态UI设计,由于大屏设备可以容纳更多的UI组件,且这些UI组 ...
- Android开发之Fragment的介绍、使用及生命周期
Fragment官网介绍-http://developer.android.com/guide/components/fragments.html 郭大神的使用实例文章:http://blog.csd ...
- Android06-Fragment碎片
¨Fragment简介 ¨Fragment生命周期 ¨动态加载碎片Fragment Manager的使用 1.Fragment表示Activity中的一种行为或者一部分用户界面. 可以将Fragm ...
- Fragment生命周期及实现点击导航图片切换fragment,Demo
PS:Fragment简介 Fragment是Android3.0后引入的一个新的API,他出现的初衷是为了适应大屏幕的平板电脑, 当然现在他仍然是平板APP UI设计的宠儿,而且我们普通手机开发也会 ...
随机推荐
- django源码分析——处理请求到wsgi及视图view
本文环境python3.5.2,django1.10.x系列 根据前上一篇runserver的博文,已经分析了本地调试服务器的大致流程,现在我们来分析一下当runserver运行起来后,django框 ...
- 分治思想--快速排序解决TopK问题
----前言 最近一直研究算法,上个星期刷leetcode遇到从两个数组中找TopK问题,因此写下此篇,在一个数组中如何利用快速排序解决TopK问题. 先理清一个逻辑解决TopK问题→快速排序→递 ...
- Codeforces Round #563 (Div. 2) A-D
A. Ehab Fails to Be Thanos 这个A题很简单,就是排个序,然后看前面n个数和后面的n个数是不是相同,相同就输出-1 #include <cstdio> #inclu ...
- MOD3干扰
1.MOD3干扰的定义 MOD3干扰也称模3干扰,是LTE网络内干扰的一种形式,要了解这种干扰的产生原理,就要从小区PCI入手. PCI全称PhysicalCellIdentifier,即物理小区标识 ...
- 多表同步 ES 的问题
原始需求 对跨业务域数据提供联查搜索能力. 比如:对退款单提供根据退款单.退款状态.发货状态的联查,其中退款状态和发货状态是跨业务域. 比如:对订单提供根据订单号.订单状态.退款状态的联查,其中订单状 ...
- Android(H5)互相调用方法
记录一下前面混合开发时很重要的java与js互调方法进行数据交互. 混合开发就需要webview这个控件了 这就很玄学了,哈哈哈 这篇文章https://www.jianshu.com/p/3d9a9 ...
- NDK clang编译器的一个bug
NDK clang编译器的一个bug 问题代码 float32_t Sum_float(float32_t *data, const int count) { float32x4_t res = vd ...
- js中刷新页面的方式总结
1.window.onload / document.onload 2.history.go(num): (1)num为参数,num为正表示前进几个页面,类似于history.forward(): ( ...
- 局域网ip地址扫描_v1版本
局域网ip地址扫描 工作中,我们有时需要对局域网中ip地址使用情况进行统计.可以使用shell脚本进行扫. 脚本功能: 在线使用IP写入list_online.txt文件 未在线IP写入list_of ...
- hive经典练习题
一.建表和加载数据 1.student表 create table if not exists student(s_id int,s_name string,s_birth string,s_sex ...