不用ViewPager和Fragment实现滑动页面的效果
2016-11-04 11:10
543 查看
这是一篇被逼出来的文章。
一入SDK深似海,从此jar包是路人,没错,你以为我愿意不用ViewPager和Fragment啊,因为SDK为了减少包体大小不能用v4的包啊!坑爹的v4包居然有1M多,你们可真能写啊。我相信一定有朋友会建议说,把v4包里相关的类抠出来用啊,呵呵哒,祝你抠的愉快。
言归正传,ViewPager和Fragment那是一套相当庞大的界面框架,想要自己实现一个功能相似且能完美的控制内存和界面生命周期,短期单人几乎是不可能完成的任务,我们只能退而求其次,把底层复杂的逻辑都剥离,再保证没有内存泄漏的情况下,实现界面上看起来相似的功能。大概分析一下滑动界面的需求,抽象出来看就是有N个宽度和屏幕宽度(或者window宽度)一样的界面排排坐,当用户滑动的时候不是缓缓过度到下一个页面,而是有一个弹性效果直接到达下一个页面,每一个页面的容器就是Fragment,而N个页面的容器,就是ViewPager,ViewPager的容器就是我们的Activity。
理解了这个,我们就可以考虑用其他容器来代替ViewPager和Fragment了,横向滑动的第一选择当然是HorizontalScrollView,而Fragment和PageAdapter只能我们自己来实现了,本质就是个View。
直接上代码,先自定义一个HorizontalScrollView来实现ViewPager的功能:
核心代码在onTouchEvent方法里,熟悉安卓触摸事件并了解下拉刷新原理的童鞋应该一看就懂的代码,就不赘述了,无非就是判断用户手势在横向上的滑动距离,当超过一定距离就认为用户是主动滑动到下一页,通过scoller帮助用户来实现这个滑动的弹性效果。
这里我们用到了GestureDetector这个类,目的是为了解决滑动冲突的问题,后面我会再单独安排文章讲这个事儿,这里先不表。
有了“ViewPager”,下面就是Fragment了,我们先定义一个接口:
其实Fragment的本质就是一个View控制器,为了简单,这里就写几个主要的回调了。然后Fragment和ViewPager直接的黏合剂PageAdapter我们也仿照着写一个
其中container就是我们设置的父容器,一般情况下都是ViewPager,第二个方法大家一看就知道不用多说,第三个方法是给子类初始化具体的fragment来用的,好,针对我们的ViewController,我们来扩展这个类:
这里再回去看一下上面自定义HorizontalScrollView的setAdapter方法,其实就把ViewPager中的根布局作为container传给Adapter,然后adapter中会把设定好的ViewController所有view添加到container中。
好,容器和适配器都有了,下面我们根据我们的界面需求去添加具体的ViewController就行了,贴一个例子:
删除了所有涉及公司业务的代码,不过大概框架各位也能看懂了,因为不能用xml来构建界面,这里还需要构建一套替代的框架,其实就是把所有View构建的代码拉出去独立成一个体系就好啦,很简单,不再赘述。眼尖的童鞋应该还看到了代码里的V和Presenter,没错,这里还尝试使用了最新的MVP架构来解耦界面控制与数据操作,后面再开新文章讲。
最后在Activity里把这些元素组织到一起就大功告成了:
好的api就是要让使用者用起来和他最熟悉的一模一样,这也是每个写框架的童鞋要给自己最起码的要求。最后贴两张效果图:
再安利一下我大移动的咪咕游戏开放平台:
http://g.10086.cn/open/
就酱~
一入SDK深似海,从此jar包是路人,没错,你以为我愿意不用ViewPager和Fragment啊,因为SDK为了减少包体大小不能用v4的包啊!坑爹的v4包居然有1M多,你们可真能写啊。我相信一定有朋友会建议说,把v4包里相关的类抠出来用啊,呵呵哒,祝你抠的愉快。
言归正传,ViewPager和Fragment那是一套相当庞大的界面框架,想要自己实现一个功能相似且能完美的控制内存和界面生命周期,短期单人几乎是不可能完成的任务,我们只能退而求其次,把底层复杂的逻辑都剥离,再保证没有内存泄漏的情况下,实现界面上看起来相似的功能。大概分析一下滑动界面的需求,抽象出来看就是有N个宽度和屏幕宽度(或者window宽度)一样的界面排排坐,当用户滑动的时候不是缓缓过度到下一个页面,而是有一个弹性效果直接到达下一个页面,每一个页面的容器就是Fragment,而N个页面的容器,就是ViewPager,ViewPager的容器就是我们的Activity。
理解了这个,我们就可以考虑用其他容器来代替ViewPager和Fragment了,横向滑动的第一选择当然是HorizontalScrollView,而Fragment和PageAdapter只能我们自己来实现了,本质就是个View。
直接上代码,先自定义一个HorizontalScrollView来实现ViewPager的功能:
/** * * @author Amuro * */ public class ScrollViewPager extends HorizontalScrollView { public interface OnPageChangedListener { void onChange(int index); } private OnPageChangedListener listener; public void setOnPageChangedListener(OnPageChangedListener listener) { this.listener = listener; } private void notifyPageChanged() { if (lastPage != currentPage) { lastPage = currentPage; if (listener != null) { listener.onChange(currentPage); } } } private int subChildCount = 0; private int downX = 0; private int lastPage = 0; private int currentPage = 0; private ArrayList<Integer> pointList = new ArrayList<Integer>(); public ScrollViewPager(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public ScrollViewPager(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ScrollViewPager(Context context) { super(context); init(); } private GestureDetector mGestureDetector; private void init() { setHorizontalScrollBarEnabled(false); mGestureDetector = new GestureDetector(getContext(), new HScrollDetector()); } // Return false if we're scrolling in the y direction class HScrollDetector extends SimpleOnGestureListener { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { if (Math.abs(distanceX) > Math.abs(distanceY)) { return true; } return false; } } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: downX = (int) ev.getX(); break; } return super.onInterceptTouchEvent(ev) && mGestureDetector.onTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { // case MotionEvent.ACTION_DOWN: // downX = (int) ev.getX(); // break; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { if (Math.abs((ev.getX() - downX)) > getWidth() / 4) { if (ev.getX() - downX > 0) { smoothScrollToPrePage(); } else { smoothScrollToNextPage(); } notifyPageChanged(); } else { smoothScrollToCurrent(); } return true; } } return super.onTouchEvent(ev); } private void smoothScrollToCurrent() { smoothScrollTo(pointList.get(currentPage), 0); } private void smoothScrollToNextPage() { if (currentPage < subChildCount - 1) { currentPage++; smoothScrollTo(pointList.get(currentPage), 0); } } private void smoothScrollToPrePage() { if (currentPage > 0) { currentPage--; smoothScrollTo(pointList.get(currentPage), 0); } } public boolean gotoPage(int page) { if (page > 0 && page < subChildCount - 1) { smoothScrollTo(pointList.get(page), 0); currentPage = page; notifyPageChanged(); return true; } return false; } public void setAdapter(PagerAdapter<?> adapter) { LinearLayout container = (LinearLayout) this.getChildAt(0); adapter.setContainer(container); adapter.notifyDatasetChanged(); // receiveChildInfo(); subChildCount = adapter.getCount(); for (int i = 0; i < subChildCount; i++) { pointList.add(0 + Constants.HOME_VIEW_WIDTH * i); } } }
核心代码在onTouchEvent方法里,熟悉安卓触摸事件并了解下拉刷新原理的童鞋应该一看就懂的代码,就不赘述了,无非就是判断用户手势在横向上的滑动距离,当超过一定距离就认为用户是主动滑动到下一页,通过scoller帮助用户来实现这个滑动的弹性效果。
这里我们用到了GestureDetector这个类,目的是为了解决滑动冲突的问题,后面我会再单独安排文章讲这个事儿,这里先不表。
有了“ViewPager”,下面就是Fragment了,我们先定义一个接口:
public interface IViewController { View getView(); void create(); void onShow(); }
其实Fragment的本质就是一个View控制器,为了简单,这里就写几个主要的回调了。然后Fragment和ViewPager直接的黏合剂PageAdapter我们也仿照着写一个
public abstract class PagerAdapter<T> { protected List<T> pages; protected ViewGroup container; public PagerAdapter(List<T> pages) { this.pages = pages; } protected void setContainer(ViewGroup container) { this.container = container; } public abstract int getCount(); public abstract void notifyDatasetChanged(); protected abstract T instantiateItem(int positioin); }
其中container就是我们设置的父容器,一般情况下都是ViewPager,第二个方法大家一看就知道不用多说,第三个方法是给子类初始化具体的fragment来用的,好,针对我们的ViewController,我们来扩展这个类:
package cn.cmgame2_0.launch_model.shortcut.main; import java.util.List; import cn.cmgame2_0.launch_model.shortcut.main.V.base.IViewController; import cn.cmgame2_0.utils.custom_view.PagerAdapter; public class HomeViewPageAdapter extends PagerAdapter<IViewController> { public HomeViewPageAdapter(List<IViewController> pages) { super(pages); } @Override public int getCount() { return pages.size(); } @Override protected IViewController instantiateItem(int positioin) { IViewController vc = pages.get(positioin); container.addView(vc.getView()); return vc; } @Override public void notifyDatasetChanged() { container.removeAllViews(); for(int i = 0; i < getCount(); i++) { instantiateItem(i); } } }
这里再回去看一下上面自定义HorizontalScrollView的setAdapter方法,其实就把ViewPager中的根布局作为container传给Adapter,然后adapter中会把设定好的ViewController所有view添加到container中。
好,容器和适配器都有了,下面我们根据我们的界面需求去添加具体的ViewController就行了,贴一个例子:
public class RecommendViewController extends ShortcutViewController implements RecommendV { private RecommendPresenter presenter; private long lastRefreshTime = 0; public RecommendViewController(Context context) { super(context); } private GridView gridViewInstalled; private InstalledAdapter installedAdapter; private GridView gridViewRecommend; private RecommendAdapter recommendAdapter; private Button buttonRefresh; private ProgressDialog progressDialog; @Override public void create() { presenter = new RecommendPresenter(this); rootView = new RecommendView(context); initView(); } private void initView() { gridViewInstalled = (GridView)findViewById(RecommendView.id_gv_installed); gridViewRecommend = (GridView)findViewById(RecommendView.id_gv_recommend); buttonRefresh = (Button)findViewById(RecommendView.id_bt_refresh); progressDialog = DialogUtils.getProgressDialog(context); installedAdapter = new InstalledAdapter(context, presenter.getInstalledGames()); gridViewInstalled.setAdapter(installedAdapter); gridViewInstalled.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { } }); recommendAdapter = new RecommendAdapter(context, presenter.getRecommendGames()); gridViewRecommend.setAdapter(recommendAdapter); buttonRefresh.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { } }); } @Override public void onShow() { } @Override public void showLoading() { progressDialog.show(); } @Override public void hideLoading() { progressDialog.dismiss(); } @Override public void onError(String errorCode, String errorMsg) { ToastUtils.showToast(context, ""); } @Override public void onDataFetched(List<RecommendBean> data) { recommendAdapter.notifyDataSetChanged(); } @Override public Context getContext() { return context; } }
删除了所有涉及公司业务的代码,不过大概框架各位也能看懂了,因为不能用xml来构建界面,这里还需要构建一套替代的框架,其实就是把所有View构建的代码拉出去独立成一个体系就好啦,很简单,不再赘述。眼尖的童鞋应该还看到了代码里的V和Presenter,没错,这里还尝试使用了最新的MVP架构来解耦界面控制与数据操作,后面再开新文章讲。
最后在Activity里把这些元素组织到一起就大功告成了:
private void initViewPager() { if(bean.collectionList == null || bean.collectionList.size() == 0) { pageCount = 1; } else { pageCount = bean.collectionList.size() + 1; } final List<IViewController> vcList = new ArrayList<IViewController>(); for(int i = 0; i < pageCount; i++) { IViewController vc = null; if(i == 0) { vc = new RecommendViewController(this); vc.create(); } else { vc = new CollectionViewController(this, i - 1); vc.create(); } vcList.add(vc); } HomeViewPageAdapter adapter = new HomeViewPageAdapter(vcList); viewPager.setAdapter(adapter); viewPager.setOnPageChangedListener(new OnPageChangedListener() { @Override public void onChange(int index) { indicatorManager.change(index); vcList.get(index).onShow(); } }); }
好的api就是要让使用者用起来和他最熟悉的一模一样,这也是每个写框架的童鞋要给自己最起码的要求。最后贴两张效果图:
再安利一下我大移动的咪咕游戏开放平台:
http://g.10086.cn/open/
就酱~
相关文章推荐
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果(牛逼)
- Android ViewPager + Fragment实现滑动页面效果
- Android开发之ViewPager结合Fragment实现滑动页面的效果(源代码分享)
- Android使用TabLayou+fragment+viewpager实现滑动切换页面效果
- TabLayout 和ViewPager和Fragment的多页面滑动(主要实现一个水平的布局用来展示Tabs加上ViewPager实现联动效果)
- Android 利用ViewPager实现底部圆点导航左右滑动效果以及Fragment页面切换
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果(转载)
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- 利用viewpager、Fragment、pagertabStrip 实现多页面滑动效果
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- 【Android 界面效果27】利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- Fragment嵌套ViewPager加上RadioButton实现微信页面滑动点击效果
- 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- 如何利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- 【Android 界面效果27】利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- Android中用ViewPager实现多页面滑动切换及动画效果的实例
- ViewPager,LayoutInflater实现页面滑动效果。