ViewPager与Fragment用到的比较多,为避免使用的时候遇到的一些常见问题,这里尝试实现一个比较方便通用的实践。
1、底部导航按钮,用于切换Fragment,可以用radioGroup+radioButton,也可以使用imageButton或TextView都可以。这里用的是TextView,添加图标用android:drawableTop=""
属性。
MainActivity布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.administrator.weatherdemo.MainActivity">
<android.support.v4.view.ViewPager
android:id="@+id/vp_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</android.support.v4.view.ViewPager>
<LinearLayout
android:id="@+id/bottom_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/textview"
style="@style/bottom_textview"
android:text="主页"
/>
<TextView
android:id="@+id/textview2"
style="@style/bottom_textview"
android:text="专题"/>
<TextView
android:id="@+id/textview3"
style="@style/bottom_textview"
android:text="其他"/>
<TextView
android:id="@+id/textview4"
style="@style/bottom_textview"
android:text="设置"/>
</LinearLayout>
</LinearLayout>
2、ViewPager滑动与导航按钮事件,控件点击后设置该enable属性为false,其他的设置为true,相应地设置状态选择器,文字也相应地改变颜色状态。
public class MainActivity extends BaseActivity implements MainPresentInt {
private static final String TAG = "MainActivity";
private TextView textview2;
private TextView textview3;
private TextView textview1;
private TextView textview4;
private NoScrollViewPager vpContainer;
private LinearLayout bottomContainer;
@Override
public void onActivityCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
}
@Override
public void showViewPager() {
ContainViewPagerAdapter adapter = new ContainViewPagerAdapter(this.getSupportFragmentManager());
vpContainer.setAdapter(adapter);
}
private void changeChildEnable(View v) {
int childCount = bottomContainer.getChildCount();
for (int i = 0; i < childCount; i++) {
View view = bottomContainer.getChildAt(i);
if (view instanceof TextView) {
TextView tv = (TextView) view;
tv.setTextColor(Color.parseColor("#000000"));
}
view.setEnabled(true);
}
TextView currentView = (TextView) v;
currentView.setEnabled(false);
currentView.setTextColor(Color.parseColor("#ffffff"));
}
@Override
protected void initView() {
textview2 = (TextView) findViewById(R.id.textview2);
textview3 = (TextView) findViewById(R.id.textview3);
textview1 = (TextView) findViewById(R.id.textview);
textview4 = (TextView) findViewById(R.id.textview4);
vpContainer = (NoScrollViewPager) findViewById(R.id.vp_container);
bottomContainer = (LinearLayout) findViewById(R.id.bottom_container);
Log.i(TAG, "App.getActivityListComponent():" + App.getActivityListComponent());
Log.i(TAG, "mActivities:" + mActivities);
showViewPager();
}
@Override
protected void initListener() {
textview1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
changeChildEnable(v);
switchPager(0);
}
});
textview2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
changeChildEnable(v);
switchPager(1);
}
});
textview3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
changeChildEnable(v);
switchPager(2);
}
});
textview4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
changeChildEnable(v);
switchPager(3);
}
});
vpContainer.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
clickWhickPagerButton(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
//用代码主动点击第一个底部按钮
textview1.performClick();
}
/**
* PagerView滑动时联动导航按钮的状态
* 如果有需求滑动要被禁用,这个方法就不必用到了,实现则可以通过
* 重写ViewPager的onTouchEvent()和onInterceptTouchEvent方法 直接返回false 不进行事件拦截也不消费事件
*
* @param position
*/
private void clickWhickPagerButton(int position) {
switch (position) {
case 0:
changeChildEnable(textview1);
break;
case 1:
changeChildEnable(textview2);
break;
case 2:
changeChildEnable(textview3);
break;
case 3:
changeChildEnable(textview4);
break;
}
}
/**
* 按钮控制ViewPager切换
*
* @param position
*/
public void switchPager(int position) {
//切换动画可以选择有或者无
vpContainer.setCurrentItem(position, false);
}
}
3、ViewPager的数据适配器的设置,可以使用FragmentStatePagerAdapter
或者FragmentPagerAdapter
这里使用
了FragmentPagerAdapter
,适配器会对fragment进行缓存,用方法vpContainer.setOffscreenPageLimit()
可以设置缓存的页面数量,但是如果不想缓存,或者说想在fragment显示的时候刷新数据的话,要么重写FragmentPagerAdapter,要么使用fragment里的setUserVisibleHint()
方法在显示Fragment的时候加载数据。
public class ContainViewPagerAdapter extends FragmentPagerAdapter {
private static int COUNT = 4;
private String TAG = this.getClass().getSimpleName();
public ContainViewPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
//这个方法是在没有Fragment缓存的时候调用的
return FragmentFactory.createFragment(position);
}
@Override
public int getCount() {
return COUNT;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Log.i(TAG, "instantiateItem执行: ");
//这个方法是在读取到Fragment缓存的时候执行的
return super.instantiateItem(container, position);
}
}
4、Fragment构建,根据position来构建指定的Fragment,可以用单例的Map来管理,也可以用工厂类来进行管理。
““java
public class FragmentFactory {
public static String BUNDLE_FLAG = “DATA”;
public static Fragment createFragment(int position) {
Fragment fragment;
switch (position) {
case 0:
fragment = FragmentInstanceManager.getFragmentInstance(HomeFragment.class);
return fragment;
case 1:
fragment = FragmentInstanceManager.getFragmentInstance(TitleFragment.class);
return fragment;
case 2:
fragment = FragmentInstanceManager.getFragmentInstance(OtherFragment.class);
return fragment;
case 3:
fragment = FragmentInstanceManager.getFragmentInstance(SettingFragment.class);
return fragment;
}
return null;
}
}
““
5、用于管理Fragment实例的类,关于Fragment的构造方法传入数据或者set方法设置数据,官方不推荐这种方式,原因是如果程序进程被kill的时候再在任务菜单中打开Fragment恢复的时候会直接调用onCreateView()方法,而不会执行走早方法和set方法,从而之前的数据就为空了,因此,如果需要在Fragment实例创建的时候传入参数可以用bundle传入。
/**
* auther:wzy
* date:2016/12/2 23 :19
* desc: 管理所有的Fragment , fragment的实例只能通过这个类来构造(饿汉式单例)
* 用于取消Fragment+viewPager模式的缓存
*/
public class FragmentInstanceManager {
private FragmentInstanceManager() {
}
private static Map<String, Fragment> mFragments = new HashMap<>();
private static FragmentInstanceManager mInstance = new FragmentInstanceManager();
public static FragmentInstanceManager getInstance() {
return mInstance;
}
public static Fragment getFragmentInstance(Class<? extends Fragment> clazz, Bundle data) {
String key = clazz.getSimpleName();
Fragment fragment = mFragments.get(key);
if (fragment == null) {
synchronized (FragmentInstanceManager.class) {
if (fragment == null) {
fragment = data == null ? Fragment.instantiate(App.getContext(), clazz.getName())
: Fragment.instantiate(App.getContext(), clazz.getName(), data);
mFragments.put(key, fragment);
}
}
}
return fragment;
}
//没有参数的创建
public static Fragment getFragmentInstance(Class<? extends Fragment> clazz) {
return getFragmentInstance(clazz, null);
}
}
6、BaseFragment类的需要做的事,防止页面为空时多次调用onCreateView()方法,如果想在frament显示时就进行数据的加载,就得用到setUserVisibleHint
,这是用于应付ViewPager的缓存机制。另外如果创建实例时传入了bundle的话就要在这里用getArguments();
取出。
public abstract class BaseFragment extends Fragment {
private View rootView;
private String TAG = this.getClass().getSimpleName();
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//判断为空,为空就去加载布局,onCreateView在界面切换的时候会被多次调用,防止界面跳转回来的时候显示空白
if (rootView == null) {
rootView = createView(inflater, container, savedInstanceState);
initView();
initListener();
}
return rootView;
}
abstract protected View createView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);
protected abstract void initView();
protected abstract void initListener();
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
//Fragment可见
lazyLoad();
} else {
//不可见
}
}
/**
* 可见的时候处理一些页面数据刷新的操作,用于应付Fragment被缓存时没有办法进行数据的刷新
*/
protected void lazyLoad() {
Log.i(TAG, "lazyLoad执行了: ");
}
}