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

来源:互联网 发布:关于养生之道的软件 编辑:程序博客网 时间:2024/05/22 12:39

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


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

之后呢,就进入这个界面






看到这里,大家应该清楚我说的需求吧,好吧,不讲逻辑,单单讲一下这个界面 实现。因为数据都是动态生成的,每次搜索的结果对应的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。即可。


不明白的可以咨询我。

阅读全文
0 0
原创粉丝点击