美文网首页 Android知识进阶(遥远的重头开始)安卓
Android-开源通用弹窗的封装CommonPopupWind

Android-开源通用弹窗的封装CommonPopupWind

作者: MonkeyLei | 来源:发表于2019-07-21 16:55 被阅读3次

    开源通用弹窗目前增加了9种弹窗(7种类别),包括自定义、警告类、更新类、列表类、地区选择类。小萌新个人项目目前用到了警告类和列表类。地区选择弹窗后面项目升级也需要,所以就搞了搞。

    Github FanChael/CommonPopupWindow

    码云同步 NetNut/CommonPopupWindow

    地区选择弹窗,三级联动效果:

    image

    实现思路:

    1. 三个均分屏幕宽度的垂直Recycleview列表

    2. 省列表采用List<String>参数,市列表根据省的名称获取HashMap<String, List<CityBean>>, 区列表根据市名称获取HashMap<String, List<String>>. 目前暂时就是这样的结构。采用json转对象的话,传递到内部,我的处理还是会处理为对应的结构。

    另外实际垂直列表适配器需要的是List<String>。因此就有以下数据列表

    image

    3. 滑动定位的问题

    3.1 首先滑动到某个pos采用scrollToPositionWithOffset,此时Rv会滑动该条目并且置顶。但是我们需要在中间那个位置

    image

    小萌新想的一个方案就是,列表的前后分别增加两个空字符串条目,总共就是四个。此时想想,第一个真的地区实际上是第2个,要保证第2个居中,那么就需要第1个置顶。如果需要第二个真的地区居中(此时第二个是第3个,从0开始哟),那么就是第二个置顶。

    那么滑动后,如何计算当前应该是哪个居中?请看如下代码:

           /**
             * 定位item到指定的貌似中间的位置
             *
             * @param mRecyclerView
             * @return
             */
            private int changePos(RecyclerView mRecyclerView) {
                LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
                View firstVisibItem = mRecyclerView.getChildAt(0);
                //int firstItemPosition = layoutManager.findFirstVisibleItemPosition();
                int itemCount = layoutManager.getItemCount();
                //int recycleViewHeight = recyclerView.getHeight();
                int itemHeight = firstVisibItem.getHeight();
                //int lastItemPosition = layoutManager.findLastVisibleItemPosition();
                //int lastCItemPosition = layoutManager.findLastCompletelyVisibleItemPosition();
    
                int scrollViewHeight = getDistance(mRecyclerView);
    
                ///< 滚动后需要停留的位置
                ///< 列表前后各加了两个空位置,第1个置顶刚好是我们默认需要停留的位置(此时第0个看不见了)
                ///< 条目高度假设是100, 那么第1个置顶,滚动的距离也scrollViewHeight = 100;
                ///< 再向上或者向下滑动距离不足150的话,还是第1个置顶,具体看图说话才好
                int scrollPos = 1;
                if (scrollViewHeight < (itemHeight + itemHeight / 2)) {
                    scrollPos = 1;
                } else if (scrollViewHeight > itemHeight * (itemCount - 4)) {
                    scrollPos = itemCount - 4;
                } else {
                    scrollPos = (scrollViewHeight + itemHeight / 2) / itemHeight;
                }
                ((LinearLayoutManager) mRecyclerView.getLayoutManager()).scrollToPositionWithOffset(scrollPos, 0);
                return scrollPos;
            }
    
            /**
             * RecyclerView已滑动的距离
             *
             * @param mRecyclerView
             * @return
             */
            private int getDistance(RecyclerView mRecyclerView) {
                LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
                View firstVisibItem = mRecyclerView.getChildAt(0);
                int firstItemPosition = layoutManager.findFirstVisibleItemPosition();
                //int itemCount = layoutManager.getItemCount();
                //int recycleViewHeight = mRecyclerView.getHeight();
                int itemHeight = firstVisibItem.getHeight();
                int firstItemBottom = layoutManager.getDecoratedBottom(firstVisibItem);
                return (firstItemPosition + 1) * itemHeight - firstItemBottom;
            }
    

    其中置顶的条目位置计算如下(真正居中的条目则是改置顶条目的下一个条目):

    image

    先看个图,每个条目高度是100

    image

    1. 此时如果向上滑动了0 - 150的距离,此时居中的那个条目就应该回弹居中。 而上面一个条目则应该scrollToPositionWithOffset置顶,此时滚动的位置就是0。

    2. 此时如果滚动额距离是150 - 250的话,此时中间条目已经滑动过半了。此时则最下面的条目应该居中,此时scrollToPositionWithOffset的位置应该是1,正好第二个置顶,第三个居中。

    一次类推。。。

    此时为了计算这个scrollToPositionWithOffset,统一先将0 - 150, 150 - 250等 加上一个50成为50 - 200(<200) 200 - 300, 300 - 400, 然后再除以100就可以得到整数1, 2, 3。

    而针对上面的第2点,我们滚动了150 - 250 ,加了50成了200 - 300之间。除以100就是2, 貌似多了1. 这个就可以减去1. 但是,因为我们为了滚动前后列表增加了两个空条目,此时滚动如果是2,你想,滚2正好就是我们要居中的条目。 如果前后增加,正常逻辑即可。但是就没有那种空位置的视觉效果。你可以自己推演一下应该怎么计算该滚动多少。。

    至于列表更新代码也贴下,主要是就是地区列表数据滑动选择、更新以及重置位置的处理,此外也保存了下相关值。方便传递...

           /**
             * 滚动监听并定位滑动item居中
             */
            private class MyOnSrollListenner extends RecyclerView.OnScrollListener {
                private int type = 0;
    
                MyOnSrollListenner(int type) {
                    this.type = type;
                }
    
                @Override
                public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                    super.onScrollStateChanged(recyclerView, newState);
                    if (0 == newState) {
                        int scrollPos = changePos(recyclerView);
                        if (0 == type) {         ///< 滑动省
                            currentProvince = provinceData.get(scrollPos + 1);
    
                            ///< 更新市
                            cityData.clear();
                            cityData.addAll(cityList.get(currentProvince));
                            cityAdapter.notifyDataSetChanged();
                            ((LinearLayoutManager) cityRv.getLayoutManager()).scrollToPositionWithOffset(1, 0);
    
                            currentCity = cityData.get(2);
    
                            ///< 更新区
                            districtData.clear();
                            districtData.addAll(districtList.get(currentCity));
                            districtAdapter.notifyDataSetChanged();
                            ((LinearLayoutManager) districtRv.getLayoutManager()).scrollToPositionWithOffset(1, 0);
    
                            currentDistrict = districtData.get(2);
                        } else if (1 == type) {   ///< 滑动市
                            currentCity = cityData.get(scrollPos + 1);
    
                            ///< 更新区
                            districtData.clear();
                            districtData.addAll(districtList.get(currentCity));
                            districtAdapter.notifyDataSetChanged();
                            ((LinearLayoutManager) districtRv.getLayoutManager()).scrollToPositionWithOffset(1, 0);
    
                            currentDistrict = districtData.get(2);
                        } else if (2 == type) {   ///< 滑动区
                            currentDistrict = districtData.get(scrollPos + 1);
                        }
                    }
                }
    
                @Override
                public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);
                }
            }
    
    

    重点是逻辑。本身滚动还好。我看了下好多实现貌似像这样的效果,具体是不是这个逻辑不太清楚呀。 我看了下京东的地区选择,后面空了可以搞搞。感觉实现不难,处理下逻辑应该就ojbk了。 不准说脏话,要开心哟!

    image

    相关文章

      网友评论

        本文标题:Android-开源通用弹窗的封装CommonPopupWind

        本文链接:https://www.haomeiwen.com/subject/gocvlctx.html