尝试Fragment与ViewPager结合的最佳实践

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执行了: ");
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值