最近项目中遇到了这么个需求,妈的竟然和高德地图实现一模一样的功能。因为保密性原则,我就直接上高德地图的截图了。

首先这么一步操作,输入起始点,

之后呢,就进入这个界面

看到这里,大家应该清楚我说的需求吧,好吧,不讲逻辑,单单讲一下这个界面 实现。因为数据都是动态生成的,每次搜索的结果对应的list的大小和内容都是不一样的,所以呢,我们做这个界面的时候,也需要遵循数据源定的游戏规则,我们界面上也要做成动态的。并且是可复用的,这样不论是在操作还是性能上,都会很好。

这个布局主要分两大部分,

1,头部的水平recycle +指示器

2,头部以下的分级列表

好,分析完大的结构,然后我们再来看一下他们的关系,头部的滑动,会影响到下面列表内容的展示,实施更新对应的数据。

所以我们需要对recycle 的滑动做监听,目的是有两个:1,及时更新指示器 ; 2,及时更新分级列表内容。

下面分享一下我个人这个界面的实现过程。将Recycleview 设置成水平滑动模式,很简单,就这么一句话。

hLinearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(hLinearLayoutManager);

恩,这就实现了水平滑动,但是单单实现水平滑动是不够的,滑动的距离要是一个item 的宽度才行,这样才能像高仿的viewpager。那么很关键的东西出现了。Scroller ,处理recycle滑动分页的工具类。我们自定义的Scorller 继承于recycleview 的onscroollIstener。

最开始是看到这位兄台的博客得到的灵感:一行代码让RecyclerView变身ViewPager

因为他没有实现 指示器的功能,所以我在他的基础上,把指示器添加了进来。下面是我自己的Scroller:

public class PagingScrollHelper {RecyclerView mRecyclerView = null;

    private RouteTopAdapter myAdapter = null;

    private MyOnScrollListener mOnScrollListener = new MyOnScrollListener();

    private MyOnFlingListener mOnFlingListener = new MyOnFlingListener();
    private int offsetY = 0;
    private int offsetX = 0;

    int startY = 0;
    int startX = 0;

    private PageIndicatorView mIndicatorView = null;

    private int index = 0;

    private int totalPage = 0;

    enum ORIENTATION {HORIZONTAL, VERTICAL, NULL
    }ORIENTATION mOrientation = ORIENTATION.HORIZONTAL;

    public void setUpRecycleView(RecyclerView recycleView) {if (recycleView == null) {throw new IllegalArgumentException("recycleView must be not null");
        }mRecyclerView = recycleView;
        //处理滑动
        recycleView.setOnFlingListener(mOnFlingListener);
        //设置滚动监听,记录滚动的状态,和总的偏移量
        recycleView.setOnScrollListener(mOnScrollListener);
        //记录滚动开始的位置
        recycleView.setOnTouchListener(mOnTouchListener);
        //获取滚动的方向
        updateLayoutManger();
    }public void updateLayoutManger() {RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
        if (layoutManager != null) {if (layoutManager.canScrollVertically()) {mOrientation = ORIENTATION.VERTICAL;
            } else if (layoutManager.canScrollHorizontally()) {mOrientation = ORIENTATION.HORIZONTAL;
            } else {mOrientation = ORIENTATION.NULL;
            }if (mAnimator != null) {mAnimator.cancel();
            }startX = 0;
            startY = 0;
            offsetX = 0;
            offsetY = 0;

        }}ValueAnimator mAnimator = null;

    public class MyOnFlingListener extends RecyclerView.OnFlingListener {@Override
        public boolean onFling(int velocityX, int velocityY) {if (mOrientation == ORIENTATION.NULL) {return false;
            }//获取开始滚动时所在页面的index
            int p = getStartPageIndex();

            //记录滚动开始和结束的位置
            int endPoint = 0;
            int startPoint = 0;

            //如果是垂直方向
            if (mOrientation == ORIENTATION.VERTICAL) {startPoint = offsetY;

                if (velocityY < 0) {p--;
                } else if (velocityY > 0) {p++;

                }//更具不同的速度判断需要滚动的方向
                //注意,此处有一个技巧,就是当速度为0的时候就滚动会开始的页面,即实现页面复位
                endPoint = p * mRecyclerView.getHeight();

            } else {startPoint = offsetX;
                if (velocityX < 0) {p--;
                } else if (velocityX > 0) {p++;
                }endPoint = p * mRecyclerView.getWidth();

            }if (endPoint < 0) {endPoint = 0;
            }//使用动画处理滚动
            if (mAnimator == null) {mAnimator = new ValueAnimator().ofInt(startPoint, endPoint);

                mAnimator.setDuration(300);
                mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Override
                    public void onAnimationUpdate(ValueAnimator animation) {int nowPoint = (int) animation.getAnimatedValue();

                        if (mOrientation == ORIENTATION.VERTICAL) {int dy = nowPoint - offsetY;
                            //这里通过RecyclerView的scrollBy方法实现滚动。
                            mRecyclerView.scrollBy(0, dy);
                        } else {int dx = nowPoint - offsetX;
                            mRecyclerView.scrollBy(dx, 0);
                        }}});
                mAnimator.addListener(new AnimatorListenerAdapter() {@Override
                    public void onAnimationEnd(Animator animation) {//回调监听
                        if (null != mOnPageChangeListener) {mOnPageChangeListener.onPageChange(getPageIndex());
                        }}});
            } else {mAnimator.cancel();
                mAnimator.setIntValues(startPoint, endPoint);
            }mAnimator.start();

            return true;
        }}public class MyOnScrollListener extends RecyclerView.OnScrollListener {@Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {//newState==0表示滚动停止,此时需要处理回滚
            if (newState == 0 && mOrientation != ORIENTATION.NULL) {boolean move;
                int vX = 0, vY = 0;
                if (mOrientation == ORIENTATION.VERTICAL) {int absY = Math.abs(offsetY - startY);
                    //如果滑动的距离超过屏幕的一半表示需要滑动到下一页
                    move = absY > recyclerView.getHeight() / 2;
                    vY = 0;

                    if (move) {vY = offsetY - startY < 0 ? -1000 : 1000;
                    }} else {int absX = Math.abs(offsetX - startX);
                    move = absX > recyclerView.getWidth() / 2;
                    if (move) {vX = offsetX - startX < 0 ? -1000 : 1000;
                    }}mOnFlingListener.onFling(vX, vY);

            }}@Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {//滚动结束记录滚动的偏移量
            offsetY += dy;
            offsetX += dx;
        }}private MyOnTouchListener mOnTouchListener = new MyOnTouchListener();

    public class MyOnTouchListener implements View.OnTouchListener {@Override
        public boolean onTouch(View v, MotionEvent event) {//手指按下的时候记录开始滚动的坐标
            if (event.getAction() == MotionEvent.ACTION_DOWN) {startY = offsetY;
                startX = offsetX;
            }return false;
        }}private int getPageIndex() {int p = 0;
        if (mOrientation == ORIENTATION.VERTICAL) {p = offsetY / mRecyclerView.getHeight();
        } else {p = offsetX / mRecyclerView.getWidth();
        }mIndicatorView.setSelectedPage(p);

        return p;
    }private int getStartPageIndex() {int p = 0;
        if (mOrientation == ORIENTATION.VERTICAL) {p = startY / mRecyclerView.getHeight();
        } else {p = startX / mRecyclerView.getWidth();
        }return p;
    }onPageChangeListener mOnPageChangeListener;

    public void setOnPageChangeListener(onPageChangeListener listener) {mOnPageChangeListener = listener;
    }public interface onPageChangeListener {void onPageChange(int index);
    }public void setIndicator(PageIndicatorView indicatorView) {this.mIndicatorView = indicatorView;
    }public void setAdapter(RecyclerView.Adapter adapter) {this.myAdapter = (RouteTopAdapter) adapter;
        update();
    }// 更新页码指示器和相关数据
    private void update() {int temp = myAdapter.getItemCount();
        if (temp != totalPage) {mIndicatorView.initIndicator(temp);
            if (temp < totalPage && index == totalPage) {index = temp;

            }mIndicatorView.setSelectedPage(index);
            totalPage = temp;
        }}}

上面有详细的注释,这个类如果你想用的话,可以直接去copy。

这个是Indicator类的代码

public class PageIndicatorView extends LinearLayout {private Context mContext = null;
    private int dotSize = 15; // 指示器的大小(dp)
    private int margins = 4; // 指示器间距(dp)
    private List<View> indicatorViews = null; // 存放指示器

    public PageIndicatorView(Context context) {this(context, null);
    }public PageIndicatorView(Context context, AttributeSet attrs) {this(context, attrs, 0);
    }public PageIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
        init(context);
    }private void init(Context context) {this.mContext = context;

        setGravity(Gravity.CENTER);
        setOrientation(HORIZONTAL);

        dotSize = dip2px(context, dotSize);
        margins = dip2px(context, margins);
    }// 初始化指示器,默认选中第一页

    public void initIndicator(int count) {if (indicatorViews == null) {indicatorViews = new ArrayList<>();
        } else {indicatorViews.clear();
            removeAllViews();
        }View view;
        LayoutParams params = new LayoutParams(dotSize, dotSize);
        params.setMargins(margins, margins, margins, margins);
        for (int i = 0; i < count; i++) {view = new View(mContext);
            view.setBackgroundResource(android.R.drawable.presence_invisible);
            addView(view, params);
            indicatorViews.add(view);
        }if (indicatorViews.size() > 0) {indicatorViews.get(0).setBackgroundResource(android.R.drawable.presence_offline);
        }}//设置选中页
    public void setSelectedPage(int selected) {for (int i = 0; i < indicatorViews.size(); i++) {if (i == selected) {indicatorViews.get(i).setBackgroundResource(android.R.drawable.presence_offline);
            } else {indicatorViews.get(i).setBackgroundResource(android.R.drawable.presence_invisible);
            }}}}

这两个关键类已经给出。其他的就是自己来实例化Adapter  ,在XML布局中添加 PagerIndicatorView控件。

绑定adapter 和 scroller。即可。

不明白的可以咨询我。

使用recycleview 实现viewpager 功能,并带有指示器。(仿高德交通路线规划实现)相关推荐

  1. android 通过scheme唤起百度、高德、腾讯地图路线规划功能,唤起滴滴出行打车功能

    import android.content.Context; import android.content.Intent; import android.net.Uri;import java.ut ...

  2. android 流程指示,Android实现带有指示器的进度条

    背景 当我们看到UI给我们设计的效果的时候,我们习惯性的思路就是看看google有没有为我们提供相应的控件或者是能否在网上找到一些合适的轮子拿过来直接用.但是,有时候很不巧的是没有这样的轮子供我们直接 ...

  3. 百度步行导航加poi搜索android,Android Studio百度地图路线规划以及POI搜索功能的实现...

    在Eclipse上开发百度地图的教程比较多,最近用的比较多的是Android Studio平台开发,本文主要是学习如何在Android Studio上进行百度地图的开发. 1.准备工作 这第一步网上说 ...

  4. 华为近场通讯nfc在哪里打开_华为手机怎么使用NFC功能?华为手机使用NFC交通卡功能教程...

    NFC(Near Field Communication)即近距离无线通讯技术(近场通信),是一种非接触式识别和互联技术,可以在移动设备.消费类电子产品等设备间进行近距离无线通信.通过 NFC 可实现 ...

  5. 百度地图API制作类似 百度地图的路线导航界面并实现简单的路线规划功能

    之前我们讲了怎么在百度地图上设置Marker(如A点..) 和弹出框(跟随Marker的,Marker移动的时候也是会跟着移动的),接着又觉得百度地图自带的放大缩小不(fei)是(chang)很(de ...

  6. 百度地图多点路线规划_自驾游必备,多地点路线规划功能已经出炉了!!!

    对于喜欢自驾游的小伙伴们,元旦节假就快要开始啦.大家打算怎么计划自己的旅行呢? 身为一个拖延患者,我通常会拖到出发前的最后一个晚上,花上半小时,在网上搜索当地感兴趣的景点 (POI), 然后在百度地图 ...

  7. android 圆点指示器,ViewPager加上小圆点指示器效果

    分析 环境 环境:Android Studio 4.0 语言:Java 特点:简单,易懂,效果爆炸 效果 效果2.gif ViewPager类的来历 ViewPager是android扩展包v4包中的 ...

  8. RecycleView和ViewPager冲突解决与原理

    1.概述 在实际开发中,我们经常遇到需要在ListView或RecycleView头部添加ViewPager实现Banner轮播效果,并需要添加下拉刷新,上拉加载功能. 但,横向滑动ViewPager ...

  9. NestedScrollView、RecycleView、ViewPager 嵌套常见问题

    在开发中我们经常会用到 NestedScrollView 和 RecycleView,一般情况下这两种布局是不需要进行嵌套的,很多情况下 RecycleView 就可以自行解决,但是毕竟是一般情况,因 ...

最新文章

  1. oracle右对齐,Oracle 学习笔记(基础)
  2. 传华西村将斥资亿元涉足网游业
  3. [Python3] 003 变量类型概述 数字类型详叙
  4. pku 1191 棋盘分割 DP / 记忆化搜索
  5. 在大城市打拼的你,是想留下还是想攒够了钱回家?
  6. sklearn自学指南(part24)--随机梯度下降
  7. 部署项目,所谓“部署”到底是在干什么?
  8. 四大跨平台的APP分析
  9. matlab中nc文件,教程合集 | MATLAB文件读写(以nc与txt为例)
  10. java web 编程技术 pdf_Java WEB编程技术.pdf
  11. 如何实现实时文本过滤
  12. 51单片机基础之OLED
  13. java就业班学什么呀_传智播客JAVA就业班的学习心得
  14. 5GC核心网之UPF
  15. 微信支付宝刷步数_一劳永逸版(在用)
  16. led屏背后线路安装图解_液晶拼接屏安装方法图解及接线方法
  17. 揭开docker的神秘面纱?镜像制作
  18. 腾讯云手游安全MTP怎么样?腾讯云手游安全MTP值得入手吗?
  19. css 软键盘,移动端键盘,数字键盘效果实现
  20. 爬虫练习--豆瓣英美剧爬虫

热门文章

  1. AppleID忘记密保问题,同时未开启双重认证,需要签署新的协议才能进行下载或者开发的情况处理
  2. 2018年科研大事件——科学和伦理之间的较量
  3. SpringBoot热部署--bunny0728
  4. vue 重写element input限制字数
  5. 计算机上电自检的过程,电脑每次开机都自检的几种解决方法介绍
  6. 疫情数据分析平台工作报告【2】接口API
  7. 【ATcode】怪文書 / Dubious Document(题意)
  8. 计算机三级考试网络技术资料,计算机三级考试2017网络技术辅导资料
  9. ThreadPoolExecutor中addWorker,continue retry和break retry是什么意思
  10. java中retry的使用