使用Android-PickerView实现地址选择器时间选择器
2016-12-21 10:35
746 查看
这里贡献下我使用Android-PickerView实现地址选择器遇到的坑,算是一个笔记。首先要吐槽下后台接口,为了实现移动端和web端的统一(ps:可能他没搞过后台接口),修改地址的时候本来要用下拉框。。。我去,后面ios的大兄弟苦苦哀求,改成了他们ios的PickerView,就是地址选择联动的,我一想也可以,不是有个大兄弟老早就封装了精仿这个PickerView吗,美美的~可是有点曲折
给看下最终的效果图:
这个是那个大兄弟的github:https://github.com/saiwu-bigkoo/Android-PickerView
大写的挽尊,我去
这个大兄弟已经离开编程界了,虽然这也是我的终极目标,嘿嘿
好吧,我们只能做一些摸索了,还好留下了demo,这个控件堪称完美,兄弟们可以一起去用用,大家交流体验。做那种滚动选项选择的不在话下,用了这个,就可以跟ios的大兄弟同步了,当然我不再介绍这里的时间选择器,数据固定已经封装。弱水三千,我取一瓢饮。我这里只拿选项选择器来进行地址滚动选择器(省、市、区三级联动)的用法。主要是数据不统一性,我不改动pickerView已封装好的控件,只在使用上下个道道。
欲哭无泪的是,我这边的后台数据接口需要我这边修改地址选择提交的是id,就像ERP系统的下拉框一样,展示的时候显示键(name),实际提交的时候是提交值(id),有点点恶心,但是恶心也有个恶心的做法。然后我这边的数据库只做查询操作,所以直接生成后,放在assert文件下读取使用了,当然你还会发现更好的做法,大兄弟,你可以带带我~~
一、首先是添加依赖
二、然后你就可以欢快地去使用了,网上有可以直接读取assert文件夹下数据库的方法,稍微封装了下。还有几个bean实体类,用于存储城市编码,省份id,等字段(最重要的是要存储id做后续修改提交的参数)。
1、读取assert下的sqlite数据库
2、这里的数据提取需要注意要严格区分层级关系。其实就是你要模拟占位你的父级元素的个数,因为后面是通过ArrayList的position提取的。这个作者没讲清楚,我也没时间做进一步封装,因为主体源码都是作者的,不好意思提炼出来做二次封装。我debug模式下,贴几张图,给大伙感受下:
如果你报了这个错误,就好好用心感受下,这7张图。。。血的教训
ArrayList数组越界
1、3个数据最外层的size必须一致
2、省份就一层数据
3、城市需要先外部嵌套一层省份
4、第二层list中的才是每个省份所对应的城市
5、区域最外层(跟省份size一致)
6、区域第二层(跟对应省份的对应城市的size一致)
7、区域第三层(跟对应省份的对应城市的对应区域的size)
8、主要原因是因为,提取显示的时候是严格按照list下的position来获取的,所以一切都清楚了
3、初始化OptionsPickerView,并使用
4、xml文件就不贴了,就是一个Hellow World的标签
看下这个demo的运行效果图:
5、github地址:https://github.com/TrebleZ/Pickerviewdemo
6、下载地址:http://download.csdn.net/detail/z_zt_t/9717228
给看下最终的效果图:
这个是那个大兄弟的github:https://github.com/saiwu-bigkoo/Android-PickerView
大写的挽尊,我去
这个大兄弟已经离开编程界了,虽然这也是我的终极目标,嘿嘿
好吧,我们只能做一些摸索了,还好留下了demo,这个控件堪称完美,兄弟们可以一起去用用,大家交流体验。做那种滚动选项选择的不在话下,用了这个,就可以跟ios的大兄弟同步了,当然我不再介绍这里的时间选择器,数据固定已经封装。弱水三千,我取一瓢饮。我这里只拿选项选择器来进行地址滚动选择器(省、市、区三级联动)的用法。主要是数据不统一性,我不改动pickerView已封装好的控件,只在使用上下个道道。
欲哭无泪的是,我这边的后台数据接口需要我这边修改地址选择提交的是id,就像ERP系统的下拉框一样,展示的时候显示键(name),实际提交的时候是提交值(id),有点点恶心,但是恶心也有个恶心的做法。然后我这边的数据库只做查询操作,所以直接生成后,放在assert文件下读取使用了,当然你还会发现更好的做法,大兄弟,你可以带带我~~
一、首先是添加依赖
compile 'com.bigkoo:pickerview:2.1.1'
二、然后你就可以欢快地去使用了,网上有可以直接读取assert文件夹下数据库的方法,稍微封装了下。还有几个bean实体类,用于存储城市编码,省份id,等字段(最重要的是要存储id做后续修改提交的参数)。
1、读取assert下的sqlite数据库
package db; import android.content.Context; import android.content.SharedPreferences; import android.content.res.AssetManager; import android.database.sqlite.SQLiteDatabase; import android.util.Log; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; /** * This is a Assets Database Manager * Use it, you can use a assets database file in you application * It will copy the database file to "/data/data/[your application package name]/database" when you first time you use it * Then you can get a SQLiteDatabase object by the assets database file * @author RobinTang * @time 2012-09-20 * * * How to use: * 1. Initialize AssetsDatabaseManager * 2. Get AssetsDatabaseManager * 3. Get a SQLiteDatabase object through database file * 4. Use this database object * * Using example: * AssetsDatabaseManager.initManager(getApplication()); // this method is only need call one time * AssetsDatabaseManager mg = AssetsDatabaseManager.getManager(); // get a AssetsDatabaseManager object * SQLiteDatabase db1 = mg.getDatabase("db1.db"); // get SQLiteDatabase object, db1.db is a file in assets folder * db1.??? // every operate by you want * Of cause, you can use AssetsDatabaseManager.getManager().getDatabase("xx") to get a database when you need use a database */ public class AssetsDatabaseManager { private static String tag = "AssetsDatabase"; // for LogCat private static String databasepath = "/data/data/%s/databases"; // %s is packageName // A mapping from assets database file to SQLiteDatabase object private Map<String, SQLiteDatabase> databases = new HashMap<String, SQLiteDatabase>(); // Context of application private Context context = null; // Singleton Pattern private static AssetsDatabaseManager mInstance = null; /** * Initialize AssetsDatabaseManager * @param context, context of application */ public static void initManager(Context context){ if(mInstance == null){ mInstance = new AssetsDatabaseManager(context); } } /** * Get a AssetsDatabaseManager object * @return, if success return a AssetsDatabaseManager object, else return null */ public static AssetsDatabaseManager getManager(){ return mInstance; } private AssetsDatabaseManager(Context context){ this.context = context; } /** * Get a assets database, if this database is opened this method is only return a copy of the opened database * @param dbfile, the assets file which will be opened for a database * @return, if success it return a SQLiteDatabase object else return null */ public SQLiteDatabase getDatabase(String dbfile) { if(databases.get(dbfile) != null){ Log.i(tag, String.format("Return a database copy of %s", dbfile)); return (SQLiteDatabase) databases.get(dbfile); } if(context==null) return null; Log.i(tag, String.format("Create database %s", dbfile)); String spath = getDatabaseFilepath(); String sfile = getDatabaseFile(dbfile); File file = new File(sfile); SharedPreferences dbs = context.getSharedPreferences(AssetsDatabaseManager.class.toString(), 0); boolean flag = dbs.getBoolean(dbfile, false); // Get Database file flag, if true means this database file was copied and valid if(!flag || !file.exists()){ file = new File(spath); if(!file.exists() && !file.mkdirs()){ Log.i(tag, "Create \""+spath+"\" fail!"); return null; } if(!copyAssetsToFilesystem(dbfile, sfile)){ Log.i(tag, String.format("Copy %s to %s fail!", dbfile, sfile)); return null; } dbs.edit().putBoolean(dbfile, true).commit(); } SQLiteDatabase db = SQLiteDatabase.openDatabase(sfile, null, SQLiteDatabase.NO_LOCALIZED_COLLATORS); if(db != null){ databases.put(dbfile, db); } return db; } private String getDatabaseFilepath(){ return String.format(databasepath, context.getApplicationInfo().packageName); } private String getDatabaseFile(String dbfile){ return getDatabaseFilepath()+"/"+dbfile; } private boolean copyAssetsToFilesystem(String assetsSrc, String des){ Log.i(tag, "Copy "+assetsSrc+" to "+des); InputStream istream = null; OutputStream ostream = null; try{ AssetManager am = context.getAssets(); istream = am.open(assetsSrc); ostream = new FileOutputStream(des); byte[] buffer = new byte[1024]; int length; while ((length = istream.read(buffer))>0){ ostream.write(buffer, 0, length); } istream.close(); ostream.close(); } catch(Exception e){ e.printStackTrace(); try{ if(istream!=null) istream.close(); if(ostream!=null) ostream.close(); } catch(Exception ee){ ee.printStackTrace(); } return false; } return true; } /** * Close assets database * @param dbfile, the assets file which will be closed soon * @return, the status of this operating */ public boolean closeDatabase(String dbfile){ if(databases.get(dbfile) != null){ SQLiteDatabase db = (SQLiteDatabase) databases.get(dbfile); db.close(); databases.remove(dbfile); return true; } return false; } /** * Close all assets database */ static public void closeAllDatabase(){ Log.i(tag, "closeAllDatabase"); if(mInstance != null){ for(int i=0; i<mInstance.databases.size(); ++i){ if(mInstance.databases.get(i)!=null){ mInstance.databases.get(i).close(); } } mInstance.databases.clear(); } } }
package db; import android.app.Application; import android.database.sqlite.SQLiteDatabase; public class DBManager { public static SQLiteDatabase getdb(Application mApplication) { // 初始化,只需要调用一次 AssetsDatabaseManager.initManager(mApplication); // 获取管理对象,因为数据库需要通过管理对象才能够获取 AssetsDatabaseManager mg = AssetsDatabaseManager.getManager(); // 通过管理对象获取数据库 SQLiteDatabase db = mg.getDatabase("china_citys_name.sqlite"); return db; } }
2、这里的数据提取需要注意要严格区分层级关系。其实就是你要模拟占位你的父级元素的个数,因为后面是通过ArrayList的position提取的。这个作者没讲清楚,我也没时间做进一步封装,因为主体源码都是作者的,不好意思提炼出来做二次封装。我debug模式下,贴几张图,给大伙感受下:
如果你报了这个错误,就好好用心感受下,这7张图。。。血的教训
ArrayList数组越界
java.lang.IndexOutOfBoundsException: Invalid index 1, size is 1
1、3个数据最外层的size必须一致
2、省份就一层数据
3、城市需要先外部嵌套一层省份
4、第二层list中的才是每个省份所对应的城市
5、区域最外层(跟省份size一致)
6、区域第二层(跟对应省份的对应城市的size一致)
7、区域第三层(跟对应省份的对应城市的对应区域的size)
8、主要原因是因为,提取显示的时候是严格按照list下的position来获取的,所以一切都清楚了
//返回的分别是三个级别的选中位置 String tx = options1Items.get(options1).getPro_name() + options2Items.get(options1).get(option2).getName() + options3Items.get(options1).get(option2).get(options3).getName();
3、初始化OptionsPickerView,并使用
MainActivity.java package com.pickerview.pickerviewdemo; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.TextView; import com.bigkoo.pickerview.OptionsPickerView; import java.util.ArrayList; import db.AreaBean; import db.CityBean; import db.DBManager; import db.ProvinceBean; public class MainActivity extends AppCompatActivity { private TextView tvTitle; private OptionsPickerView pvOptions;//地址选择器 private ArrayList<ProvinceBean> options1Items = new ArrayList<>();//省 private ArrayList<ArrayList<CityBean>> options2Items = new ArrayList<>();//市 private ArrayList<ArrayList<ArrayList<AreaBean>>> options3Items = new ArrayList<>();//区 private ArrayList<String> Provincestr = new ArrayList<>();//省 private ArrayList<ArrayList<String>> Citystr = new ArrayList<>();//市 private ArrayList<ArrayList<ArrayList<String>>> Areastr = new ArrayList<>();//区 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initData(); initEvent(); } private void initView() { tvTitle = (TextView) findViewById(R.id.tvTitle); } private void initData() { //选项选择器 pvOptions = new OptionsPickerView(this); // 获取数据库 SQLiteDatabase db = DBManager.getdb(getApplication()); //省 Cursor cursor = db.query("province", null, null, null, null, null, null); while (cursor.moveToNext()) { int pro_id = cursor.getInt(0); String pro_code = cursor.getString(1); String pro_name = cursor.getString(2); String pro_name2 = cursor.getString(3); ProvinceBean provinceBean = new ProvinceBean(pro_id, pro_code, pro_name, pro_name2); options1Items.add(provinceBean);//添加一级目录 Provincestr.add(cursor.getString(2)); //查询二级目录,市区 Cursor cursor1 = db.query("city", null, "province_id=?", new String[]{pro_id + ""}, null, null, null); ArrayList<CityBean> cityBeanList = new ArrayList<>(); ArrayList<String> cityStr = new ArrayList<>(); //地区集合的集合(注意这里要的是当前省份下面,当前所有城市的地区集合我去) ArrayList<ArrayList<AreaBean>> options3Items_03 = new ArrayList<>(); ArrayList<ArrayList<String>> options3Items_str = new ArrayList<>(); while (cursor1.moveToNext()) { int cityid = cursor1.getInt(0); int province_id = cursor1.getInt(1); String code = cursor1.getString(2); String name = cursor1.getString(3); String provincecode = cursor1.getString(4); CityBean cityBean = new CityBean(cityid, province_id, code, name, provincecode); //添加二级目录 cityBeanList.add(cityBean); cityStr.add(cursor1.getString(3)); //查询三级目录 Cursor cursor2 = db.query("area", null, "city_id=?", new String[]{cityid + ""}, null, null, null); ArrayList<AreaBean> areaBeanList = new ArrayList<>(); ArrayList<String> areaBeanstr = new ArrayList<>(); while (cursor2.moveToNext()) { int areaid = cursor2.getInt(0); int city_id = cursor2.getInt(1); // String code0=cursor2.getString(2); String areaname = cursor2.getString(3); String citycode = cursor2.getString(4); AreaBean areaBean = new AreaBean(areaid, city_id, areaname, citycode); areaBeanList.add(areaBean); areaBeanstr.add(cursor2.getString(3)); } options3Items_str.add(areaBeanstr);//本次查询的存储内容 options3Items_03.add(areaBeanList); } options2Items.add(cityBeanList);//增加二级目录数据 Citystr.add(cityStr); options3Items.add(options3Items_03);//添加三级目录 Areastr.add(options3Items_str); } //设置三级联动效果 pvOptions.setPicker(Provincestr, Citystr, Areastr, true); //设置选择的三级单位 // pvOptions.setLabels("省", "市", "区"); pvOptions.setTitle("选择城市"); //设置是否循环滚动 pvOptions.setCyclic(false, false, false); //设置默认选中的三级项目 //监听确定选择按钮 pvOptions.setSelectOptions(0, 0, 0); pvOptions.setOnoptionsSelectListener(new OptionsPickerView.OnOptionsSelectListener() { @Override public void onOptionsSelect(int options1, int option2, int options3) { //返回的分别是三个级别的选中位置 String tx = options1Items.get(options1).getPro_name() + options2Items.get(options1).get(option2).getName() + options3Items.get(options1).get(option2).get(options3).getName(); tvTitle.setText(tx); } }); } private void initEvent() { tvTitle.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { pvOptions.show(); } }); } }
4、xml文件就不贴了,就是一个Hellow World的标签
看下这个demo的运行效果图:
5、github地址:https://github.com/TrebleZ/Pickerviewdemo
6、下载地址:http://download.csdn.net/detail/z_zt_t/9717228
总结:等有空还是要完善下的。希望我的这些坑对你有一点点的帮助,have a nice day~
相关文章推荐
- Android 时间选择器 PickerView,的详细使用
- 安卓学习笔记---Android-PickerView实现 3D滚轮效果(时间选择器、省市区三级联动,单项选择效果)
- Android中使用开源框架Citypickerview实现省市区三级联动选择
- Android中使用开源框架citypickerview实现省市区三级联动选择
- Android中使用开源框架Citypickerview实现省市区三级联动选择
- Android中使用开源框架citypickerview实现省市区三级联动选择
- android—DatePicker 和TimePicker显示日期以及使用TimePickerDialog,DatePickerDialog来专门实现时间选择对话框
- android自定义spinner,使用AppCompatTextView+PopupWindow 实现下拉选择的功能
- Android基于开源项目的WheelView的时间、地址联动选择对话框
- Recyclerview的一些个人理解与使用(五)Recyclerview的联动,时间选择的实现
- android开发之&使用ViewPager加gridView实现菜单按钮分页滑动(类似QQ表情选择翻页效果)
- 微信小程序例子——使用picker实现时间和日期选择框
- Android使用RecyclerView实现列表数据选择操作
- Android DatePicker日期选择器、TimePicker时间选择器的使用
- 7.Android开源项目WheelView的时间和地址联动选择对话框
- 怎样使用pickerview来实现地址菜单的三级联动效果
- 微信小程序使用picker实现时间和日期选择框功能【附源码下载】
- Android使用wheelView实现简单类似ios PickerView选择器效果
- Android基于开源项目的WheelView的时间、地址联动选择对话框【转】
- Android View 滚轮控件LoopView+自定义Dialog [时间地域选择器] Picker