WheelView地区选择三级联动详解

article/2025/9/12 22:30:16

1. 效果

最近需要做一个地区选择的功能,但是在网上和github上找了很久都没找到满意的,然后朋友推荐了一个给我,我花了点时间把代码大致看懂并改成我想要的,并写上我的理解。效果如图:
这里写图片描述

2. 注意

a. 首先我们要明白,网上这写三级联动的demo,不管是把数据库文件放在raw还是assets中,我们都要进行复制,将这个文件复制到app目录下,即

/data/data/"+context.getPackageName()+"/databases/

至于到底放在raw还是assets中哪里好些,我也在网上查了下,见这篇博客这里点击 ,但是按照这里面说的好像.db文件最好放在assets中,但是这个demo中拿过来,原来的.db文件就是放在raw中,所以我也没改了。
b. 最重要的一点,因为我们一般都是将选择完以后的数据都要上传到服务器中,但是因为每个后台要求的不一样,所以对于这个地区的.db文件也要求不一样,所以我们最主要的是学会读取数据库文件然后通过代码形成联动,举个例子,比如美团有自己的一个数据库,我们定位湖南长沙芙蓉区,然后进行地区选择,将岳麓区上传服务器并且请求数据下来,但是我们一般不会直接上传文字,一个是怕重名,还有一个就是文字不会绝对的准确,比如两个字的地名黄山 和 黄 山,看起来都对,但是你们的后台服务器接受的参数是黄山,而且使用别人的数据库中使黄 山,那就出事了,所以我们一般都是传编码,但是每个数据库中城市编码一般都是不一样,所以我们需要找后台拿匹配的数据库文件或者js文件。如图所示,两份数据库,一份是朋友推荐自带的,例外一份是我们的。
这里写图片描述
这里写图片描述
我的数据库中省市区只有一个表,而那份却有三个表,省 市 区三个表。并且编码也不一样,可以明显看出来。

3. 干货

a. 布局就直接贴代码了,很简单,mainactivity中就一个按钮,而popupwindow中是三个wheelview,

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:background="#00000000"android:gravity="bottom"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayout
        android:id="@+id/ly_myinfo_changeaddress_child"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:background="#ffffff"android:orientation="vertical" ><RelativeLayout
            android:layout_width="match_parent"android:layout_height="44dp"><View
                android:background="@color/silver"android:layout_width="match_parent"android:layout_height="0.5dp" /><TextView
                android:id="@+id/btn_myinfo_cancel"android:layout_width="wrap_content"android:layout_height="match_parent"android:paddingLeft="18dp"android:text="取消"android:gravity="center"android:layout_alignParentLeft="true"android:layout_marginRight="15dip"android:textColor="#e84515"android:textSize="14sp" /><TextView
                android:id="@+id/btn_myinfo_sure"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_alignParentRight="true"android:gravity="center"android:text="完成"android:textColor="#e84515"android:paddingRight="18dp"android:textSize="14sp" /></RelativeLayout><View android:layout_width="match_parent"android:layout_height="1dp"android:background="#d8d8d8"/><LinearLayout
            android:layout_width="match_parent"android:layout_height="190dip"android:orientation="horizontal"android:gravity="center_vertical"><guozhaohui.com.wlylocationchoose.locationchoose.WheelView
              android:id="@+id/provinceView"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_weight="1"/><guozhaohui.com.wlylocationchoose.locationchoose.WheelView
                android:id="@+id/cityView"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_weight="1"/><guozhaohui.com.wlylocationchoose.locationchoose.WheelView
                android:id="@+id/districtView"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_weight="1"/></LinearLayout></LinearLayout></LinearLayout>

b. 因为上面说了,需要将文件copy到app目录下,所以直接最好这代码在application中写,

package guozhaohui.com.wlylocationchoose;import android.app.Application;import java.io.InputStream;import guozhaohui.com.wlylocationchoose.locationchoose.CityDataHelper;/*** Created by ${GuoZhaoHui} on 2017/2/13.* Abstract:*/public class MyApplication extends Application {private CityDataHelper dataHelper;@Overridepublic void onCreate() {super.onCreate();/*** 放在application中,让app一启动就把raw中文件copy到 "/data/data/"+context.getPackageName()+"/databases/"* 这是app读取数据的方法,不管是将数据库文件放在raw或者assets中都是一样*/dataHelper=CityDataHelper.getInstance(this);InputStream in = this.getResources().openRawResource(R.raw.city);dataHelper.copyFile(in,CityDataHelper.DATABASE_NAME,CityDataHelper.DATABASES_DIR);}
}

c. popupwindow不是本次的重点也直接贴代码,

 View popupView = LayoutInflater.from(this).inflate(R.layout.popup_locationchoose, null);mPopupWindow = new PopupWindow(popupView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);mPopupWindow.setTouchable(true);mPopupWindow.setFocusable(true);mPopupWindow.setOutsideTouchable(true);mPopupWindow.setAnimationStyle(R.style.popup_locationchoose_bottom);//  pickText = (TextView)popupView.findViewById(R.id.tv_pickText);provinceView = (WheelView)popupView.findViewById(R.id.provinceView);cityView = (WheelView)popupView.findViewById(R.id.cityView);districtView = (WheelView)popupView.findViewById(R.id.districtView);//确定或者取消btn_myinfo_sure = (TextView)popupView.findViewById(R.id.btn_myinfo_sure);btn_myinfo_cancel = (TextView) popupView.findViewById(R.id.btn_myinfo_cancel);btn_myinfo_cancel.setOnClickListener(this);btn_myinfo_sure.setOnClickListener(this);

设置三个wheelview的可见条目数

        provinceView.setVisibleItems(7);cityView.setVisibleItems(7);districtView.setVisibleItems(7);

为三个 wheelview添加滑动事件

    // 添加change事件provinceView.addChangingListener(this);// 添加change事件cityView.addChangingListener(this);// 添加change事件districtView.addChangingListener(this);

c. 拿到操作数据的对象SQLiteDatabase

 db = dataHelper.openDataBase();

观察数据库文件可知这表中是根据字段level来判断省市区的,如图
这里写图片描述
同时我们也可知这省市区三个模型中的字段都是一样的,都是

    private int cityID;private int parentId;private int level;private String name;private String pinyin;

不清楚的可以自己查下表,如图,我查一个省
这里写图片描述
所以我们建立一样的模型,虽然三个字段是一样的,建一个就可以了,但是为了标准最好还是建三个。

package guozhaohui.com.wlylocationchoose.locationchoose.model;public class ProvinceModel {private int cityID;private int parentId;private int level;private String name;private String pinyin;public int getCityID() {return cityID;}public void setCityID(int cityID) {this.cityID = cityID;}public int getParentId() {return parentId;}public void setParentId(int parentId) {this.parentId = parentId;}public int getLevel() {return level;}public void setLevel(int level) {this.level = level;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPinyin() {return pinyin;}public void setPinyin(String pinyin) {this.pinyin = pinyin;}}

进行sql查询,将查询到的结果保存在cursor中,然后进行一行行的循环遍历,然后将遍历一行的对象添加到这个对象的集合中。这里得到省的集合。

 public List<ProvinceModel> getProvice(SQLiteDatabase db){String sql="SELECT * FROM ChooseCityModel where level = 1 ORDER BY cityID";Cursor cursor = db.rawQuery(sql,null);List<ProvinceModel> list=new ArrayList<ProvinceModel>();if (cursor!=null&&cursor.getCount() > 0) {while (cursor.moveToNext()){ProvinceModel provinceModel=new ProvinceModel();provinceModel.setCityID(cursor.getInt(cursor.getColumnIndex("cityID")));provinceModel.setParentId(cursor.getInt(cursor.getColumnIndex("parentId")));provinceModel.setLevel(cursor.getInt(cursor.getColumnIndex("level")));provinceModel.setName(cursor.getString(cursor.getColumnIndex("name")));provinceModel.setPinyin(cursor.getString(cursor.getColumnIndex("pinyin")));list.add(provinceModel);}}return list;}

根据表的结构,得到相应的sql语句,希望根据上一级省的cityId得到下面的市,其实换句话说本级市的parentId就是上一级的cityid,不清楚的可以将sql语句查一遍,验证下对不对,如图
这里写图片描述
得到相应省下面市的集合

 public List<CityModel> getCityByParentId(SQLiteDatabase db, String code){String sql="SELECT * FROM ChooseCityModel WHERE  level = 2  and parentId = ? ORDER BY cityID";Cursor cursor = db.rawQuery(sql,new String[]{code});List<CityModel> list=new ArrayList<CityModel>();if (cursor!=null&&cursor.getCount() > 0) {while (cursor.moveToNext()){CityModel cityModel=new CityModel();cityModel.setCityID(cursor.getInt(cursor.getColumnIndex("cityID")));cityModel.setParentId(cursor.getInt(cursor.getColumnIndex("parentId")));cityModel.setLevel(cursor.getInt(cursor.getColumnIndex("level")));cityModel.setName(cursor.getString(cursor.getColumnIndex("name")));cityModel.setPinyin(cursor.getString(cursor.getColumnIndex("pinyin")));list.add(cityModel);}}return list;}

区也是一样的,直接贴代码了

   public List<DistrictModel> getDistrictById(SQLiteDatabase db, String code){//注意这里的parentId其实就是上一级的cityIDString sql="SELECT * FROM ChooseCityModel WHERE  level = 3  and parentId = ? ORDER BY cityID";Cursor cursor = db.rawQuery(sql,new String[]{code});List<DistrictModel> list=new ArrayList<DistrictModel>();if (cursor!=null&&cursor.getCount() > 0) {while (cursor.moveToNext()){DistrictModel districtModel=new DistrictModel();districtModel.setCityID(cursor.getInt(cursor.getColumnIndex("cityID")));districtModel.setParentId(cursor.getInt(cursor.getColumnIndex("parentId")));districtModel.setLevel(cursor.getInt(cursor.getColumnIndex("level")));districtModel.setName(cursor.getString(cursor.getColumnIndex("name")));districtModel.setPinyin(cursor.getString(cursor.getColumnIndex("pinyin")));list.add(districtModel);}}return list;}

d. 对弹出popuwindow显示的wheelview进行初始化,注释都写在代码里,

 private void initpopData() {//初始化数据dataHelper = CityDataHelper.getInstance(this);db = dataHelper.openDataBase();provinceDatas = dataHelper.getProvice(db);if (provinceDatas.size() > 0) {//弹出popup时,省wheelview中当前的省其实就是省集合的第一个mCurrentProvince = provinceDatas.get(0).getName();//根据省cityid查询到第一个省下面市的集合cityDatas = dataHelper.getCityByParentId(db, provinceDatas.get(0).getCityID()+"");}if (cityDatas.size() > 0) {//根据市cityid查询到第一个市集合下面区的集合districtDatas = dataHelper.getDistrictById(db, cityDatas.get(0).getCityID()+"");}//wheelview的适配器代码provinceAdapter = new ProvinceAdapter(this, provinceDatas);provinceAdapter.setTextSize(TEXTSIZE);//设置字体大小provinceView.setViewAdapter(provinceAdapter);updateCitys();updateAreas();}

更新省下面市的wheelview内容,注释很清楚,直接上代码

   private void updateCitys() {int pCurrent = provinceView.getCurrentItem();if (provinceDatas.size() > 0) {//这里是必须的的,上面得到的集合只是第一个省下面所有市的集合及第一个市下面所有区的集合//这里得到的是相应省下面对应市的集合cityDatas = dataHelper.getCityByParentId(db, provinceDatas.get(pCurrent).getCityID()+"");} else {cityDatas.clear();}citysAdapter = new CitysAdapter(this, cityDatas);citysAdapter.setTextSize(TEXTSIZE);cityView.setViewAdapter(citysAdapter);if (cityDatas.size() > 0) {//默认省下面 市wheelview滑动第一个,显示第一个市cityView.setCurrentItem(0);mCurrentCity = cityDatas.get(0).getName();} else {mCurrentCity = "";}updateAreas();}

第三个wheelview和第二个一样的,代码直接上

  private void updateAreas() {int cCurrent = cityView.getCurrentItem();if (cityDatas.size() > 0) {districtDatas = dataHelper.getDistrictById(db, cityDatas.get(cCurrent).getCityID()+"");} else {districtDatas.clear();}areaAdapter = new AreaAdapter(this, districtDatas);areaAdapter.setTextSize(TEXTSIZE);districtView.setViewAdapter(areaAdapter);if (districtDatas.size() > 0) {mCurrentDistrict = districtDatas.get(0).getName();districtView.setCurrentItem(0);} else {mCurrentDistrict = "";}}

4. 道友留步

1.因为我朋友也是在网上哪里找到的demo,所以这原版是谁的也不知道了。
2. 源码地址这里点击


http://chatgpt.dhexx.cn/article/53c1cLk7.shtml

相关文章

如何用美剧真正提升你的英语水平————转自厦大口译的博客

看到很多童鞋讨论有关美剧学习英语到底有没有用&#xff0c;以及用哪部美剧练习&#xff0c;我在这里想说这只是一个参考&#xff0c;世界上没有绝对的事情&#xff0c;究竟有没有用看个人 1. 不是所有的美剧都适合学英语 如果喜欢看如《24小时》这样的动作片, 那你基本会讲一口…

美剧命名规则

缘起 最近工作需要研究了下ffmpeg这个工具.在查资料的时候意外发现美剧制作组发布的美剧的名字是有规则的. 美剧命名规则 剧名.S季数E集数.集名(可以不标).发布年代(可以不标).分辨率(可以不标).信号采集源.音频编码(默认的MP3可以不标).视频编码-制作组 下面举例子说一下吧. 例…

EOS 智能合约

1. EOS智能合约的介绍 1.1. 所需背景知识 C / C 经验 基于EOS.IO的区块链使用Web Assembly(WASM)执行开发者提供的应用代码。WASM是一个已崭露头角的web标准&#xff0c;受到Google, Microsoft, Apple及其他大公司的广泛支持。目前为止&#xff0c;最成熟的用于构建应用及WA…

EOS智能合约开发(三)EOS创建和管理账户

创建好钱包和密钥后&#xff0c;我们就需要创建账户。为什么创建账户&#xff0c;为了方便人与区块链交互。以太坊40位地址&#xff0c;让我们非常难以记忆。EOS有账户概念&#xff0c;我们就可以定义账户权限。 在区块链上执行操作&#xff0c;需要使用到账号。我们使用cleos…

固化EOS智能合约,监管升级权限,净化EOS DAPP生态

最近EOS版的Fomo 3D狼人杀游戏骗局引发了大家对EOS智能合约的安全性的大讨论。 和以太坊智能合约的不可升级不同&#xff0c;EOS智能合约可升级&#xff0c;因而保存在智能合约中的数据称不上去中心化&#xff0c;因为智能合约的管理员可偷偷的升级智能合约来修改合约里的任何数…

[EOS源码分析]7.EOS智能合约开发实践之合约调用合约(inline action)

首先&#xff0c;目前dawn-4.1, dawn-4.2使用inline action是会报如下错误 transaction declares authority {"actor":"hello.code","permission":"active"}, but does not have signatures for it under a provided delay of 0 ms 这…

区块链实现智能合约

区块链实现智能合约 一、制定生成智能合约 1、首先参与智能合约的用户必须先注册成为区块链的用户&#xff0c;区块链返回给用户一对公钥和私钥。公钥做为用户在区块链上的账户地址&#xff0c;私钥做为操作该账户的唯一钥匙。 2、两个以两个以上的用户根据需要&#xff0c;…

以太坊中的智能合约

以太坊中的智能合约&#xff08;Smart Coantract&#xff09; 创建智能合约 以太坊中的智能合约是运行在区块链上的一段代码&#xff0c;代码的逻辑定义了合约的内容。合约的账户保存了合约当前的运行状态&#xff0c;主要包含了4部分内容。 balance&#xff1a;当前余额non…

智能合约(一)————智能合约入门

1、智能合约的基本组成 1.1.程序版本 1.2. 合约声明 1.3.状态变量 1.4.合约方法 在这里constant相当于他声明这个局部变量不能更改&#xff0c;但是他并没有实际作用&#xff08;实际就只是警示作用&#xff09;2、地址adress - address.balance 账户余额 - adress.transfe…

EOS 智能合约源代码解读 (10)token合约“简介”

1. 记录用户的token&#xff0c;比如有哪些代币 class [[eosio::contract("eosio.token")]] token : public contract {public:using contract::contract;[[eosio::action]]void _create( const text_name& issuer, const asset& maximum_supply);[[eosi…

区块链智能合约介绍

作者&#xff1a;qinyutong、chengyueqiang 智能合约 (smart contract) 是一种由事件驱动的、具有状态的代码合约和算法合同 [11]&#xff0c;随着以比特币为代表的区块链技术的蓬勃发展, 区块链技术已经开始逐步超越可编程货币时代而进入智能合约时代。智能合约作为区块链的核…

智能合约简介

区块链技术简史 区块链技术的第一次应用是在2008年&#xff0c;当时比特币首次亮相。抛弃传统金融机构&#xff0c;比特币引入了促进数字金融交易的新方法。然而&#xff0c;尽管在当时这是革命性的举措&#xff0c;但在这种状态下&#xff0c;区块链技术无法得到广泛应用。 区…

十分钟教你开发EOS智能合约

在CSDN、柏链道捷&#xff08;PDJ Education&#xff09;、HelloEOS、中关村区块链产业联盟主办的「EOS入门及最新技术解读」专场沙龙上&#xff0c;柏链道捷&#xff08;PDJ Education&#xff09;CTO、副总裁康烁&#xff0c;作了「如何在EOS上开发智能合约」的精彩演讲。演讲…

智能合约--如何实现可升级的智能合约

一. 什么是智能合约 智能合约通俗点说就是写在区块链上面的代码&#xff0c;代码里面编写着严谨完善的规则&#xff0c;一旦某个用户满足了合约里面的规则条件&#xff0c;就会触发里面的代码&#xff0c;执行某个方法。 二. 为什么要使智能合约达到可升级 智能合约的特点之一…

智能合约简单介绍

本学期学习了区块链的课程&#xff0c;作业是对于智能合约学习后的报告&#xff1a; 1 智能合约简单了解 1.1智能合约是什么 智能合约是由事件驱动的、具有状态的、部署于可共享的分布式数据库上的计算机程序&#xff0c;多用IF-THEN语句。狭义来说&#xff0c;智能合约是设计…

智能合约

智能合约&#xff08;英语&#xff1a;Smart contract &#xff09;是一种旨在以信息化方式传播、验证或执行合同的计算机协议。智能合约允许在没有第三方的情况下进行可信交易。这些交易可追踪且不可逆转。[1]智能合约概念于1994年由Nick Szabo首次提出。智能合同的目的是提供…

[EOS源码分析]6.EOS特殊智能合约eosio

这里说的eosio智能合约不是泛指eos的智能合约&#xff0c;它是一个特殊的具体的合约。它本事可大了&#xff0c;我们一起来看看它有哪些功能 负责智能合约部署 大家有注意到如下红色字体的log吗 $ cleos set contract hello.code ../eos-contract/hello -p hello.code Publish…

EOS智能合约开发(一)

一、 智能合约功能二、 通讯模式 2.1 运行机制2.2 内联通信2.3 延迟通信2.4 交易和动作2.5 交易确认2.6 动作处理程序和动作的apply上下文2.7 交易限制 三、文件结构 3.1 创建框架3.2 apply处理程序3.3 EOSIO_ABI 宏 四、多索引数据库API 4.1 EOSIO多索引API4.2 EOSIO多索引迭代…

EOS系列 - WASM智能合约 - 特性

构造函数 addressbook(name receiver, name code, datastream<const char*> ds):contract(receiver, code, ds) {}#单例表&#xff08;code和scope都用receiver的表&#xff09;也可在初始化列表中实例化 singleton_example( name receiver, name code, datastream<c…

EOS智能合约开发(十五)EOS 状态机架构解析

好久没有写文章了&#xff0c;最近公司事情比较忙。非常感谢上次杭州团队一位负责EOS状态机的同事精彩分享。今天查阅很多资料后&#xff0c;整理这篇文章&#xff0c;希望对大家有所帮助。 EOS状态机是什么&#xff1f; 简单讲就是数据库&#xff0c;是EOS记录智能合约执行结…