Android--UI之Fragment
前言
开门见山开篇名义,本篇博客将讲解一下Android中Fragment的内容,必要的地方会提供相应的演示代码,并且会在最后给出源码下载。
本文主要有以下内容:
什么是Fragment
Fragment,碎片,是Android3.0之后新增加的特性。主要是为了支持更多的UI设计在大屏幕设备上,如平板。因为现在设备的屏幕越来越大,使用Fragment可以更灵活的管理视图层次的变化。像Activity一样,可以创建Fragment来包含View,进行布局,但是Fragment必须嵌入在Activity中,不能单独存在,而且一个Activity可以嵌入多个Fragment,同时一个Fragment可以被多个Activity重用。
上图是从官方文档中挂载的,可以很清晰的说明Activity和Fragment的关系和优点。在平板中,因为屏幕大,显示的内容全,如果还像手机哪样通过Activity跳转的方式去加载UI,太浪费屏幕资源了,而如上左图,可以结合Fragment布局,使一个Activity左右分别包含一个Fragment,这样可以通过对左边Fragment的操作来影响右边Fragment的显示,例如:新闻阅读,系统设置等。如果一个应用的是采用Activity+Fragment结合布局,那么可以很方便的在平板与手机之间相互移植,大部分代码是可以重用的,并且Fragment无需在AndroidManifest.xml清单文件中注册。
如何创建一个Fragment
上面已经介绍了Fragment,再来讲讲如何使用Fragment。使用Fragment必须继承这个类或其子类,并且重写其的onCreateView()方法,这个方法是用于指定Fragment在初次加载的时候,显示的View。下面是这个方法的签名:
public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState)
- inflater:当前布局的填充者,可以用inflater.inflate()方法去填充一个布局文件。
- container:为包裹当前Fragment的容器,一般是一个布局控件。
- savedInstanceState:当前实例化的状态,一般用不上。
onCreateView()返回一个View,用于Fragment的显示,这里使用inflater.inflate()方法动态膨胀一个View对象做返回值,inflate()的签名如下:
public View inflate(int resource,ViewGroup root,boolean attachToRoot)
- resource:动态膨胀的布局资源ID。
- root:膨胀出的View的上层布局对象,一般传递onCreateView的container参数即可。
- attachToRoot:指定展开的布局是否依附到root这个ViewGroup中,一般传递false即可。
inflate()的resource是一个普通的布局资源,和之前的布局没有什么特殊性。而在布局中使用Fragment使用<fragment/>标签来在XML文件中布局,大多数属性与UI控件一样,但是其中有两个属性需要特别注意:
- android:name:这个Fragment的实现类。
- android:layout_weight:当前Fragment在Activity的权重,数值越大,在Activity中占的权重越大。
下面通过一个示例,来演示一下Fragment在Activity中的应用。示例中在一个Activity中,添加了两个Fragment。
activity_fragment.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" > <fragment
android:id="@+id/fragment1"
android:name="com.example.fragmentSimple.Fragment1"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="2" /> <fragment
android:id="@+id/fragment2"
android:name="com.example.fragmentSimple.Fragment2"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1" /> </LinearLayout>
Fragment1:
package com.example.fragmentSimple; import com.example.fragmentdemo.R;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; public class Fragment1 extends Fragment { @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// 填充一个布局View到ViewGrope中
return inflater.inflate(R.layout.fragment1, container, false);
}
}
Fragment2:
package com.example.fragmentSimple; import com.example.fragmentdemo.R; import android.os.Bundle;
import android.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; public class Fragment2 extends Fragment { @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment2, container, false);
}
}
启动后显示效果:
Fragment的生命周期
Fragment有自己独立的生命周期,但是它有是依托于Activity的,所以Fragment的生命周期直接受Activity的影响。下图很直观的描述了Activity的生命周期:
从上图中可以看出Fragment的生命周期大体上和Activity一样,有两个生命周期方法需要注意,onAttach()附加、onDetach()剥离,从这两个方法的位置可以看出,Fragment在创建的时候,是先附加到Activity中,然后才开始从onCreateView()中加载View的,记住这一点很重要。并且在生命周期结束的时候,也是先销毁onDestroy()然后才回调onDetach()从Activity中剥离这个Fragment。
如何管理一个Fragment
在代码中管理一个Fragment非常简单,需要用到一个FragmentTransaction对象,这个对象通过getFragmentManager().beginTransaction()获取,它将开启一个事务,用于操作一个ViewGroup中的Fragment。
FragmentTransaction的常用方法:
- add():增加一个Fragment,具有多个重载。
- replace():替换一个Fragment,具有多个重载。
- remove():移除掉一个指定的Fragment。
- addToBackStack():在事务中添加一个栈,用于回退。
- commit():提交事务。
其中add、replace、remove都是很常见的方法,无需过多介绍。但是addToBackStack()方法就需要额外讲解一下,正常情况下,应用中的Activity是有一个任务栈去管理它的。默认情况下,当我们在不同的Activity中跳转的时候,点击回退总是能回到上一个Activity中。而Fragment是嵌套在Activity,所以默认无法向Activity的任务栈中添加,当点击回退的时候只会回到上一个Activity,不会理会Fragment的操作(add、replace、remove),而使用addToBackStack()可以将当前的事务添加到另一个栈中,这个栈由Fragment的Activity管理,这个栈中的每一条都是一个Fragment的一次事务,有了这个栈去管理Fragment,就可以通过回退按键,反向回滚Fragment的事务。这一点很重要,因为Fragment无需在清单文件中配置,所以现在有些应用会使用Fragment来布局跳转。
下面通过一个示例,演示一下动态操作Fragment的例子。在示例中,会实现一个分栏的效果,在左边点击项会动态修改右边的内容。
布局文件,activity_fragmenttab.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" > <LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical" > <TextView
android:id="@+id/tabfgt1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="fragment1" /> <TextView
android:id="@+id/tabfgt2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="fragment2" /> <TextView
android:id="@+id/tabfgt3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="fragment3" />
</LinearLayout> <LinearLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
</LinearLayout> </LinearLayout>
FragmentTabActivity.java:
package com.example.fragmentTab; import com.example.fragmentSimple.Fragment1;
import com.example.fragmentSimple.Fragment2;
import com.example.fragmentTurn.Fragment3;
import com.example.fragmentdemo.R; import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView; public class FragmentTabActivity extends Activity {
private TextView tabfgt1, tabfgt2, tabfgt3; @Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragmenttab); tabfgt1 = (TextView) findViewById(R.id.tabfgt1);
tabfgt2 = (TextView) findViewById(R.id.tabfgt2);
tabfgt3 = (TextView) findViewById(R.id.tabfgt3); tabfgt1.setOnClickListener(click);
tabfgt2.setOnClickListener(click);
tabfgt3.setOnClickListener(click); } private View.OnClickListener click = new View.OnClickListener() { @Override
public void onClick(View v) {
tabfgt1.setBackgroundColor(Color.GRAY);
tabfgt2.setBackgroundColor(Color.GRAY);
tabfgt3.setBackgroundColor(Color.GRAY);
// 获取FragmentManager对象
FragmentManager fm = getFragmentManager();
// 开启事务
FragmentTransaction ft = fm.beginTransaction();
switch (v.getId()) {
case R.id.tabfgt1:
tabfgt1.setBackgroundColor(Color.GREEN);
// 替换R.id.content中的Fragment
ft.replace(R.id.content, new Fragment1());
break;
case R.id.tabfgt2:
tabfgt2.setBackgroundColor(Color.YELLOW);
ft.replace(R.id.content, new Fragment2());
break;
case R.id.tabfgt3:
tabfgt3.setBackgroundColor(Color.RED);
ft.replace(R.id.content, new Fragment3());
break;
default:
break;
}
// 提交事务
ft.commit();
}
};
}
效果展示:
在Fragment中,如何交互
既然Fragment是嵌套在Activity中的,而在Fragment加载的布局文件中,又可以额外的布局,那么出现了新的问题,如何操作两个不同Fragment中的控件呢?回忆一下在Activity中,操作一个控件需要通过findViewById(int)方法通过控件的ID去找到控件,而使用Fragment其实到最后Fragment.onCreateActivity()的时候是把膨胀的View加载到Activity中了,所以可以直接在Activity中通过findViewById()方法找到控件,进而操作它,这一点和直接操作Activity的方式一致。但是如果需要在一个Fragment中操作另外一个Fragment的控件,就需要用到Fragment.getActivity()来获取到当前Fragment承载的Activity对象,拿到这个Activity对象,获取到其中的控件就不成问题了。
下面通过一个示例来演示Fragment中的交互,在Activity中,有三个Fragment,从其中的一个Fragment的Button点击的时候,修改其他Fragment的值。
布局,activity_fragmentturn.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" > <fragment
android:id="@+id/fragment1"
android:name="com.example.fragmentSimple.Fragment1"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1" />
<!-- 加载了两个Fragment1 -->
<fragment
android:id="@+id/fragment2"
android:name="com.example.fragmentSimple.Fragment1"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="@+id/fragment3"
android:name="com.example.fragmentTurn.Fragment3"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1" /> </LinearLayout>
带Button的Fragment:
package com.example.fragmentTurn; import com.example.fragmentdemo.R; import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class Fragment3 extends Fragment { @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
return inflater.inflate(R.layout.fragment3, container, false);
}
@Override
public void onStart() {
super.onStart();
// 方法2: 在Fragment中获取操作其他Fragment的控件
// Button btnGetText=(Button)getActivity().findViewById(R.id.btnGetText);
// btnGetText.setOnClickListener(new View.OnClickListener() {
//
// @Override
// public void onClick(View v) {
// TextView tv=(TextView)getActivity().findViewById(R.id.tvFragment1);
// Toast.makeText(getActivity(), tv.getText().toString() ,Toast.LENGTH_SHORT).show();
// }
// });
}
}
FragmentTurnActivity.java:
package com.example.fragmentTurn; import com.example.fragmentdemo.R; import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast; public class FragmentTurnActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragmentturn);
// 方法1:在Activity中操作旗下Fragment中的控件
Button btn=(Button)findViewById(R.id.btnGetText);
btn.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
TextView tv=(TextView)findViewById(R.id.tvFragment1);
tv.setText("动态修改");
Toast.makeText(FragmentTurnActivity.this,tv.getText().toString() ,Toast.LENGTH_SHORT ).show();
}
});
}
}
效果展示:
从上面的例子有一个问题,无论是在Activity中使用findViewById()还是在Fragment中使用getActivity().findViewById(),虽然可以获取到控件,但是有个例外的情况。就是在Activity中,同时使用了两个一样的Fragment,这个时候仅仅使用上面介绍的方法,只能通过id获取到第一个Fragment中的控件。因为,在布局文件中定义的控件,就算ID重复了,AndroidSDK维护的R.java类中,也只会声明一次,也就是说,想在Activity中区分同一个Fragment类的两个实例中的控件,是无法做到的。
那么就嘚换一个思路,我的解决方案:在Fragment中声明一个View变量,然后在onCreateView中膨胀的View并不直接返回,而是把它引用到声明的View变量上,然后在应用的任何地方,使用getFragmentManager().findFragmentById(int)通过Fragment的Id找到这个Fragment对象,然后获取其中的View对象,使用View.findViewById(int)找到Fragment的对应Id的控件,进而操作它,这里就不提供示例了。虽然这个方法可以解决问题,但是一般不推荐如此做,因为大部分场景没必要在一个Activity中定义两个相同的Fragment。
Fragement向下兼容
上面已经提到,Fragment是Android3.0行增加的特性。 而对于低版本的Android设备,Google也没有放弃。细心的朋友应该已经发现了,当对Fragment引用包的时候,会有两个选项,android.app.Fragment和android.support.v4.app.Fragment,其中android.support.v4.app.Fragment就是为了兼容低版本而考虑的,只需要引用它即可。
一般而言,如果考虑向下兼容的问题的话,推荐直接引用android.support.v4.app.Fragment包进行开发,就不会存在兼容性的问题。
Android--UI之Fragment的更多相关文章
- android UI:Fragment碎片
碎片(Fragment) 嵌入与活动中的UI片段,为了合理的分配布局而存在,这是我的简单理解.多用于兼顾手机与平板的UI,也适用于灵活高级的UI制作. Demo 简单的按键切换两片不同的Demo 新建 ...
- 【Android UI】:Fragment官方文档
概述 Fragment表现Activity中UI的一个行为或者一部分.可以将多个fragment组合在一起,放在一个单独的activity中来创建一个多界面区域的UI,并可以在多个activity ...
- 【Android UI设计与开发】4.底部菜单栏(一)Fragment介绍和简单实现
TabActivity在Android4.0以后已经被完全弃用,取而代之的是Fragment.Fragment是Android3.0新增的概念,Fragment翻译成中文是碎片的意思,不过却和Acti ...
- Android UI开发第三十篇——使用Fragment构建灵活的桌面
http://www.lupaworld.com/article-222973-1.html 当我们设计应用程序时,希望能够尽最大限度的适配各种设备,包括4寸屏.7寸屏. 10寸屏等等,Android ...
- Android UI开发详解之Fragment
Fragment是Android自从3.0之后新加入的一个组件,我相信很多人都已经听说过这个组件了,但这个组件到底是个什么,如何去使用他呢,且听我讲来. 以下部分资料来自官网(官网才是王道,其他都是浮 ...
- Android入门——UI(8)——Fragment(2)
先演示一下如何在一个activity中放置两个Fragment,先定义两个Fragment <?xml version="1.0" encoding="utf-8& ...
- Android - 用Fragments实现动态UI - 创建Fragment
你可以把fragment当作activity中的一个活动模块,它有自己的生命周期,自己接收输入消息,可以在activity运行的时候添加和删除(就像可以在其他activity中重用的"子ac ...
- 【转】【Android UI设计与开发】第07期:底部菜单栏(二)Fragment的详细介绍和使用方法
原始地址:http://blog.csdn.net/yangyu20121224/article/category/1431917/1 由于TabActivity在Android4.0以后已经被完全弃 ...
- Android UI开发第二十八篇——Fragment中使用左右滑动菜单
Fragment实现了Android UI的分片管理,尤其在平板开发中,好处多多.这一篇将借助Android UI开发第二十六篇——Fragment间的通信. Android UI开发第二十七篇——实 ...
- Android入门——UI(7)——Fragment
先上fragment静态加载的代码 <?xml version="1.0" encoding="utf-8"?> <LinearLayout ...
随机推荐
- thymeleaf拆分头部(head)显示异常问题
问题描述: 刚用thymeleaf不久,考虑到公共头部的导入css,js代码,需要拆分. 拆分之后,bootstrap-select下拉多选框出现“样式异常”,本认为是头部拆分问题,css样式未导入成 ...
- ApocalypseSomeday
ApocalypseSomeday CountDownLatch和CyclicBarrier分别都是在什么时候使用的? Charles使用(apphttp抓包,request拦截,response拦截 ...
- HTML5调用手机的Datepicker(日期选择器)
HTML5 拥有多个新的表单输入类型.这些新特性提供了更好的输入控制和验证,包含了如下新的输入类型: email url number range Date pickers (date, month, ...
- Educational Codeforces Round 58 (Rated for Div. 2) F dp + 优化(新坑) + 离线处理
https://codeforces.com/contest/1101/problem/F 题意 有n个城市,m辆卡车,每辆卡车有起点\(s_i\),终点\(f_i\),每公里油耗\(c_i\),可加 ...
- 更改MAC地址,突破公司绑定MAC地址的限制
步骤/方法 1 打开开始菜单,选择控制面板. 2 3 打开控制面板项,选择网络和共享中心. 4 5 选择更改适配器设置. 6 7 选择本地要修改MAC地址的网卡. 8 9 右键该网卡, ...
- 怎么解决syntaxerror:non-utf-8 code starting with \xc4'in file
怎么解决syntaxerror:non-utf-8 code starting with \xc4'in file 首行增加,已测试可用. # coding=gbk 程序中出现中文,运行的时候 ...
- MQTT
1.IBM提出,适用于IOT,订阅和发布模式. 2.订阅和发布模式:这种模式是异步的形式,有些类似于邮件接发的形式,发送者将邮件发至代理,接收者如果没同时接收,也不影响发送者的二次发送. 3.主题模式 ...
- xmlhttprequest readyState 属性的五种状态
关于readystate五个状态总结如下: readyState 状态 状态说明(0)未初始化此阶段确认XMLHttpRequest对象是否创建,并为调用open()方法进行未初始化作好准备.值 ...
- G++与C++的区别
C++是一门计算机编程语言,G++不是语言,是一款编译器中编译C++程序的命令而已. 不同的编译器,会对代码做出一些不同的优化 比如说: a++; 和 ++a; 如果从标准C的角度去理解.a++这个 ...
- Mybatis的JDBC提交设置/关闭mysql自动提交------关于mysql自动提交引发的惨剧
学习Mybatis时提到了JDBC方式需要自己手动提交事务,如果不加session.commit会导致数据库的数据无法正常插入(程序本身又不给你报错,还装出一副我已经插入成功的样子) SqlSessi ...