随着移动设备的飞速发展,平板电脑也变得越来越普及。平板电脑和手机最大的区别就在于屏幕的大小,一般手机屏幕的大小会在 3 英寸到 5 英寸之间,而一般平板电脑屏幕的大小会在 7 英寸到 10 英寸之间。屏幕大小差距过大有可能会让同样的界面在视觉效果上有较大的差异,比如一些界面在手机上看起来非常美观,但在平板电脑上看起来就可能会有控件被过分拉长、元素之间空隙过大等情况。
作为一名专业的 Android 开发人员,能够同时兼顾到手机和平板的开发是我们必须要做到的事情,所以android3.0引入了Flagment的概念,它可以让界面在平板上更好的展示。
参考: 1. 第一行代码160页 2. http://blog.csdn.net/lmj623565791/article/details/37970961
1. Flagment的概念
Flagment也被称为碎片,是一种可以嵌入在Activity中的 UI 片段,它能让程序更加合理和充分地利用大屏幕的空间。
在很多情况下,都是先针对手机开发一套App,然后拷贝一份,修改布局以适应平板神马超级大屏的。难道无法做到一个App可以同时适应手机和平板么,当然了,必须有啊。Fragment的出现就是为了解决这样的问题。
你可以把Fragment当成Activity的一个界面的一个组成部分,甚至Activity的界面可以完全有不同的Fragment组成,1. 更帅气的是Fragment拥有自己的生命周期和接收、处理用户的事件,这样就不必在Activity写一堆控件的事件处理的代码了。2.更为重要的是,你可以动态的添加、替换和移除某个Fragment。(flagement的两点优势)
2. Flagment的生命周期
Fragment必须是依存与Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期,下图能体现两者的关系:
- 可以看出Fragment比Activity多了几个额外的生命周期回调方法
- onAttach(Activity)------当Fragment与Activity发生关联时调用。
- onCreateView(LayoutInflater, ViewGroup,Bundle)------创建该Fragment的视图
- onActivityCreated(Bundle)------当Activity的onCreate方法返回时调用
- onDestoryView()------与onCreateView想对应,当该Fragment的视图被移除时调用
- onDetach()------与onAttach相对应,当Fragment与Activity关联被取消时调用①
注意:除了onCreateView(父类返回null,必须子类实现view并返回),其他的所有方法如果你重写了,必须调用父类对于该方法的实现。
问题解决①:onCreate()与onCreateView()的区别
onCreate是指创建该fragment类似于Activity.onCreate,你可以在其中初始化除了view之外的东西,
onCreateView是创建该fragment对应的视图,你必须在这里创建自己的视图并返回给调用者,例如
return inflater.inflate(R.layout.fragment_settings, container, false);
super.onCreateView有没有调用都无所谓,因为super.onCreateView是直接返回null的。
3. Flagment的使用
① 静态使用Fragment
把Fragment当成普通的控件,直接写在Activity的布局文件中。步骤如下:
1. 继承Fragment,重写onCreateView决定Fragemnt的布局 —> 2. 在Activity的布局文件中通过 android:name 属性来显式指明要添加的Flagment类名
—> 3. 在Activity中声明此Fragment,就当和普通的View一样
初步优势:所有控件的事件处理等代码都由各自的Fragment去处理,瞬间觉得Activity好干净有木有~~代码的可读性、复用性以及可维护性是不是瞬间提升了
② 动态添加Flagment
碎片真正的强大之处在于可以在程序中动态的添加删除更新到活动当中,根据具体情况来动态地添加碎片,你就可以使你的界面更加多样化
使用步骤:
1. 创建待添加的碎片实例
2. 获取到 FragmentManager,在活动中可以直接调用 getFragmentManager()方法得到
3. 开启一个事务,通过调用FragmentManager.beginTransaction()方法开启
4. 向容器内加入碎片,一般使用 replace()方法实现,需要传入容器的 id 和待添加的碎片实例
5. 提交事务,调用 commit()方法来完成
例子: 在Activity中用B碎片替代A碎片
BFragment fragment = new BFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.right_layout, fragment);
transaction.commit();
③其他一些主要的操作都是FragmentTransaction的方法③
事先:FragmentTransaction transaction = fm.benginTransatcion(); //开启一个事务
Fragment的id可以通过FragmentManager.findFragmentByid(id)的得到
1. transaction.add(布局文件中的id,Fragment) 往Activity中添加一个Fragment
2. transaction.remove() 从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁。
3. transaction.replace() 使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体~
4. transaction.hide() 隐藏当前的Fragment,仅仅是设为不可见,并不会销毁
5. transaction.show() 显示之前隐藏的Fragment
6. detach()和attach() 会将view从UI中移除或重建view视图,和remove()不同,此时fragment的状态依然由FragmentManager维护。
最后 记得:transatcion.commit()//提交一个事务
注意:常用Fragment的哥们,可能会经常遇到这样Activity状态不一致:State loss这样的错误。主要是因为:commit方法一定要在Activity.onSaveInstance()之前调用。
④总结:
1. 要清楚哪些方法会销毁视图,哪些会影藏视图。
比如:我在FragmentA中的EditText填了一些数据,当切换到FragmentB时,如果希望会到A还能看到数据,则适合你的就是hide和show;也就是说,希望保
留用户操作的面板,你可以使用hide和show
2. 不希望保留用户操作,你可以使用remove(),然后add();或者使用replace()这个和remove,add是相同的效果
3. remove和detach有一点细微的区别,在不考虑回退栈的情况下,remove会销毁整个Fragment实例,而detach则只是销毁其视图结构,实例并不会被销、
毁。那么二者怎么取舍使用呢?如果你的当前Activity一直存在,那么在不希望保留用户操作的时候,你可以优先使用detach。
4. Fragment的回退栈
类似与Android系统为Activity维护一个任务栈,我们也可以通过Activity维护一个回退栈来保存每次Fragment事务发生的变化。如果你将Fragment任务添加到回
退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。
操作代码: FragmentTransaction.addToBackStack(String 回退栈状态) 将一个事务添加到回退栈中,回退栈状态一般为空
5. Fragment与Activity的通信
① 得到Fragment的实例
1. 如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法
2. 如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentById()获得Fragment实例,然后进行操作。
② 在Fragment中获得Activity
MainActivity activity = (MainActivity) getActivity()
注意:如果在Fragment中需要Context,可以通过调用getActivity(),如果该Context需要在Activity被销毁后还存在,则使用getActivity().getApplicationContext()。
③ Fragment与Fragment之间的通信
首先在一个碎片中可以得到与它相关联的活动,然后再通过这个活动去获取另外一个碎片的实例,这样也就实现了不同碎片之间的通信功能,
但是在现实情况下,1.考虑Fragment的重复使用,所以必须降低Fragment与Activity的耦合。
2. Fragment更不应该直接操作别的Fragment,毕竟Fragment操作应该由它的管理者Activity来决定。
所以一般除非特别情况不提倡上面的通信做法,选取下面的两种做法
优化做法①: 重构Fragment的点击事件
- import android.app.Fragment;
- import android.os.Bundle;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.view.ViewGroup;
- import android.widget.Button;
- public class FragmentOne extends Fragment implements OnClickListener
- {
- private Button mBtn;
- /**
- * 设置按钮点击的回调
- * @author J
- *
- */
- public interface FOneBtnClickListener
- {
- void onFOneBtnClick();
- }
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState)
- {
- View view = inflater.inflate(R.layout.fragment_one, container, false);
- mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);
- mBtn.setOnClickListener(this);
- return view;
- }
- /**
- * 交给宿主Activity处理,如果它希望处理
- */
- @Override
- public void onClick(View v)
- {
- if (getActivity() instanceof FOneBtnClickListener)
- {
- ((FOneBtnClickListener) getActivity()).onFOneBtnClick();
- }
- }
- }
可以看到现在的FragmentOne不和任何Activity耦合,任何Activity都可以使用;并且我们声明了一个接口,来回调其点击事件,想要管理其点击事件的Activity实现此接口就即可。
可以看到我们在onClick中首先判断了当前绑定的Activity是否实现了该接口,如果实现了则调用。
优化方法②: 重构Activity对点击事件的响应
- import android.app.Fragment;
- import android.os.Bundle;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.view.ViewGroup;
- import android.widget.Button;
- public class FragmentTwo extends Fragment implements OnClickListener
- {
- private Button mBtn ;
- private FTwoBtnClickListener fTwoBtnClickListener ;
- public interface FTwoBtnClickListener
- {
- void onFTwoBtnClick();
- }
- //设置回调接口
- public void setfTwoBtnClickListener(FTwoBtnClickListener fTwoBtnClickListener)
- {
- this.fTwoBtnClickListener = fTwoBtnClickListener;
- }
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState)
- {
- View view = inflater.inflate(R.layout.fragment_two, container, false);
- mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);
- mBtn.setOnClickListener(this);
- return view ;
- }
- @Override
- public void onClick(View v)
- {
- if(fTwoBtnClickListener != null)
- {
- fTwoBtnClickListener.onFTwoBtnClick();
- }
- }
- }
与FragmentOne极其类似,但是我们提供了setListener这样的方法,意味着Activity不仅需要实现该接口,还必须显示调用mFTwo.setfTwoBtnClickListener(this)。
6. 没有布局文件的Fragment的作用
没有布局文件Fragment实际上是为了保存,当Activity重启时,保存大量数据准备的
来源:https://www.cnblogs.com/chenjinsen/p/5199321.html