这篇主要是列表滑动后停止后,自动选中居中的条目,类似于WheelView的效果;通俗的讲就是用RecyclerView实现WheelView的效果;

接上篇:Android 弧形转盘的实现,弧形列表; 弧形列表已经实现了,下面就是自动选中的功能了;

代码已上传:https://github.com/CuiChenbo/ArcSelectList , 欢迎Star

列表滑动后自动选中

先来分析一波:

如果RecyclerView滑动停止后是下面这个情况,应该把索引4这个条目滑动到中间位置

红色的这条线是RecyclerView的竖向的中心线,当列表滑动停止后遍历可见区域的所有View,计算出距离中心线最近的一个View(是该View的中心点距离中心线最近),然后移动该View至中心线位置;

1、RecyclerView滑动停止后获取可见区域的所有View:

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {            @Override            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {                super.onScrollStateChanged(recyclerView, newState);                if (newState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {                    LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();                    int fi = linearLayoutManager.findFirstVisibleItemPosition();                    int la = linearLayoutManager.findLastVisibleItemPosition();                    Log.i("ccb", "onScrollStateChanged:首个item: " + fi + "  末尾item:" + la);                }            }        });

2、计算出距离中心线最近的一个View;

这个可以粗略的获取到中间的View(粗略获取到中心View):

 int centerPositionDiffer = (la - fi) / 2; int centerChildViewPosition = fi + centerPositionDiffer; //获取当前所有条目中中间的一个条目索引

但是它未必是最靠近中线的一个,我们这个地方需要这个View的前一个和后一个,从这三个View中计算最靠近中线的一个View(精确获取到中心View):

//获取最中间的Item View                        int centerPositionDiffer = (la - fi) / 2;                        int centerChildViewPosition = fi + centerPositionDiffer; //获取当前所有条目中中间的一个条目索引                        centerViewItems.clear();                        //遍历循环,获取到和中线相差最小的条目索引(精准查找最居中的条目)                        if (centerChildViewPosition != 0){                            for (int i = centerChildViewPosition -1 ; i < centerChildViewPosition+2; i++) {                                View cView = recyclerView.getLayoutManager().findViewByPosition(i);                                int viewTop = cView.getTop()+(cView.getHeight()/2);                                centerViewItems.add(new CenterViewItem(i ,Math.abs(centerToTopDistance - viewTop)));                            }                           CenterViewItem centerViewItem = getMinDifferItem(centerViewItems);                            centerChildViewPosition = centerViewItem.position;                        }
  static class CenterViewItem{        public CenterViewItem(int position, int differ) {            this.position = position; //当前Item索引            this.differ = differ; //当前item和居中位置的差值        }        public int position;        public int differ;    }
    /**     * 计算距离中间最近的一个ItemView     * @param itemHeights     * @return     */    private static CenterViewItem getMinDifferItem(List itemHeights){        CenterViewItem minItem = itemHeights.get(0); //默认第一个是最小差值        for (int i = 0; i < itemHeights.size(); i++) {            //遍历获取最小差值            if (itemHeights.get(i).differ <= minItem.differ){                minItem = itemHeights.get(i);            }        }        return minItem;    }

3、移动View至中心线:

 /**     * 移动指定索引到中心处 , 只可以移动可见区域的内容     * @param position     */    private void scrollToCenter(int position){      LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();        View childView = linearLayoutManager.findViewByPosition(position);        Log.i("ccb", "滑动后中间View的索引: " + position);        //把当前View移动到居中位置        if (childView == null) return;        int childVhalf = childView.getHeight() / 2;        int childViewTop = childView.getTop();        int viewCTop = centerToTopDistance;        int smoothDistance = childViewTop - viewCTop + childVhalf;        Log.i("ccb", "\n居中位置距离顶部距离: " + viewCTop                + "\n当前居中控件距离顶部距离: " + childViewTop                + "\n当前居中控件的一半高度: " + childVhalf                + "\n滑动后再次移动距离: " + smoothDistance);        recyclerView.smoothScrollBy(0, smoothDistance,null,500);        mAdapter.setSelectPosition(position);        TUtils.show(AutoSelectActivity.this , "滑动后选中:" + mDatas.get(position));    }

移动RecyclerView条目后,还会再次触发 onScrollStateChanged的回调,需要做一个标记来判断是用户滑动的还是RecuclerView自己滑动的;

recyclerView.setOnTouchListener(new View.OnTouchListener() {            @Override            public boolean onTouch(View view, MotionEvent motionEvent) {                isTouch = true;                return false;            }        });

这个具体实现等下看完整代码;

OK,这样一个RecyclerView滑动后居中选中的功能就好了;但是还有一个问题,前几条和最后几条无法滑动到中间你位置,这个地方还需要处理一下,我这边做法比较简单粗暴,直接给RecyclerView的数据源设置几条空数据,使用空数据把条目填充起来;

所有条目都可以滑动到中心处

1、先计算RecyclerView一屏最多可以显示几个item,然后再除2就是半个RecyclerView可以显示几个Item;

int childViewHeight = UiUtils.dip2px(AutoSelectActivity.this, 43); //43是当前已知的 Item的高度childViewHalfCount = (recyclerView.getHeight() / childViewHeight + 1) / 2;

2、填充空数据;

    private void initData() {        if (mDatas == null) mDatas = new ArrayList<>();        for (int i = 0; i < 55; i++) {            mDatas.add("CAR_Item" + i);        }        for (int j = 0; j < childViewHalfCount; j++) { //头部的空布局            mDatas.add(0, "");        }        for (int k = 0; k < childViewHalfCount; k++) {  //尾部的空布局            mDatas.add("");        }    }

3、自动选中居中条目时不可选择空数据的Item;

  /**     * 移动指定索引到中心处 , 只可以移动可见区域的内容     * @param position     */    private void scrollToCenter(int position){        position = position < childViewHalfCount ? childViewHalfCount : position;        position = position < mAdapter.getItemCount() - childViewHalfCount -1 ? position : mAdapter.getItemCount() - childViewHalfCount -1;        LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();        View childView = linearLayoutManager.findViewByPosition(position);        Log.i("ccb", "滑动后中间View的索引: " + position);        //把当前View移动到居中位置        if (childView == null) return;        int childVhalf = childView.getHeight() / 2;        int childViewTop = childView.getTop();        int viewCTop = centerToTopDistance;        int smoothDistance = childViewTop - viewCTop + childVhalf;        Log.i("ccb", "\n居中位置距离顶部距离: " + viewCTop                + "\n当前居中控件距离顶部距离: " + childViewTop                + "\n当前居中控件的一半高度: " + childVhalf                + "\n滑动后再次移动距离: " + smoothDistance);        recyclerView.smoothScrollBy(0, smoothDistance,null,500);        mAdapter.setSelectPosition(position);        TUtils.show(AutoSelectActivity.this , "滑动后选中:" + mDatas.get(position));    }

好了,这样一个RecyclerView实现的WheelView就完成了;

如果需要点击其它条目自动选中时调用scrollToCenter方法就好了, 如果需要自动选中一个不在屏幕内的条目,需要先调用 scrollToPosition方法:

/**     * 移动指定索引     * @param position     */    private void smoothToPosition(int position){        position = position < childViewHalfCount ? childViewHalfCount : position;        position = position < mAdapter.getItemCount() - childViewHalfCount -1 ? position : mAdapter.getItemCount() - childViewHalfCount -1;        LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();        linearLayoutManager.scrollToPosition(position);    }

好了,上图!!!

代码已上传:https://github.com/CuiChenbo/ArcSelectList , 欢迎Star

再配合弧形列表的效果:

下一篇:Android 弧形转盘的实现(三),View跟随RecyclerView做旋转动画;

 

RecyclerView实现WheelView功能,Activity代码:

 

/** * 滑动后自动选中居中的条目  类似 WheelView */public class AutoSelectActivity extends AppCompatActivity {    private RecyclerView recyclerView;    private MAdapter mAdapter;    private int centerToTopDistance; //RecyclerView高度的一半 ,也就是控件中间位置到顶部的距离 ,    private int childViewHalfCount = 0; //当前RecyclerView一半最多可以存在几个Item    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_auto_select);        recyclerView = findViewById(R.id.rv);        init();    }    private void init() {        recyclerView.setLayoutManager(new LinearLayoutManager(this));        recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {            @Override            public void onGlobalLayout() {                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {                    recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);                }                centerToTopDistance = recyclerView.getHeight() / 2;                int childViewHeight = UiUtils.dip2px(AutoSelectActivity.this, 43); //43是当前已知的 Item的高度                childViewHalfCount = (recyclerView.getHeight() / childViewHeight + 1) / 2;                initData();                findView();            }        });        recyclerView.postDelayed(new Runnable() {            @Override            public void run() {                scrollToCenter(childViewHalfCount);            }        }, 100L);    }    private List mDatas;    private void initData() {        if (mDatas == null) mDatas = new ArrayList<>();        for (int i = 0; i < 55; i++) {            mDatas.add("CAR_Item" + i);        }        for (int j = 0; j < childViewHalfCount; j++) { //头部的空布局            mDatas.add(0, "");        }        for (int k = 0; k < childViewHalfCount; k++) {  //尾部的空布局            mDatas.add("");        }    }    private boolean isTouch = false;    private List centerViewItems = new ArrayList<>();    private void findView() {        mAdapter = new MAdapter();        recyclerView.setAdapter(mAdapter);        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {            @Override            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {                super.onScrollStateChanged(recyclerView, newState);                if (newState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {                    LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();                    int fi = linearLayoutManager.findFirstVisibleItemPosition();                    int la = linearLayoutManager.findLastVisibleItemPosition();//                    int fi = linearLayoutManager.findFirstCompletelyVisibleItemPosition();//                    int la = linearLayoutManager.findLastCompletelyVisibleItemPosition();                    Log.i("ccb", "onScrollStateChanged:首个item: " + fi + "  末尾item:" + la);                    if (isTouch) {                        isTouch = false;                        //获取最中间的Item View                        int centerPositionDiffer = (la - fi) / 2;                        int centerChildViewPosition = fi + centerPositionDiffer; //获取当前所有条目中中间的一个条目索引                        centerViewItems.clear();                        //遍历循环,获取到和中线相差最小的条目索引(精准查找最居中的条目)                        if (centerChildViewPosition != 0){                            for (int i = centerChildViewPosition -1 ; i < centerChildViewPosition+2; i++) {                                View cView = recyclerView.getLayoutManager().findViewByPosition(i);                                int viewTop = cView.getTop()+(cView.getHeight()/2);                                centerViewItems.add(new CenterViewItem(i ,Math.abs(centerToTopDistance - viewTop)));                            }                           CenterViewItem centerViewItem = getMinDifferItem(centerViewItems);                            centerChildViewPosition = centerViewItem.position;                        }                        scrollToCenter(centerChildViewPosition);//                        centerChildViewPosition = centerChildViewPosition < childViewHalfCount ? childViewHalfCount : centerChildViewPosition;//                        centerChildViewPosition = centerChildViewPosition <= mAdapter.getItemCount() - childViewHalfCount -1 ? centerChildViewPosition : mAdapter.getItemCount() - childViewHalfCount -1;//                        View childView = recyclerView.getLayoutManager().findViewByPosition(centerChildViewPosition);//                        Log.i("ccb", "滑动后中间View的索引: " + centerChildViewPosition);////                        //把当前View移动到居中位置//                        if (childView == null) return;//                        int childVhalf = childView.getHeight() / 2;//                        int childViewTop = childView.getTop();//                        int viewCTop = centerToTopDistance;//                        int smoothDistance = childViewTop - viewCTop + childVhalf;//                        Log.i("ccb", "居中位置距离顶部距离: " + viewCTop + "当前居中控件距离顶部距离: " + childViewTop);//                        Log.i("ccb", "滑动后再次移动距离: " + smoothDistance);//                        recyclerView.smoothScrollBy(0, smoothDistance);//                        Toast.makeText(AutoSelectActivity.this, "滑动后选中:" + mDatas.get(centerChildViewPosition), Toast.LENGTH_SHORT).show();//                        mAdapter.setSelectPosition(centerChildViewPosition);                    }                }            }            @Override            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {                super.onScrolled(recyclerView, dx, dy);                for (int i = 0; i < recyclerView.getChildCount(); i++) {                    recyclerView.getChildAt(i).invalidate();                }            }        });        recyclerView.setOnTouchListener(new View.OnTouchListener() {            @Override            public boolean onTouch(View view, MotionEvent motionEvent) {                isTouch = true;                return false;            }        });    }    /**     * 移动指定索引到中心处 , 只可以移动可见区域的内容     * @param position     */    private void scrollToCenter(int position){        position = position < childViewHalfCount ? childViewHalfCount : position;        position = position < mAdapter.getItemCount() - childViewHalfCount -1 ? position : mAdapter.getItemCount() - childViewHalfCount -1;        LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();        View childView = linearLayoutManager.findViewByPosition(position);        Log.i("ccb", "滑动后中间View的索引: " + position);        //把当前View移动到居中位置        if (childView == null) return;        int childVhalf = childView.getHeight() / 2;        int childViewTop = childView.getTop();        int viewCTop = centerToTopDistance;        int smoothDistance = childViewTop - viewCTop + childVhalf;        Log.i("ccb", "\n居中位置距离顶部距离: " + viewCTop                + "\n当前居中控件距离顶部距离: " + childViewTop                + "\n当前居中控件的一半高度: " + childVhalf                + "\n滑动后再次移动距离: " + smoothDistance);        recyclerView.smoothScrollBy(0, smoothDistance,null,500);        mAdapter.setSelectPosition(position);        TUtils.show(AutoSelectActivity.this , "滑动后选中:" + mDatas.get(position));    }    class MAdapter extends RecyclerView.Adapter {        @NonNull        @Override        public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {            return new VH(LayoutInflater.from(AutoSelectActivity.this).inflate(R.layout.item_auto_select, parent, false));        }        @Override        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {            VH vh = (VH) holder;            if (selectPosition == position) {                vh.tv.setTextColor(getResources().getColor(R.color.textSelect));            } else {                vh.tv.setTextColor(getResources().getColor(R.color.colorText));            }            vh.tv.setText(mDatas.get(position));            final int fp = position;            vh.itemView.setOnClickListener(new View.OnClickListener() {                @Override                public void onClick(View v) {                    scrollToCenter(fp);                    Toast.makeText(AutoSelectActivity.this, "点击" + mDatas.get(fp), Toast.LENGTH_SHORT).show();                }            });        }        private int selectPosition = -1;        public void setSelectPosition(int cposition) {            selectPosition = cposition;//            notifyItemChanged(cposition);            notifyDataSetChanged();        }        @Override        public int getItemCount() {            return mDatas.size();        }        class VH extends RecyclerView.ViewHolder {            public TextView tv;            public VH(@NonNull View itemView) {                super(itemView);                tv = itemView.findViewById(R.id.tv);            }        }    }    /**     * 计算距离中间最近的一个ItemView     * @param itemHeights     * @return     */    private static CenterViewItem getMinDifferItem(List itemHeights){        CenterViewItem minItem = itemHeights.get(0); //默认第一个是最小差值        for (int i = 0; i < itemHeights.size(); i++) {            //遍历获取最小差值            if (itemHeights.get(i).differ <= minItem.differ){                minItem = itemHeights.get(i);            }        }        return minItem;    }    public static void main(String[] a){        CenterViewItem i = getMinDifferItem(Arrays.asList(                new CenterViewItem(2 , 39)                ,new CenterViewItem(3 , 3)                ,new CenterViewItem(1 , 9)                ,new CenterViewItem(4 , 449)));       System.out.println("position:"+i.position+"   height:"+i.differ);    }    static class CenterViewItem{        public CenterViewItem(int position, int differ) {            this.position = position; //当前Item索引            this.differ = differ; //当前item和居中位置的差值        }        public int position;        public int differ;    }}

 

更多相关文章

  1. android app -- 关于listview的几种用法(复用,不复用,半复用)解决ite
  2. 关于Android(安卓)draw中的画布的说明
  3. Android开发:APP引导页启动页小Demo(实例)
  4. Android(安卓)项目中常用的页面切换TableLayout+Fragment+ViewPa
  5. Android事件分发机制以及滑动冲突处理
  6. android ViewPager 实现点击小圆点切换页面 案例
  7. Android(安卓)RecyclerView 实现快速滑动
  8. Android基础控件(EditView、SeekBar等)的使用方法
  9. Android(安卓)自定义Spinner显示条目与下拉框的布局

随机推荐

  1. android菜鸟学习笔记4----android项目结
  2. Android(安卓)Studio中aidl的使用示例
  3. 【Android】AndroidManifest 中original-
  4. Android仿qq侧滑——上
  5. Unity与Android交互-基础知识
  6. Android编程入门-第1天
  7. nexus 7(一代)上android和ubuntu多系统启
  8. 最详细的Android(安卓)Bitmap回收机制(从
  9. android逆向概述
  10. Android(安卓)WebView加载页面的输入框被