时间:2016年5月24日16:44:21
作者:JustDo23
说明:在开发中经常会遇到使用Fragment的情况。由于功能等的需要会对Fragment有一点点深入的理解。现在简单总结记录一下。
01. ViewPager + Fragment
在使用ViewPager嵌套Fragment的时候可能需要有以下的操作
Activity
需要继承子FragmentActivity
-
需要在
xml
布局中添加ViewPager的控件并指定ID,具体代码如下:<!-- 为了兼容需要使用v4包下面的控件 --> <android.support.v4.view.ViewPager android:id="@+id/vp_reserve" android:layout_width="fill_parent" android:layout_height="match_parent"/>
-
需要一个ViewPager的适配器,可以继承自
FragmentPagerAdapter
,同时需要重写一些方法。各个方法有各自不同的作用。import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import java.util.List; /** * 订单管理的ViewPagerIndicator适配器 * * @author JustDo23 */ public class ReserveManageAdapter extends FragmentPagerAdapter { private List<Fragment> fragList;// 数据集合 private String[] TITLES;// 标题 public ReserveManageAdapter(FragmentManager fm, List<Fragment> fragList, String[] TITLES) { super(fm); this.fragList = fragList; this.TITLES = TITLES; } @Override public int getCount() { return fragList.size(); } @Override public Fragment getItem(int arg0) { return fragList.get(arg0); } @Override public int getItemPosition(Object object) { return super.getItemPosition(object); } @Override public CharSequence getPageTitle(int position) { return TITLES[position % TITLES.length]; } }
之后就可以在Activity中
findViewById
找到相应的ViewPager控件对象。进行
List<Fragment>
中各个Item的初始化。Fragment对象的new
。-
如果使用的
Fragment
对象是相同类型的,Fragment需要使用参数
的时候,需要在new
之后用Bundle
对象去传递参数。可以使用代码:Fragment needVisit = new FragReserveList(); needVisit.setArguments(getBundle(0));// [ 0,待处理 ] fragList.add(needVisit); Fragment finished = new FragReserveList(); finished.setArguments(getBundle(1));// [ 1,已处理 ] fragList.add(finished);
-
以上需要传递参数的代码如下
private Bundle getBundle(int type) { Bundle bundle = new Bundle(); bundle.putInt("type", type); return bundle; }
-
在Fragment的
onCreateView
方法中进行参数的获取Bundle bundle = this.getArguments(); if (bundle != null) { type = bundle.getInt("type", -1); }
-
List<Fragment>
初始化之后,进行适配器Adapter的新建与设置。vp_reserve.setAdapter(new ReserveManageAdapter(getSupportFragmentManager(), fragList, TITLES));
-
利用
setCurrentItem(int position)
方法可以设置ViewPager当前显示的位置。一般情况下是不用进行设置的,这个位置是0的位置。vp_reserve.setCurrentItem(showPosition);
至此就基本完成了ViewPager嵌套Fragment的实现步骤了。
02. setUserVisibleHint(boolean isVisibleToUser)方法
一般情况下,在ViewPager嵌套Fragment使用的时候,ViewPager会在初始化的时候一次性初始化2个Fragment,这两个都会走onCreateView
并进入到resume状态
。但是用户是只能看到第一个Fragment,第二Fragment虽然没有显示却同样进入到了resume状态
。此时再用旧的想法当Fragment对用户可见的时候在onResume()方法中处理某些事情,如网络请求等
这种想法就不太科学了。
在这种情况下Fragment中有个setUserVisibleHint(boolean isVisibleToUser)
的方法就派上用场了。这个方法是专门设置Fragment显示和不显示的。
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
if (isFirst && isVisibleToUser && TextUtils.isEmpty(nextUrl)) {
getDataFromServer(ConstantURLs.RESERVE_LIST, true, true);
}
super.setUserVisibleHint(isVisibleToUser);
}
ViewPager会在初始化及切换Fragment的时候调用这个方法来设置Fragment的显示与否。ViewPager会在设置Adapter之后立即调用第一个、第二个fragment的setUserVisibleHint(boolean isVisibleToUser)方法
设置为false
,然后会对第一个fragment再次调用setUserVisibleHint(boolean isVisibleToUser)方法
设置为true
。
需要注意的是setUserVisibleHint(boolean isVisibleToUser)
方法会在ViewPager嵌套Fragment这种情况下调用。别的情况可能不会调用。
03. RadioButton + FrameLayout + Fragment
这一种机制和ViewPager嵌套Fragment是两种完全不同的机制。使用RadioButton这种机制更类似于很早以前的TabHost
的使用。因为之前的TabHost
使用非常复杂而且不好用,所以很多人提出了使用RadioButton + FrameLayout + Fragment
这种比较流行的解决方法。
对于这种有一个参考的demo,仿微信导航栏https://github.com/JayFang1993/android-demos
使用这种方法
Activity
同样需要继承子FragmentActivity
-
在xml中需要使用控件
RadioGroup
和FrameLayout
并指定id<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/ll_root" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white"> <RadioGroup android:id="@+id/rg_nav" android:layout_width="match_parent" android:layout_height="55dip" android:layout_alignParentBottom="true" android:orientation="horizontal"> <RadioButton android:id="@+id/rb_message" style="@style/MainNavigation" android:checked="true" android:drawableTop="@drawable/nav_selector_message" android:text="@string/message"/> <RadioButton android:id="@+id/rb_patient_manage" style="@style/MainNavigation" android:drawableTop="@drawable/nav_selector_patient_manage" android:text="@string/patient_manage"/> <RadioButton android:id="@+id/rb_me" style="@style/MainNavigation" android:drawableTop="@drawable/nav_selector_me" android:text="@string/me"/> </RadioGroup> <!-- 小红点实现,完全可以删除 --> <LinearLayout android:layout_width="match_parent" android:layout_height="55dip" android:layout_alignParentBottom="true" android:orientation="horizontal"> <RelativeLayout style="@style/MainNavigation"/> <RelativeLayout style="@style/MainNavigation"/> <RelativeLayout style="@style/MainNavigation" android:gravity="right|top"> <View android:id="@+id/message_view" android:layout_width="10dp" android:layout_height="10dp" android:layout_marginRight="40dp" android:background="@drawable/shape_red_round" android:visibility="gone"/> </RelativeLayout> </LinearLayout> <FrameLayout android:id="@+id/frag_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@+id/v_divider" android:layout_alignParentTop="true"> </FrameLayout> </RelativeLayout>
在
Activity
中通过findViewById
方法来获取RadioGroup
对象的引用-
设置
RadioGroup
的切换监听rg_nav.setOnCheckedChangeListener(this);
-
监听方法的实现
@Override public void onCheckedChanged(RadioGroup group, int checkedId) { switch (checkedId) { case R.id.rb_message: setTabSelected(0); break; case R.id.rb_patient_manage: setTabSelected(1); break; case R.id.rb_me: setTabSelected(2); break; default: break; } }
-
两个重点方法
private void setTabSelected(int i) { FragmentManager fm = getSupportFragmentManager(); FragmentTransaction transaction = fm.beginTransaction(); hideTab(transaction); switch (i) { case 0: fragMessage = fm.findFragmentByTag("message"); if (fragMessage == null) { fragMessage = new FragMessage(); transaction.add(R.id.frag_container, fragMessage, "message"); } else { transaction.show(fragMessage); } break; case 1: fragPatientManage = fm.findFragmentByTag("patientmanage"); if (fragPatientManage == null) { fragPatientManage = new FragPatientManage(); transaction.add(R.id.frag_container, fragPatientManage, "patientmanage"); } else { transaction.show(fragPatientManage); } break; case 2: fragMe = fm.findFragmentByTag("me"); if (fragMe == null) { fragMe = new FragMe(); transaction.add(R.id.frag_container, fragMe, "me"); } else { transaction.show(fragMe); } break; default: break; } transaction.commitAllowingStateLoss(); } private void hideTab(FragmentTransaction transaction) { if (fragMessage != null) { transaction.hide(fragMessage); } if (fragPatientManage != null) { transaction.hide(fragPatientManage); } if (fragMe != null) { transaction.hide(fragMe); } }
04. onHiddenChanged(boolean hidden)方法
这个方法和setUserVisibleHint(boolean isVisibleToUser)
有些类似。但这个方法一看名称就是很明显是显示和隐藏的方法。在上边RadioButton + FrameLayout + Fragment
模式中的Fragment中可以使用onHiddenChanged(boolean hidden)
方法进行判断,而且setUserVisibleHint(boolean isVisibleToUser)
方法是不会执行的。
05. add() show() hide() 以及 replace()
对于上边提到的RadioButton + FrameLayout + Fragment
模式中使用的主要是第一种add() show() hide()
方式进行Fragment的显示和隐藏。对于Fragment界面的切换还可以使用replace()
的方法进行。
对于这两种方法有各自的特点,暂时没有总结到。
- 使用
replace()
方法,每次会把Fragment的生命周期都走一边。
06. Fragment中的Context为空现象
在Fragment中使用context最常用的就是使用getActivity()
方法进行获取上下文对象。但是有时候总是会出现getActivity()
方法获取的对象为空的情况。这里忘记了当时使用的切换方式,应该是使用replace()
方式的时候经常出现空的现象。
网上搜索后有提到Activity会被系统回收重建,但是Fragment不会,所以出现Fragment丢失Activity的情况。多数的解决方法是在onSaveInstanceState
方法中进行处理。
本人的解决方法是在BaseFragment
中定义mContext
并在其onCreateView
方法中进行获取。简单代码如下:
protected Context mContext;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (rootView == null) {
rootView = inflater.inflate(getContentView(), null);
mContext = getActivity();
initView();
}
ViewGroup parent = (ViewGroup) rootView.getParent();
if (parent != null) {
parent.removeView(rootView);
}
return rootView;
}
另外提供一个搜索关键词fragment getactivity() 为空
最后
以上基本都是本人在工作过程中遇到的东西。工作比较忙碌以及本人写记录的习惯不强等原因。本人能力问题等,本片文档写的比较浅,并不很深。还是需要不断的学习努力,不断的提高。
最后附上一个刚看的连接,主要使用友盟相关http://www.jianshu.com/p/850556d33f63
不断完善,继续努力!
补丁
时间:2016年9月12日15:11:48
作者:JustDo23
简述:样式设置相关的代码做一个补充。
01. RadioButton使用的style
<!-- 导航按钮 -->
<style name="MainNavigation">
<item name="android:layout_width">0dip</item>
<item name="android:layout_height">match_parent</item>
<item name="android:layout_weight">1</item>
<item name="android:button">@null</item>
<item name="android:background">@null</item>
<!-- 消除不同版本上的位置错乱问题 -->
<item name="android:drawablePadding">0dip</item>
<item name="android:gravity">center</item>
<item name="android:scaleX">0.8</item>
<item name="android:scaleY">0.8</item>
<item name="android:paddingTop">3dip</item>
<item name="android:textSize">15sp</item>
<item name="android:textColor">@drawable/selector_text_color_main</item>
</style>
02. 图片使用的selector
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/ic_nav_me_on" android:state_checked="true" android:state_window_focused="false"/>
<item android:drawable="@mipmap/ic_nav_me_off" android:state_checked="false" android:state_window_focused="false"/>
<item android:drawable="@mipmap/ic_nav_me_off" android:state_checked="false"/>
<item android:drawable="@mipmap/ic_nav_me_on" android:state_checked="true"/>
</selector>
03. 小红点
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval"
android:useLevel="false">
<!--小红点-->
<gradient
android:endColor="#F74A28"
android:startColor="#F74A28"/>
</shape>
04. 飘红带数字
<!-- [融云]飘红 -->
<style name="Little_Red_Dot">
<item name="android:background">@drawable/rc_unread_count_bg</item>
<item name="android:gravity">center</item>
<item name="android:textColor">@android:color/white</item>
<item name="android:textSize">9sp</item>
<item name="android:layout_centerVertical">true</item>
<item name="android:layout_alignParentRight">true</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
</style>