Android 弧形列表转盘的实现(二),列表自动选中;RecyclerView滑动后自动选中居中的条目,RecyclerView实现WheelView效果;

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

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

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

列表滑动后自动选中

先来分析一波:

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

Android 弧形列表转盘的实现(二),列表自动选中;RecyclerView滑动后自动选中居中的条目,RecyclerView实现WheelView效果;_第1张图片

红色的这条线是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 弧形列表转盘的实现(二),列表自动选中;RecyclerView滑动后自动选中居中的条目,RecyclerView实现WheelView效果;_第2张图片

下一篇: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;
    }
}

 

你可能感兴趣的:(Android)