【Swing】Java Swing实现省市区选择编辑器
- 1.需求描述
- 2.需求实现
- 3.效果展示
系统:Win10
JDK:1.8.0_351
IDEA:2022.3.3
1.需求描述
在公司的一个 Swing 的项目上需要实现一个选择省市区的编辑器,这还是第一次做这种编辑器,在前端实现各种地区选择编辑器都比较常见,反而是 Swing 实现还是第一次,故将结果记录下来。
2.需求实现
地区选择从原理上来看很简单,通过级联的方式,将某个对象的所属对象数据展示出来,比如先选择省,就将省下的市等数据展示出来再选。
在 Swing 里我选择使用JPopupMenu 的方式来展示省市区的选择,相对于JDialog 来说,JPopupMenu 的级联操作更加简单一点。
实现代码如下:
import com.lijinjiang.beautyeye.BeautyEyeLNFHelper;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
/**
* @Description 地址选择器demo
* @Author 03010430
* @ModifyDate 2024/3/13 13:20
*/
public class CityPickerDemo {
public static JTextField textField;
public static JPopupMenu popupMenu;
public static DefaultListModel<String> provinceModel;
public static JList<String> provinceList;//省份列表
public static DefaultListModel<String> cityModel;
public static JList<String> cityList;//城市列表
public static DefaultListModel<String> districtModel;
public static JList<String> districtList;//区县列表
public static boolean haveInit;//初始化完毕
public static void main(String[] args) {
try {
BeautyEyeLNFHelper.frameBorderStyle = BeautyEyeLNFHelper.FrameBorderStyle.generalNoTranslucencyShadow;
BeautyEyeLNFHelper.launchBeautyEyeLNF();
} catch (Exception e) {
e.printStackTrace();
}
JFrame frame = new JFrame();
JPanel panel = new JPanel(null);
JLabel label = new JLabel("选择地址");
label.setBounds(10, 30, 80, 36);
panel.add(label);
textField = new JTextField();
textField.setEditable(false);
textField.setBounds(100, 30, 270, 36);
textField.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
clickFieldAction();
}
});
panel.add(textField);
frame.add(panel);
frame.setSize(400, 150);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置默认关闭操作
frame.setVisible(true);
}
public static void clickFieldAction() {
popupMenu = new JPopupMenu();//初始化菜单面板
provinceModel = new DefaultListModel<>();
provinceList = new JList<>(provinceModel);//省份列表
cityModel = new DefaultListModel<>();
cityList = new JList<>(cityModel);//城市列表
districtModel = new DefaultListModel<>();
districtList = new JList<>(districtModel);//区县列表
haveInit = false;//初始化赋值
popupMenu.setLayout(new GridLayout(1, 3));
//添加省份模块
JScrollPane scrollPane1 = new JScrollPane(provinceList);
scrollPane1.setBorder(BorderFactory.createTitledBorder("省级"));
popupMenu.add(scrollPane1);
//添加城市模块
JScrollPane scrollPane2 = new JScrollPane(cityList);
scrollPane2.setBorder(BorderFactory.createTitledBorder("地级/县级"));
popupMenu.add(scrollPane2);
//添加区县模块
JScrollPane scrollPane3 = new JScrollPane(districtList);
scrollPane3.setBorder(BorderFactory.createTitledBorder("县级"));
popupMenu.add(scrollPane3);
String text = textField.getText();
if (text != null && !text.isEmpty()) {
initProvinceData();
String[] arr = text.trim().split("/");
if (arr.length > 0) {
String oldProvince = arr[0];
if (provinceModel.contains(oldProvince)) {
provinceList.setSelectedValue(oldProvince, true);
}
if (arr.length > 1) {
String oldCity = arr[1];
if (cityModel.contains(oldCity)) {
cityList.setSelectedValue(oldCity, true);
}
if (arr.length > 2) {
String oldDistrict = arr[2];
if (districtModel.contains(oldDistrict)) {
districtList.setSelectedValue(oldDistrict, true);
}
}
}
}
} else {
initProvinceData();//初始化省份列表
}
haveInit = true;//标识初始化结束
showPopupMenu(textField, popupMenu);
}
//第一级:省级
public static void initProvinceData() {
try {
List<String> list = getProvinceData();//模拟省份数据
for (String str : list) {
provinceModel.addElement(str);
}
//添加事件监听
provinceList.addListSelectionListener(e -> {
String province = provinceList.getSelectedValue();
if (province != null && !province.isEmpty()) {
initCityData(province);
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//第二级:地级/县级
public static void initCityData(String province) {
try {
cityModel.removeAllElements();//清空城市数据
districtModel.removeAllElements();//清空区县数据
List<String> list = getCityData(province);//模拟城市数据
if (haveInit && list.size() == 0) {
//第二级没有数据时,直接第一级的数据填写上
textField.setText(province);//取值
popupMenu.setVisible(false); //隐藏菜单
popupMenu.removeAll(); //移除所有菜单项
} else {
for (String str : list) {
cityModel.addElement(str);
}
//添加事件监听
cityList.addListSelectionListener(e -> {
String selectedProvince = provinceList.getSelectedValue();
String selectedCity = cityList.getSelectedValue();
if (selectedCity != null && !selectedCity.isEmpty()) {
initDistrictData(selectedProvince, selectedCity);
}
});
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void initDistrictData(String province, String city) {
try {
districtModel.removeAllElements();//先清空区县数据
List<String> list = getDistrictData(province,city);//模拟区县数据
if (haveInit && list.size() == 0) {
//区县没有数据时,直接将市的数据填写上
String value = province + "/" + city;
textField.setText(value);//取值
popupMenu.setVisible(false); //隐藏菜单
popupMenu.removeAll(); //移除所有菜单项
} else {
for (String str : list) {
districtModel.addElement(str);
}
//添加事件监听
districtList.addListSelectionListener(e -> {
if (haveInit) {
String selectedDistrict = districtList.getSelectedValue();
if (selectedDistrict != null && !selectedDistrict.isEmpty()) {
String selectedProvince = provinceList.getSelectedValue();
String selectedCity = cityList.getSelectedValue();
String value = selectedProvince + "/" + selectedCity + "/" + selectedDistrict;
textField.setText(value);//取值
popupMenu.setVisible(false); //隐藏菜单
popupMenu.removeAll(); //移除所有菜单项
}
}
});
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//点击按钮事件,继承该类的编辑器都要复写改代码
public static void showPopupMenu(JComponent parent, JPopupMenu popupMenu) {
GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
Rectangle maximumWindowBounds = graphicsEnvironment.getMaximumWindowBounds();
double deskHeight = maximumWindowBounds.getHeight();//桌面高度(不包括任务栏)
int width = parent.getWidth();
if (width < 400) {
width = 400;
}
popupMenu.setPreferredSize(new Dimension(width, 200));
int popupMenuBottom = parent.getLocationOnScreen().y + parent.getHeight() + 200;
if (deskHeight < popupMenuBottom) {//超出桌面高度显示在输入框上面
popupMenu.show(parent, parent.getWidth() - width, -200);
} else {
popupMenu.show(parent, 0, parent.getHeight());
}
}
//模拟省份数据
private static List<String> getProvinceData() {
List<String> list = new ArrayList<>();
list.add("北京市");
list.add("上海市");
list.add("江西省");
list.add("福建省");
list.add("浙江省");
list.add("江苏省");
list.add("河北省");
list.add("安徽省");
list.add("台湾省");
return list;
}
//模拟城市数据
private static List<String> getCityData(String province) {
List<String> list = new ArrayList<>();
switch (province) {
case "北京市": {
list.add("东城区");
list.add("西城区");
list.add("朝阳区");
break;
}
case "上海市": {
list.add("黄浦区");
list.add("徐汇区");
list.add("长宁区");
list.add("静安区");
break;
}
case "江西省": {
list.add("南昌市");
list.add("景德镇市");
list.add("萍乡市");
list.add("九江市");
list.add("新余市");
list.add("鹰潭市");
list.add("赣州市");
list.add("吉安市");
list.add("宜春市");
list.add("抚州市");
list.add("上饶市");
break;
}
case "福建省": {
list.add("福州市");
list.add("厦门市");
list.add("莆田市");
break;
}
default:
break;
}
return list;
}
private static List<String> getDistrictData(String province, String city) {
List<String> list = new ArrayList<>();
if ("江西省".equals(province)){
if ("南昌市".equals(city)){
list.add("东湖区");
list.add("西湖区");
list.add("青云谱区");
list.add("青山湖区");
list.add("新建区");
list.add("红谷滩区");
list.add("南昌县");
list.add("安义县");
list.add("进贤县");
} else if ("上饶市".equals(city)){
list.add("信州区");
list.add("广丰区");
list.add("广信区");
list.add("玉山县");
list.add("铅山县");
list.add("横峰县");
list.add("弋阳县");
list.add("余干县");
list.add("鄱阳县");
list.add("万年县");
list.add("婺源县");
list.add("德兴市");
}
}
return list;
}
}
3.效果展示
我这里测试代码中的数据只模拟了一部分数据,在生产中根据实际接口获取数据。这里推荐使用 2022年中华人民共和国县以上行政区划代码 国家区划数据