阅读过程中有疑惑的地方:
- inflate方法的第三个参数,为true和false时,到底有何区别;
- FragmentTransaction的replace方法,到底是怎么个替换法?是把当前view中所有fragment都替换掉,还是只替换id相同的fragment;
- FragmentTransaction的addToBackStack()方法,是返回上一次状态,但是为何平板上面实际操作的时候是直接退出了activity;
- padding的单位,按我的理解,只能是像素吧;
发现了一个自适应计算的方法:TypedValue.applyDimension,方法源码如下:
1 public static float applyDimension(int unit, float value,
2 DisplayMetrics metrics)
3 {
4 switch (unit) {
5 case COMPLEX_UNIT_PX:
6 return value;
7 case COMPLEX_UNIT_DIP:
8 return value * metrics.density;
9 case COMPLEX_UNIT_SP:
10 return value * metrics.scaledDensity;
11 case COMPLEX_UNIT_PT:
12 return value * metrics.xdpi * (1.0f/72);
13 case COMPLEX_UNIT_IN:
14 return value * metrics.xdpi;
15 case COMPLEX_UNIT_MM:
16 return value * metrics.xdpi * (1.0f/25.4f);
17 }
18 return 0;
19 }
根据当前value以及单位、屏幕密度来动态计算,返回的值是计算过后的实际像素。
实例:
1 int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
2 4, getActivity().getResources().getDisplayMetrics());
3 text.setPadding(padding, padding, padding, padding);
dp实际上可以理解成一个相对的比例,比如若在mdpi、分辨率是480x800的屏幕上面,一个宽240dp的按钮,实际像素是240px,占了屏幕的1/2;那么在ldpi、分辨率是320x480的屏幕上面,它也要占屏幕的1/2,但是button的像素只有160px了。
阅读完以后的总结:
- 设计原则,为什么要用fragment
- 创建方式,包括两种,一种是通过xml文件夹,另外一种是在代码中添加
- 如何管理
- 与activity如何交互
一、设计原则
fragment是为了使大屏幕的界面更加灵活而产生,将UI抽象成可重用的组件,然后在界面上灵活使用。最常见的用法是在横竖屏切换、或者平板与手机共用同一个apk的时候,如下图:
应用场景1
个人理解:平板横屏的时候,可以将左侧的列表和右侧的详细信息同时展现出来;竖屏的时候宽度不够,那么只是单独展示列表或者详细信息。常规的做法是需要针对这种方式分别写上3个activity.那么使用fragment后,将列表抽象成一个共用的fragment,详细信息也抽象成一个公用的fragment,横屏的布局文件中包含两个fragment,竖屏的只包含一个fragment,但是共用同一个activity A的代码。需要做的判断是在点左侧列表中一个选项的时候,是在右侧更新内容还是另起一个activity来展示。另起的activity B 只要将fragment B包含进去即可,并不需要新增很多再处理的代码,这样就达到了一个重用的效果。
二、创建
fragment的生命周期是受其依附的activity影响,当activity暂停或者停止时,fragment也会同时达到相应的状态。当activity出于运行状态时,fragment才会进入自由的一个生命周期。
其中,比较重要的生命周期方法包括:
- onCreateView:在onCreate之后,用于返回fragment的根视图对象,一般使用InflateLayout.inflate方法,从xml资源文件转换成View对象。
- onActivityCreated:在其依附的activity的onCreate方法成功返回后触发,用于做进一步操作,比如创建一个接口用于回调给activity,这时候可以检验activity是否实现了该接口。
- 创建方式
包括两种,一种是直接在xml文件中做配置:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:orientation="horizontal" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent"> 6 <fragment android:name="com.example.news.ArticleListFragment" 7 android:id="@+id/list" 8 android:layout_weight="1" 9 android:layout_width="0dp" 10 android:layout_height="match_parent" /> 11 <fragment android:name="com.example.news.ArticleReaderFragment" 12 android:id="@+id/viewer" 13 android:layout_weight="2" 14 android:layout_width="0dp" 15 android:layout_height="match_parent" /> 16 </LinearLayout>
其中的name属性实际就是具体指定fragment的实例,id和tag两个属性都可以用来标识fragment,但是一般用id;若是无界面的fragment,则用tag来标识。
另外一种是在代码中动态的添加:
1 FragmentManager fragmentManager = getFragmentManager()
2 FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
3 fragmentTransaction.add(R.id.replace);
其中的R.id.fragment_container可以看作成是定义好的fragment容器,也即是xml中定义的fragment节点,但是没有指定过name或者class属性;是否也可以看作成一个任意的ViewGroup对应的资源Id?两者取其一还是都可以?
2. 管理fragment
主要是通过FragmentManager对象来操作,主要负责:
- 创建transaction,进行fragment的添加、删除、替换等事物操作;
- 查找fragment,这个fragmentManager对象的引用是否依附于activity中的,通过getFragmentManager()方法可以直接获取该对象。通过findFragmentById获取findFragmentByTag来查找当前activity下存在的fragment;
- 执行返回键的操作,出栈已存放的fragment事物;
transaction提交之前的所有添加删除操作看成一个事物,当调用addToBackStack方法后,会将之前所有发生的改变看作一次改变,再返回回来的时候,是展示所有这改变之前的状态。
3. 与activity进行交互
fragment毕竟是独立出来的组件,那么fragment中发生的操作如何让activity也捕捉到呢?activity又如何向fragment传送值呢?
让activity捕捉到,无非就是在fragment中触发某种操作后,比如点击按钮后,然后fragment通知activity,然后activity再做出相关的响应。那么fragment如何在恰当的时候通知activity呢?sdk中提供了一种做法,在fragment中定义了一个接口,里面定义了一个方法;然后在onAttach时,判断当前依附的activity有没有实现该接口,同时引用已经实现了该接口的activity对象,用来强制实现;在fragment的比如button事件中,直接调用该引用的注册方法,通知activity来做处理。如下:
public class Fragment_A extends Fragment{ private OnChangedListener m_Listener; public interface OnChangedListener{ public void onChanged(int index); } @Override public void onAttach(Activity activity){ super.onAttach(activity); try{ m_Listener=(OnChangedListener)activity; }catch(ClassCastException ex){ throw new ClassCastException(activity.toString() + " 必须实现OnChangedListener!"); } }
Activity实现了OnChangedListener就可以接收到对应的事件,这个是不是就是传说中的监听者模式。
那么activity如何向fragment传递数据呢?
Fragment类包括两个方法:setArguments和getArguments。setArguments方法必须在fragment attach到activity之前调用,参数是Bundle对象;然后在fragment中调用getArguments方法,获取该fragment实例化时传递进来的Bundle对象。
4.保存状态
fragment的生命很大程度上会受到其依附的activity的映像,因此为了保证数据完整性,也需要在适当的时候保存下当前状态:
1 @Override 2 public void onSaveInstanceState(Bundle outState) { 3 super.onSaveInstanceState(outState); 4 outState.putInt("curChoice", mCurCheckPosition); 5 }
然后当该fragment再次被重新创建的时候,恢复之前保存的数据:
1 @Override 2 public void onActivityCreated(Bundle savedInstanceState) { 3 super.onActivityCreated(savedInstanceState); 4 5 if (savedInstanceState != null) { 6 // Restore last state 7 } 8 }
总结:
- fragment的使用能够让UI更灵活
- 感觉如何管理fragment是个关键
其中,图片引用自 android sdk官网,转载请说明出处~~