不用ViewPager和Fragment实现滑动页面的效果
来源:互联网 发布:macd指标公式源码 编辑:程序博客网 时间:2024/06/05 17:21
这是一篇被逼出来的文章。
一入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/
就酱~
- 不用ViewPager和Fragment实现滑动页面的效果
- ViewPager+Fragment 实现滑动页面的效果
- android viewpager和fragment相结合,实现菜单的滑动效果
- Viewpager+Fragment实现页面的滑动
- viewpager+fragment实现滑动效果
- 仿微信和QQ页面滑动效果(Fragment和ViewPager)
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- 利用viewpager、Fragment、pagertabStrip 实现多页面滑动效果
- 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- 如何利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- ViewPager+Fragment实现滑动页面
- ViewPager+Fragment实现滑动页面
- ViewPager + Fragment 实现页面滑动
- Fragment + ViewPager实现滑动页面
- i.MX6UL -- Yocto工程编译过程实践
- 学习UI后转至JAVA后的心得
- 如何提高自己的语言表达能力?
- 【HDU 1757 A Simple Math Problem】+ 矩阵
- Xcode8 解决注释以及VVDocumenter无法使用问题
- 不用ViewPager和Fragment实现滑动页面的效果
- 单点触控
- $("tr:even")--even选择器:选中偶数index值元素
- MyEclipse中MyEclipse Java Enterprise和java视图的区别是什么?
- 旋转数组的最小数字
- C++STL字符串
- android开发游记:RecyclerView无法添加onItemClickListener最佳的高效解决方案
- JSBridge连接JAVA和JS的桥梁
- 拿不定主意的东东 (数学)(欧拉函数)