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

yizhihongxing

一、背景介绍

在开发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日

相关文章

  • C++ 将数据转为字符串的几种方法

    下面是关于 C++ 将数据转为字符串的完整攻略。 1. stringstream 类型转换 可以使用 stringstream 类型转换,它是 C++ 标准库中的一个类,可以把数字转化成一个字符串类型,并且能够识别科学计数法。示例如下: #include <iostream> #include <sstream> int main()…

    other 2023年6月20日
    00
  • Golang创建构造函数的方法超详细讲解

    Golang创建构造函数的方法 在Golang中,构造函数用于初始化一个struct类型并返回该类型的指针,这里我们介绍两种不同的Golang创建构造函数的方法。 方法一:函数调用 定义一个结构体Person: type Person struct { Name string Age int } 然后定义一个函数 NewPerson() 作为结构体的构造函数…

    other 2023年6月26日
    00
  • web3.js调用链上的方法操作NFT区块链MetaMask详解

    下面是“web3.js调用链上的方法操作NFT区块链MetaMask详解”的完整攻略。 1. 准备工作 在使用web3.js调用链上的方法操作NFT区块链前,需要完成以下准备工作: 安装MetaMask插件,创建钱包,并将其连接到目标链上。 安装web3.js库。 2. 链上方法 调用链上的方法可以通过web3.js库中的合约对象实现,具体步骤如下: 创建合…

    other 2023年6月27日
    00
  • win11更新重启黑屏等多久?

    Win11更新重启黑屏等多久? 如果你安装了 Windows 11 并遇到了更新后重启后出现黑屏等问题,不必担心。这是正常情况,并且可能需要花费一些时间才能恢复正常。在这里,我们提供一些攻略来解决这个问题。 1. 等待一段时间 当你看到黑屏后第一步应该是耐心等待,因为这很可能只是 Windows 系统更新中的一部分。Windows 11 可能需要花费一些时间…

    other 2023年6月27日
    00
  • 举例讲解Java的RTTI运行时类型识别机制

    以下是使用标准的Markdown格式文本,详细讲解Java的RTTI(运行时类型识别)机制的完整攻略: Java的RTTI运行时类型识别机制 RTTI(Run-Time Type Identification)是Java中一种在运行时确定对象类型的机制。Java的RTTI机制主要通过以下两个关键字实现: instanceof:用于判断一个对象是否属于某个特定…

    other 2023年10月15日
    00
  • PHP命名空间namespace定义及导入use用法详解

    PHP命名空间namespace定义及导入use用法详解 1. 什么是命名空间? 命名空间是一种将代码组织为独立且可重用的结构的技术。通过命名空间,我们可以避免命名冲突并更好地组织和管理代码。在PHP中,我们可以使用命名空间将相关的类、函数和常量组织在一起。 2. 如何定义命名空间? 使用namespace关键字可以定义一个命名空间。命名空间通常在文件的顶部…

    other 2023年6月28日
    00
  • Android中的Activity生命周期总结

    下面我将为您详细讲解“Android中的Activity生命周期总结”的完整攻略。 1. 什么是Activity生命周期? Activity生命周期是指从Activity创建、启动、运行、暂停、停止到销毁的整个过程。当系统创建或销毁Activity、暂停或恢复Activity运行、Activity不可见或重新进入前台,都会触发相关方法。 2. Activit…

    other 2023年6月27日
    00
  • Vue 3.0双向绑定原理的实现方法

    Vue 3.0中的双向数据绑定是通过数据响应式系统实现的,下面我们将详细讲解Vue 3.0双向绑定原理的实现方法。 数据响应式系统的基本原理 Vue 3.0中的响应式系统依赖于ES6的Proxy对象,通过对数据进行代理,实现数据的监听和数据更新时的通知。 当我们在模板中使用数据时,Vue 3.0会对这些数据进行代理,并且将这些数据与一个虚拟节点VNode进行…

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