城市地址选择器-PopupWindow+ListView实现

本文介绍了一种在Android应用中使用PopupWindow和ListView实现城市地址的联动选择器。通过添加数据并分级处理,实现了省、市、区的联动效果,当选择不同层级时,只显示相应级别的列表。同时,提供了全部地区选项,并详细展示了代码实现和Activity调用过程。
摘要由CSDN通过智能技术生成

转载地址:https://blog.csdn.net/ctianju/article/details/117877789

背景

需求: 需要地址筛选,得到选择的省、市、区ID传给接口加载出列表;

求解: App中城市地址筛选器选择不同地址,查询后台接口刷新列表,网上大多类似IOS风格滚轮的地址选择器,查找无果,就简单通过PopWindow内嵌ListView实现联动选择;先上效果图:
在这里插入图片描述
在这里插入图片描述

描述

产品经理可能要的需求:
一、没有全部地区,默认显示:省–>市—>区
二、省有全部地区,市没全部地区,区有全部地区,
三、省、市、区都有全部地区.
产品可能想要的联动效果:
一、打开就显示省的列表,市的列表,区的列表,默认选中第一个;
二、打开显示省列表,默认选中,市列表不展示,区也不;选中省才显示市列表,选中市才显示区;

我的需求是第二种,联动效果市第二种;

其实不同的需求,我们都可以做到,下面会讲;

因为要控制弹窗显示位置,还有收回、弹出效果就想到了PopupWindow,内部采用三个ListView实现联动效果,界面每次打开和关闭PopupWindow就行,所以不用记录之前选择的状态;

主要PopupWindow代码

public class AreasSelectPopWindow extends PopupWindow {
    private static final String TAG = AreasSelectPopWindow.class.getName();
    private final int DEFAULT_POSITION = 0; // 默认选择的位置为 0
    private Context context;
    private View view;
    private LinearLayout parentView;

    //省的listView
    private ListView provinceListview;
    // 省级adapter
    private AreaTextAdapter provinceAdapter;
    //市的listView
    private ListView cityListview;
    // 市adapter
    private AreaTextAdapter cityAdapter;
    //区的listView
    private ListView areaListView;
    // 区adapter
    private AreaTextAdapter areaAdapter;

    private View layer;// 遮罩层

    //当前选中省联动的市级别
    private ArrayList<AreaEntity> citys;
    当前选中市 联动的区级别
    private ArrayList<AreaEntity> areas;

    //当前省市区的选中位置,
    public int provincePosition = 0;
    public int cityPosition = 0;
    public int areasPosition = 0;

    private ResultCallBack textBack;  // 选中结果返回
    private ArrayList<AreaEntity> areaListProvince = new ArrayList<>();// 省级
    private final Map<String, ArrayList<AreaEntity>> areaMapCity = new HashMap<>(); // 市级
    private final Map<String, ArrayList<AreaEntity>> areaMap = new HashMap<>(); // 区级
    private int provinceId;//省Id
    private int cityId;//市Id
    private int areaId;//区Id
    private String selectAreaName; // 当前选中的名字


    /**
     * 进行分级省,市、区
     */
    public void addData(ArrayList<AreaEntity> list) {
        if (null != list && list.size() > 0) {
            AreaEntity allArea = new AreaEntity("0", "-1", "全部地区", "-1", new ArrayList<>());
            areaListProvince.add(allArea);// 全部地区
            areaListProvince.addAll(list);//一级 省  直接赋值

            for (int i = 0; i < areaListProvince.size(); i++) {
                ArrayList<AreaEntity> cityEntitys = areaListProvince.get(i).getChildren();
                areaMapCity.put(areaListProvince.get(i).getAreaName(), cityEntitys);//二级  市  循环赋值
                for (int j = 0; j < cityEntitys.size(); j++) {

                    ArrayList<AreaEntity> areaEntityArrayList = new ArrayList<>();
                    areaEntityArrayList.add(allArea);
                    areaEntityArrayList.addAll(cityEntitys.get(j).getChildren());

                    areaMap.put(cityEntitys.get(j).getAreaName(), areaEntityArrayList);
                }
            }
        }
        init();
    }

    public AreasSelectPopWindow(Context context) {
        super(context);
        this.context = context;
        this.view = View.inflate(context, R.layout.select_areas_pop, null);
        setContentView(view);
//        setWidth(LinearLayout.LayoutParams.WRAP_CONTENT);
//        setHeight(LinearLayout.LayoutParams.WRAP_CONTENT);
//        setTouchable(true);
//        setFocusable(true);
//        setOutsideTouchable(true);
        setBackgroundDrawable(new ColorDrawable());
    }

    /**
     * 最后结果回调
     */
    public interface ResultCallBack {
        void textBack(String areaName, int proviceId, int crtyId, int areaId);
    }

    public void setTextBackListener(ResultCallBack textBack) {
        this.textBack = textBack;
    }


    private void init() {
        layer = view.findViewById(R.id.dissmiss);
        provinceListview = view.findViewById(R.id.listview1);
        parentView = view.findViewById(R.id.parent);
        cityListview = view.findViewById(R.id.listview2);
        areaListView = view.findViewById(R.id.listview3);
        initDefault();
        layer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismiss();
            }
        });

        provinceListview.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                                    int position, long id) {

                provincePosition = position;
                // 选择省Id
                provinceId = Integer.valueOf(areaListProvince.get(provincePosition).getAreaId());

                provinceAdapter.setSelectedPosition(provincePosition);
                if (position == 0) { // 点击了全部地区省
                    selectAreaName = areaListProvince.get(provincePosition).getAreaName();
                    textBack.textBack(selectAreaName, provinceId, 0, 0);
                    cityAdapter.setData(new ArrayList<>());
                    areaAdapter.setData(new ArrayList<>());
//                    AreasSelectPopWindow.this.dismiss();
                    return;
                }
                citys = areaMapCity.get(areaListProvince.get(provincePosition).getAreaName());
                cityAdapter.setData(citys);

//                cityAdapter.setSelectedPosition(DEFAULT_POSITION);
//                cityPosition = DEFAULT_POSITION; // 此处必须手动赋值默认值,防止不点击中间栏
                cityListview.setOnItemClickListener(new AdapterView.OnItemClickListener() {

                    @Override
                    public void onItemClick(AdapterView<?> parent, View view,
                                            int position, long id) {

                        cityPosition = position; // 被点击就直接赋值
                        cityAdapter.setSelectedPosition(cityPosition);

                        selectAreaName = citys.get(cityPosition).getAreaName();
                        // 选择市Id
                        cityId = Integer.valueOf(citys.get(cityPosition).getAreaId());

                        areas = areaMap.get(citys.get(cityPosition).getAreaName());
                        areaAdapter.setData(areas);
//                        areaAdapter.setSelectedPosition(DEFAULT_POSITION);
                    }
                });

//                // 点击完省就要显示区,位置不能变
//                areas = areaMap.get(citys.get(DEFAULT_POSITION).getAreaName());
                areaAdapter.setData(new ArrayList<>());
//                areaAdapter.setSelectedPosition(DEFAULT_POSITION);
            }
        });

        areaListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent,
                                    View view, int position, long id) {
                areasPosition = position;
                if (position != 0) {
                    selectAreaName = areas.get(areasPosition).getAreaName();
                }
                areaAdapter.setSelectedPosition(areasPosition);

                areaId = Integer.valueOf(areas.get(areasPosition).getAreaId());

                // 选择区Id

//                provinceId = Integer.valueOf(areaListProvince.get(provincePosition).getAreaId());
//                cityId = Integer.valueOf(areaMapCity.get(province).get(cityPosition).getAreaName());
//                area = areaMap.get(city).get(areasPosition).getAreaName();

                textBack.textBack(selectAreaName, provinceId, cityId, areaId);
            }
        });
    }

    /**
     * 默认显示以及初始化、设置适配器
     * 第一次打开显示。默认显示第一个
     */
    private void initDefault() {

        //-------------------------------------------------------------------

        provinceAdapter = new AreaTextAdapter(context, areaListProvince);
        provinceListview.setAdapter(provinceAdapter);
        provincePosition = DEFAULT_POSITION;
        provinceAdapter.setSelectedPosition(DEFAULT_POSITION);

        //显示区域屏幕一半
        int scrheight = DensityUtil.getScreenHeight(context) / 2;
        View listItem = provinceAdapter.getView(0, null, provinceListview);
        listItem.measure(0, 0);
        int relheight = listItem.getMeasuredHeight();
        if (relheight * areaListProvince.size() > scrheight) {
            FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) parentView.getLayoutParams();
            layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT;
            layoutParams.height = scrheight;
            this.parentView.setLayoutParams(layoutParams);
        }

//        citys = areaMapCity.get(areaListProvince.get(DEFAULT_POSITION).getAreaName());
//        cityPosition = DEFAULT_POSITION;
//
        cityAdapter = new AreaTextAdapter(context, new ArrayList<>());
//        cityAdapter.setSelectedPosition(DEFAULT_POSITION);
        cityListview.setAdapter(cityAdapter);
//
//
//        areasPosition = DEFAULT_POSITION;
//        areas = areaMap.get(citys.get(DEFAULT_POSITION).getAreaName());
        areaAdapter = new AreaTextAdapter(context, new ArrayList<>());
//        areaAdapter.setSelectedPosition(DEFAULT_POSITION);
        areaListView.setAdapter(areaAdapter);
    }
}

注视:

  1. 加入全部地区

:addData这个方法里面把传进来的list进行分级,省、市、区、分开,因为是三个listview,在此就可以对应的加入全部地区的Bean

  1. 最初的展示效果就是注释的,initDefault()内部那些注释的方法,放开可以看到进去默认选中了,但是在listView里面打开联动的效果了,这个我后面封装下成可配置的,目前线上代码,如果需要自己修改也行的;
  2. 值得一提是:
    setTouchable(true); setFocusable(true); setOutsideTouchable(true);
    这三个方法是让外部点击不消失的,可以让我们点击其他事件,所以popupWindow的消失我们需要自己写点击事件,手动去dissmiss();

Activity调用

public class MainActivity extends AppCompatActivity {
    private LinearLayout selectLL;
    private TextView selectTv;
    private AreasSelectPopWindow areasPopWindow;
    private ArrayList<AreaEntity> arrayList;
    //------- ID 需要传递给后台的,可选---------
    private int provinceId; //省ID
    private int cityId;//市ID
    private int areaId;// 区ID

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        selectLL = findViewById(R.id.selectLL);
        selectTv = findViewById(R.id.selectTv);
        selectLL.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showAreaPop();
            }
        });
        initView();
        initData();
    }

    private void initView() {
        areasPopWindow = new AreasSelectPopWindow(this);
        areasPopWindow.setTextBackListener(new AreasSelectPopWindow.ResultCallBack() {
            @Override
            public void textBack(String mareaName, int mproviceId, int mcrtyId, int mareaId) {
                selectTv.setText(mareaName);
                provinceId = mproviceId;
                cityId = mcrtyId;
                areaId = mareaId;
                dismissAllPop();

            }
        });
        areasPopWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() {
                checkUiUpdate(selectTv, false);
            }
        });
    }

    /**
     * 初始化数据
     */
    private void initData() {
        parseFile2Json();
        areasPopWindow.addData(arrayList);

    }

    /**
     * 真是popView
     */
    private void showAreaPop() {
        if (areasPopWindow != null && areasPopWindow.isShowing()) {
            dismissAllPop();
        } else {
            checkUiUpdate(selectTv, true);
            areasPopWindow.showAsDropDown(selectLL, 0, 0);
        }
    }

    /**
     * 从assert文件夹中读取省市区的json文件,然后转化为json对象
     */
    private void parseFile2Json() {
        try {
            StringBuffer sb = new StringBuffer();
            InputStream is = getAssets().open("areas.json");
            int len = -1;
            byte[] buf = new byte[1024];
            while ((len = is.read(buf)) != -1) {
                sb.append(new String(buf, 0, len, "utf-8"));
            }
            is.close();
            arrayList = new Gson().fromJson(sb.toString(), new TypeToken<List<AreaEntity>>() {
            }.getType());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭所有的popWindow
     */
    private void dismissAllPop() {
        if (areasPopWindow != null && areasPopWindow.isShowing()) {
            areasPopWindow.dismiss();
        }
    }

    /**
     * 选中点击的文字
     */
    private void checkUiUpdate(TextView tv, boolean flag) {
        tv.setSelected(flag);
    }
}

Github 地址

总结:

此选择器,就是popupWindow+Listview效果,每次点击弹出popupWindow即可,特别注意是地区json字符串,如果不是这样的格式需要我们自己去处理下,花点时间;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值