实现快速选择的城市列表组件-字母索引

1.背景

项目有时候需要选择城市,来跳转到不同的业务逻辑中。所以有必要对各个业务线提供一个公共的组件。

2.思路

在这里插入图片描述
很多时候,城市列表有几块逻辑,城市列表,热门城市,当前城市,甚至历史记录。但是具体什么模块,又不清楚业务需要。所以需要抽取。
1.整个列表多为多布局存在,但是这里不是普通的多布局,是经过包装适配器的形式来做的。
2.内容的处理,如当前位置是一个特别的布局结构,所以作为一个单独的布局,剩下的就和列表类似,所以可以作为普通的布局结构,只是需要区分数据的类别而已。
3.忽略每个条目内部城市的排列形式,大家基于业务不同,item的形式不同。
4.注意每个分类的组标签,如 A B,这里使用itemDecroation来做的,需要了解相关间隔线的逻辑。
5.字母的快速索引使用自定义view,来绘制每个字母出来,同时处理点击和滑动的事件逻辑。且和列表进行交互。

3.code

3.1 城市列表

  mCitylistlayout = findViewById(R.id.cityListLayout);
//添加当前位置数据和热门、普通数据列表
        mCitylistlayout.addCurrLocation(cityBean, R.layout.item_city);
        mCitylistlayout.addCitySpecialData("热门", hotlist);
        mCitylistlayout.addCityList(allList);
        mCitylistlayout.setItemClickListener(new CityListLayout.ItemClickListener() {
    
    
            @Override
            public void headerViewClick(CityBean cityBean) {
    
    
                Toast.makeText(MainActivity2.this, "" + cityBean.toString(), Toast.LENGTH_SHORT).show();
            }

            @Override
            public void flowItemClick(CityBean cityBean) {
    
    

                Toast.makeText(MainActivity2.this, "" + cityBean.toString(), Toast.LENGTH_SHORT).show();
            }
        });

适配器相关逻辑


        if (mAdapter == null) {
    
    
            //处理普通数据的适配器
            mAdapter = new BaseCityAdapter(getContext());
        }
        //处理头部数据的适配器,eg: 当前位置
        mHeaderAdapter = new Header_FooterWrapperAdapter(mAdapter) {
    
    
            @Override
            protected void onBindHeaderHolder(ViewHolder holder, int headerPos, int layoutId, CityBean cityBean) {
    
    
                holder.setText(R.id.location, cityBean.getcName());
                holder.getView(R.id.location).setOnClickListener(new OnClickListener() {
    
    
                    @Override
                    public void onClick(View v) {
    
    
                        if (itemClickListener!=null){
    
    
                            itemClickListener.headerViewClick(cityBean);
                        }
                    }
                });
            }
        };
        mHeaderAdapter.addHeaderView(layoutid, currCityBean);

		//添加普通数据列表
        mAdapter.setDataMap(hashMap);
		//添加自定义间隔线,在此处是字母组名
        addItemDecoration();
        //如果没有头部适配器,说明没有添加头部类型
        if (mHeaderAdapter == null) {
    
    
            recyclerView.setAdapter(mAdapter);
        } else {
    
    
            recyclerView.setAdapter(mHeaderAdapter);
        }

3.2 字母组名

    private void addItemDecoration() {
    
    
        //添加自定义分割线----此处是字母组名
        mDecoration = new SuspensionDecoration(getContext(), hashMap);
        if (mHeaderAdapter != null) {
    
    
            mDecoration.setHeaderViewCount(mHeaderAdapter.getHeaderViewCount());
        }
        recyclerView.addItemDecoration(mDecoration);
        //如果add两个,那么按照先后顺序,依次渲染。
        recyclerView.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL));
    }
    public SuspensionDecoration(Context context, HashMap<String, List<CityBean>> hashMap) {
    
    
        super();
        this.hashMap = hashMap;
        mPaint = new Paint();
        mBounds = new Rect();
        mTitleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, context.getResources().getDisplayMetrics());
        mTitleFontSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, context.getResources().getDisplayMetrics());
        mPaint.setTextSize(mTitleFontSize);
        mPaint.setAntiAlias(true);
        mInflater = LayoutInflater.from(context);
    }

  @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
    
    
        super.onDraw(c, parent, state);
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
    
    
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams();
            int position = params.getViewLayoutPosition();
            position -= getHeaderViewCount();
            //pos为1,size为1,1>0? true
            if (hashMap == null || hashMap.isEmpty() || position > hashMap.size() - 1 || position < 0 /**|| !hashMap.get(position).isShowSuspension()**/) {
    
    
                continue;//越界
            }
            if (position >=0) {
    
    
                if (position == 0) {
    
    
                    drawTitleArea(c, left, right, child, params, position);

                } else {
    
    

                    drawTitleArea(c, left, right, child, params, position);

                }
            }
        }
    }



   @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    
    

        super.getItemOffsets(outRect, view, parent, state);
        int position = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
        position -= getHeaderViewCount();

        if (hashMap == null || hashMap.isEmpty() || position > hashMap.size() - 1) {
    
    //pos为1,size为1,1>0? true
            return;//越界
        }

        if (position >= 0) {
    
    

            String suspensionFirstWord = getSuspensionFirstWord(position);

            if (position == 0) {
    
    
                outRect.set(0, mTitleHeight, 0, 0);
            } else if (!suspensionFirstWord.equals(getSuspensionFirstWord(position - 1))) {
    
    
                outRect.set(0, mTitleHeight, 0, 0);
            }
        }
    }



   @Override
    public void onDrawOver(Canvas c, final RecyclerView parent, RecyclerView.State state) {
    
    //最后调用 绘制在最上层

        int pos = ((LinearLayoutManager) (parent.getLayoutManager())).findFirstVisibleItemPosition();
        pos -= getHeaderViewCount();

        if (hashMap == null || hashMap.isEmpty() || pos > hashMap.size() - 1 || pos < 0) {
    
    
            return;//越界
        }

        String tag = getSuspensionFirstWord(pos);

        View child = parent.findViewHolderForLayoutPosition(pos + getHeaderViewCount()).itemView;

        boolean flag = false;//定义一个flag,Canvas是否位移过的标志

        if (null != tag && !tag.equals(getSuspensionFirstWord(pos + 1))) {
    
    //当前第一个可见的Item的tag,不等于其后一个item的tag,说明悬浮的View要切换了

            if (child.getHeight() + child.getTop() < mTitleHeight) {
    
    
                c.save();
                flag = true;

                c.translate(0, child.getHeight() + child.getTop() - mTitleHeight);
            }
        }

        mPaint.setColor(COLOR_TITLE_BG);
        c.drawRect(parent.getPaddingLeft(), parent.getPaddingTop(), parent.getRight() - parent.getPaddingRight(), parent.getPaddingTop() + mTitleHeight, mPaint);

        mPaint.setColor(COLOR_TITLE_FONT);
        mPaint.getTextBounds(tag, 0, tag.length(), mBounds);
        c.drawText(tag, child.getPaddingLeft() + defaultPaddingLeft, parent.getPaddingTop() + mTitleHeight - (mTitleHeight / 2 - mBounds.height() / 2), mPaint);
        if (flag)
            c.restore();


    }

代码参考:https://github.com/cts33/CityListView

猜你喜欢

转载自blog.csdn.net/chentaishan/article/details/119897589