Android-开源通用弹窗的封装CommonPopupWindow-地区选择弹窗实现

开源通用弹窗目前增加了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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容