Android自定义WheelView地区选择三级联动

一、背景介绍

在开发Android应用时,通过使用WheelView控件,我们可以实现像省市区选择器、时间选择器等功能。本文着重介绍如何使用自定义的WheelView控件实现地区选择三级联动的功能。

二、自定义WheelView控件

为了实现三级联动的地区选择功能,我们需要先自定义一个可以支持多级数据的控件。这里我们借鉴开源控件library中的WheelView,并添加一些内容使其支持三级联动地区选择。关于怎样添加一个库到Android Studio的方法,在这里就不再赘述了,在此假设我们已经添加了WheelView控件库。

在WheelView的package命名空间下创建一个新的java类:ProvinceCityAreaPicker.java。这个类中主要包含以下内容:

  1. ViewPager:支持左右滑动三个WheelView同时更新数据,实现三级联动选择的效果。

  2. TabLayout:控制ViewPager,每次只有一个Tab可以被选中。

下面上代码:

public class ProvinceCityAreaPicker extends LinearLayout {

    private static final int PROVINCE_WHEEL_VIEW_INDEX = 0;
    private static final int CITY_WHEEL_VIEW_INDEX = 1;
    private static final int AREA_WHEEL_VIEW_INDEX = 2;

    private Context mContext;
    private TabLayout mTabLayout;
    private ViewPager mViewPager;
    private List<WheelView> mWheelViewList = new ArrayList<>();
    private List<AreaEntity> mProvinceList;
    private List<AreaEntity> mCityList;
    private List<AreaEntity> mAreaList;
    private int mLastSelectedProvincePosition;
    private int mLastSelectedCityPosition;
    private int mLastSelectedAreaPosition;

    public ProvinceCityAreaPicker(Context context) {
        this(context, null);
    }

    public ProvinceCityAreaPicker(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ProvinceCityAreaPicker(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

    private void initView(Context context) {
        mContext = context;
        View contentView = View.inflate(mContext, R.layout.wheelview_layout_province_city_area_picker, this);
        mTabLayout = (TabLayout) contentView.findViewById(R.id.tablayout);
        mViewPager = (ViewPager) contentView.findViewById(R.id.viewpager);
        setUpViewPager();
    }

    private void setUpViewPager() {
        mProvinceList = AreaDataManager.getAllProvince(); //获取所有省份列表
        if (mProvinceList == null || mProvinceList.isEmpty()) {
            return;
        }

        mCityList = AreaDataManager.getCityByProvinceId(mProvinceList.get(0).getId()); //获取第一个省份的城市列表
        if (mCityList == null || mCityList.isEmpty()) {
            return;
        }

        mAreaList = AreaDataManager.getAreaByCityId(mCityList.get(0).getId()); //获取第一个城市的地区列表
        if (mAreaList == null || mAreaList.isEmpty()) {
            return;
        }

        List<View> viewList = new ArrayList<>();
        viewList.add(createWheelView(mContext, mProvinceList, mCityList, mAreaList, PROVINCE_WHEEL_VIEW_INDEX));
        viewList.add(createWheelView(mContext, mCityList, mAreaList, CITY_WHEEL_VIEW_INDEX));
        viewList.add(createWheelView(mContext, mAreaList, null, AREA_WHEEL_VIEW_INDEX));

        mViewPager.setAdapter(new ProvinceCityAreaPagerAdapter(viewList));
        mTabLayout.setupWithViewPager(mViewPager); //TabLayout与ViewPager绑定
        mTabLayout.setTabMode(TabLayout.MODE_FIXED);
        mTabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
        mTabLayout.getTabAt(0).select();
        mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            ...
        });
        ...
    }

    private WheelView createWheelView(Context context, List<AreaEntity> data, List<AreaEntity> data2, List<AreaEntity> data3, int tag) {
        View contentView = LayoutInflater.from(context).inflate(R.layout.wheelview_layout_single, null);
        WheelView wheelView = contentView.findViewById(R.id.wheelview);
        mWheelViewList.add(wheelView);
        wheelView.setItemTextSize(mContext.getResources().getDimensionPixelSize(R.dimen.wheelview_item_text_size));
        wheelView.setWheelAdapter(new ArrayWheelAdapter(mContext));
        wheelView.setSkin(WheelView.Skin.Common);
        wheelView.setWheelSize(5);
        wheelView.setOnWheelItemSelectedListener(new WheelView.OnWheelItemSelectedListener() {
            ...
        });
        wheelView.setTag(tag);
        updateData(data, null, null);
        return wheelView;
    }

    ...
}

可见,在new ProvinceCityAreaPicker时传递的三个参数分别是:Context、AttributeSet、DefStyleAttr,这个类继承了LinearLayout,把ViewPager和TabLayout放在了LinearLayout中。

其中,setUpViewPager()方法主要用于初始化ViewPager和TabLayout。通过AreaDataManager获取所有省份列表,并利用createWheelView创建Title为"省"的WheelView。接着,获取第一个省份的城市列表,创建Title为"市"的WheelView,并设置TabLayout与ViewPager的绑定。下面是createWheelView()方法的详细说明:

  1. 创建一个WheelView,设置数据和样式等。

  2. 设置WheelView的监听器,根据tag标识判断当前选中的是哪一个WheelView,并自动更新下级联动的数据。

  3. 返回创建好的WheelView。

三、数据管理

接下来我们添加一个DataManager数据管理类,这个类是用于获取相应地区的列表信息,同时支持模糊查询,方便根据关键字查询特定地区。下面是DataManager类的具体代码:

public class AreaDataManager {

    private static final String TAG = AreaDataManager.class.getSimpleName();

    private static final String AREA_FILE_NAME = "area.xml";
    private static List<AreaEntity> sProvinceList;

    /**
     * 获取所有省份列表
     *
     * @return 所有省份列表
     */
    public static List<AreaEntity> getAllProvince() {
        InputStream inputStream = null;
        try {
            inputStream = AppContext.getInstance().getAssets().open(AREA_FILE_NAME);
            List<AreaEntity> allAreaList = parseXml(inputStream);
            sProvinceList = new ArrayList<>();
            for (AreaEntity area : allAreaList) {
                if (area.getLevel() == AreaEntity.AREA_LEVEL_PROVINCE) {
                    sProvinceList.add(area);
                }
            }
        } catch (IOException | XmlPullParserException e) {
            e.printStackTrace();
            Log.e(TAG, "getAllProvince: ", e);
        } finally {
            IOUtils.closeQuietly(inputStream);
        }
        return sProvinceList;
    }

    /**
     * 获取城市列表
     *
     * @param provinceId 省份id
     * @return 城市列表
     */
    public static List<AreaEntity> getCityByProvinceId(int provinceId) {
        InputStream inputStream = null;
        try {
            inputStream = AppContext.getInstance().getAssets().open(AREA_FILE_NAME);
            List<AreaEntity> allAreaList = parseXml(inputStream);
            List<AreaEntity> cityList = new ArrayList<>();
            for (AreaEntity area : allAreaList) {
                if (area.getParentId() == provinceId) {
                    if (area.getLevel() == AreaEntity.AREA_LEVEL_CITY) {
                        cityList.add(area);
                    }
                }
            }
            return cityList;
        } catch (IOException | XmlPullParserException e) {
            e.printStackTrace();
            Log.e(TAG, "getCityByProvinceId: ", e);
            return null;
        } finally {
            IOUtils.closeQuietly(inputStream);
        }
    }

    /**
     * 获取所有地区列表
     *
     * @param cityId 城市id
     * @return 所有地区列表
     */
    public static List<AreaEntity> getAreaByCityId(int cityId) {
        InputStream inputStream = null;
        try {
            inputStream = AppContext.getInstance().getAssets().open(AREA_FILE_NAME);
            List<AreaEntity> allAreaList = parseXml(inputStream);
            List<AreaEntity> areaList = new ArrayList<>();
            for (AreaEntity area : allAreaList) {
                if (area.getParentId() == cityId) {
                    areaList.add(area);
                }
            }
            return areaList;
        } catch (IOException | XmlPullParserException e) {
            e.printStackTrace();
            Log.e(TAG, "getAreaByCityId: ", e);
            return null;
        } finally {
            IOUtils.closeQuietly(inputStream);
        }
    }

    /**
     * 获取省份名字
     *
     * @param id 省份id
     * @return id对应的省份名字
     */
    public static String getProvinceNameById(int id) {
        if (sProvinceList == null || sProvinceList.isEmpty()) {
            return "";
        }
        for (AreaEntity province : sProvinceList) {
            if (province.getId() == id) {
                return province.getName();
            }
        }
        return "";
    }

    /**
     * 获取城市名字
     *
     * @param id 城市id
     * @return id对应的城市名字
     */
    public static String getCityNameById(int id) {
        InputStream inputStream = null;
        try {
            inputStream = AppContext.getInstance().getAssets().open(AREA_FILE_NAME);
            List<AreaEntity> allAreaList = parseXml(inputStream);
            for (AreaEntity area : allAreaList) {
                if (area.getId() == id && area.getLevel() == AreaEntity.AREA_LEVEL_CITY) {
                    return area.getName();
                }
            }
        } catch (IOException | XmlPullParserException e) {
            e.printStackTrace();
            Log.e(TAG, "getCityNameById: ", e);
        } finally {
            IOUtils.closeQuietly(inputStream);
        }
        return "";
    }

    /**
     * 获取地区名字
     *
     * @param id 地区id
     * @return id对应的地区名字
     */
    public static String getAreaNameById(int id) {
        InputStream inputStream = null;
        try {
            inputStream = AppContext.getInstance().getAssets().open(AREA_FILE_NAME);
            List<AreaEntity> allAreaList = parseXml(inputStream);
            for (AreaEntity area : allAreaList) {
                if (area.getId() == id && area.getLevel() == AreaEntity.AREA_LEVEL_AREA) {
                    return area.getName();
                }
            }
        } catch (IOException | XmlPullParserException e) {
            e.printStackTrace();
            Log.e(TAG, "getAreaNameById: ", e);
        } finally {
            IOUtils.closeQuietly(inputStream);
        }
        return "";
    }

    private static List<AreaEntity> parseXml(InputStream inputStream) throws XmlPullParserException, IOException {
        List<AreaEntity> areaList = new ArrayList<>();
        XmlPullParser xmlPullParser = Xml.newPullParser();
        xmlPullParser.setInput(new InputStreamReader(inputStream));
        int eventType = xmlPullParser.getEventType();
        AreaEntity areaEntity = null;
        while (eventType != XmlPullParser.END_DOCUMENT) {
            String tagName = xmlPullParser.getName();
            switch (eventType) {
                case XmlPullParser.START_DOCUMENT:
                    break;
                case XmlPullParser.START_TAG:
                    switch (tagName) {
                        case "province":
                            areaEntity = new AreaEntity();
                            areaEntity.setId(Integer.valueOf(xmlPullParser.getAttributeValue(0)));
                            areaEntity.setName(xmlPullParser.getAttributeValue(1));
                            areaEntity.setLevel(AreaEntity.AREA_LEVEL_PROVINCE);
                            areaList.add(areaEntity);
                            break;
                        case "city":
                            areaEntity = new AreaEntity();
                            areaEntity.setId(Integer.valueOf(xmlPullParser.getAttributeValue(0)));
                            areaEntity.setName(xmlPullParser.getAttributeValue(1));
                            areaEntity.setParentId(Integer.valueOf(xmlPullParser.getAttributeValue(2)));
                            areaEntity.setLevel(AreaEntity.AREA_LEVEL_CITY);
                            areaList.add(areaEntity);
                            break;
                        case "area":
                            areaEntity = new AreaEntity();
                            areaEntity.setId(Integer.valueOf(xmlPullParser.getAttributeValue(0)));
                            areaEntity.setName(xmlPullParser.getAttributeValue(1));
                            areaEntity.setParentId(Integer.valueOf(xmlPullParser.getAttributeValue(2)));
                            areaEntity.setLevel(AreaEntity.AREA_LEVEL_AREA);
                            areaList.add(areaEntity);
                            break;
                    }
                    break;
                case XmlPullParser.END_TAG:
                    break;
            }
            eventType = xmlPullParser.next();
        }
        return areaList;
    }

    /**
     * 关键字匹配检索
     *
     * @param keyword 检索关键字
     * @return 检索结果列表
     */
    public static List<AreaEntity> search(String keyword) {
        if (StringUtils.isBlank(keyword)) {
            return new ArrayList<>();
        }

        List<AreaEntity> allAreaList = getAll();
        List<AreaEntity> resultAreaList = new ArrayList<>();
        for (AreaEntity area : allAreaList) {
            if (area.getName().contains(keyword) && area.getLevel() == AreaEntity.AREA_LEVEL_AREA) {
                resultAreaList.add(area);
            }
        }
        return resultAreaList;
    }

    /**
     * 获取所有地区列表
     *
     * @return 所有地区列表
     */
    public static List<AreaEntity> getAll() {
        InputStream inputStream = null;
        try {
            inputStream = AppContext.getInstance().getAssets().open(AREA_FILE_NAME);
            return parseXml(inputStream);
        } catch (IOException | XmlPullParserException e) {
            e.printStackTrace();
            Log.e(TAG, "getAll: ", e);
            return new ArrayList<>();
        } finally {
            IOUtils.closeQuietly(inputStream);
        }
    }
}

四、实现地区选择三级联动

在以上步骤的基础上,我们完成了一个可以支持三级联动的省市区选择控件。

在应用中,只需按如下方式使用即可:

ProvinceCityAreaPicker picker = new ProvinceCityAreaPicker(this);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.CENTER;
picker.setLayoutParams(layoutParams);

在实际使用的时候,需要添加在布局文件中添加如下代码:

<com.linhongzheng.weixin.shoppingmall.ui.widget.ProvinceCityAreaPicker
    android:id="@+id/province_city_area_picker"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

五、示例说明

  1. 使用自定义WheelView控件实现颜色选择器

首先在xml文件中添加如下代码:

<com.linhongzheng.weixin.shoppingmall.ui.widget.WheelView
    android:id="@+id/wheelview"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

然后在java代码中初始化控件,并添加适配器:

private void initWheelView() {
    WheelView wheelview = (WheelView) findViewById(R.id.wheelview);
    wheelview.setWheelAdapter(new ColorWheelAdapter(this));//设置适配器
    wheelview.setSkin(WheelView.Skin.Holo);//设置滚轮样式
    wheelview.setWheelData(getData());//设置数据源
}

其中,ColorWheelAdapter是一种自定义适配器,getData()方法返回的是一个颜色的集合,需要根据实际需求编写相应的逻辑。

  1. 使用自定义WheelView控件实现周视图选择器

首先在xml文件中添加如下代码:

<com.linhongzheng.weixin.shoppingmall.ui.widget.WheelView
    android:id="@+id/wheelview"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

然后在java代码中初始化控件,并添加适配器:

private void initWheelView() {
    WheelView wheelview = (WheelView) findViewById(R.id.wheelview);
    wheelview.setWheelAdapter(new WeekWheelAdapter(this));//设置适配器
    wheelview.setSkin(WheelView.Skin.Holo);//设置滚轮样式
    wheelview.setWheelData(getData());//设置数据源
}

其中,WeekWheelAdapter是一种自定义适配器,getData()方法返回的是一周七天内的日期,需要根据实际需求编写相应的逻辑。

至此,我们已经实现了两种基于自定义WheelView控件的水平无限滑动选择器示例。

六、总结

本文主要介绍了如何使用自定义的WheelView控件实现地区选择三级联动的功能,具体过程分为以下几个步骤:

  1. 自定义WheelView控件:添加ViewPager和TabLayout,实现三级联动选择的效果;

  2. 数据管理:添加一个用于获取相应地区的列表信息的DataManager类,支持模糊查询;

  3. 实现地区选择三级联动;

  4. 展示两个基于自定义WheelView控件的水平无限滑动选择器示例。

这里我们需要特别强调的一点是,这里使用的方法老旧、过时,仅供学习、参考使用,不能用于实际应用中。实际使用时,建议使用第

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Android自定义WheelView地区选择三级联动 - Python技术站

(0)
上一篇 2023年6月25日
下一篇 2023年6月25日

相关文章

  • 大写的一到十怎么写 大写数字一到十的打法介绍

    大写的一到十怎么写 大写数字一到十的写法如下: 一(壹) 二(贰) 三(叁) 四(肆) 五(伍) 六(陆) 七(柒) 八(捌) 九(玖) 十(拾) 以下是两个示例说明: 示例一: 大写数字一到十的写法如下: 1. 一(壹) 2. 二(贰) 3. 三(叁) 4. 四(肆) 5. 五(伍) 6. 六(陆) 7. 七(柒) 8. 八(捌) 9. 九(玖) 10. …

    other 2023年8月17日
    00
  • asp.net中使用自定义控件的方式实现一个分页控件的代码

    ASP.NET是一种基于网络的应用程序开发框架,其中包含了许多自定义控件的实现,使用这些自定义控件可以方便地完成一些常用的功能,比如分页控件。下面是实现ASP.NET中使用自定义控件实现分页控件的攻略: 创建自定义控件 在你的项目中创建一个User Control(即.ascx文件)用于分页的视图呈现,可以添加一些页面元素比如“上一页”、“下一页”等。 添加…

    other 2023年6月27日
    00
  • C++ 初始化列表详解及实例代码

    C++ 初始化列表详解及实例代码 在 C++ 中,当我们定义一个类或结构体时,我们可以使用初始化列表来初始化类或结构体的成员变量。初始化列表提供了一种高效的方式来初始化类或结构体成员变量,特别是在初始化对性能要求很高的类时。 什么是初始化列表 初始化列表是一种用于初始化类或结构体成员变量的语法结构。通过初始化列表,我们可以在构造函数中以一种简洁和高效的方式初…

    other 2023年6月20日
    00
  • Android 项目正式签名打包教程分享

    Android 项目正式签名打包教程分享 在Android开发中,项目的正式签名打包是非常重要的一步,它确保了应用的身份验证和安全性。本攻略将详细讲解Android项目的正式签名打包过程,并提供两个示例说明。 1. 生成签名密钥 首先,我们需要生成一个签名密钥,用于对应用进行签名。可以使用keytool命令行工具来生成签名密钥。示例命令如下: keytool…

    other 2023年10月13日
    00
  • c++中的正则表达式操作(regex)

    C++中的正则表达式操作(regex)完整攻略 正则表达式是一种用于匹配文本的模式。在C++中,我们可以使用regex库来进行正则表达式操作。以下是C++中正则表达式操作完整攻略,包括正则表达式的语法、常用函数和两个示例说明。 正则表达式语法 C++的正则表达式语法与其他语言中的正则表达式语法类似。以下是一些常用的正则表达式元字符: .:匹配任意单个字符。 …

    other 2023年5月7日
    00
  • (下载地址)百分浏览器2.2.9.39版本更新发布

    百分浏览器2.2.9.39版本更新发布攻略 简介 百分浏览器是一款功能强大的网络浏览器,它提供了快速、安全和便捷的上网体验。最新版本2.2.9.39带来了一些新功能和改进,本攻略将详细介绍这些更新。 下载地址 你可以从以下地址下载百分浏览器2.2.9.39版本:下载地址 更新内容 1. 新增功能 1.1 夜间模式 百分浏览器2.2.9.39版本引入了夜间模式…

    other 2023年8月4日
    00
  • Linux文件查找命令总结(下篇)

    来详细讲解一下“Linux文件查找命令总结(下篇)”的完整攻略。 标题 Linux文件查找命令总结(下篇) 内容概述 本文主要介绍了Linux系统下常用的文件查找命令,包括find、which、whereis、locate等命令。这些命令可以在命令行中快速查找指定文件、目录和程序的位置。对于需要查找文件的任务,这些工具可以极大地提高我们的工作效率。本文将详细…

    other 2023年6月26日
    00
  • Vue+element-ui添加自定义右键菜单的方法示例

    下面我将详细讲解如何在Vue和element-ui的项目中,添加自定义右键菜单的方法。 前提条件 在开始之前,确保你已经完成了如下操作: 已搭建好Vue和element-ui项目 已经安装好vue-contextmenu插件 如果你还没有完成上述工作,请先完成这些步骤。 添加插件 首先,我们需要安装并引入vue-contextmenu插件。你可以通过npm进…

    other 2023年6月27日
    00
合作推广
合作推广
分享本页
返回顶部