转载地址:https://blog.youkuaiyun.com/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);
}
}
注视:
- 加入全部地区
:addData这个方法里面把传进来的list进行分级,省、市、区、分开,因为是三个listview,在此就可以对应的加入全部地区的Bean
- 最初的展示效果就是注释的,initDefault()内部那些注释的方法,放开可以看到进去默认选中了,但是在listView里面打开联动的效果了,这个我后面封装下成可配置的,目前线上代码,如果需要自己修改也行的;
- 值得一提是:
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);
}
}
总结:
此选择器,就是popupWindow+Listview效果,每次点击弹出popupWindow即可,特别注意是地区json字符串,如果不是这样的格式需要我们自己去处理下,花点时间;