使用Android-PickerView实现地址选择器时间选择器

这里贡献下我使用Android-PickerView实现地址选择器遇到的坑,算是一个笔记。首先要吐槽下后台接口,为了实现移动端和web端的统一(ps:可能他没搞过后台接口),修改地址的时候本来要用下拉框。。。我去,后面ios的大兄弟苦苦哀求,改成了他们ios的PickerView,就是地址选择联动的,我一想也可以,不是有个大兄弟老早就封装了精仿这个PickerView吗,美美的~可是有点曲折

给看下最终的效果图:
这里写图片描述
这个是那个大兄弟的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~
1.省,市,区三级关联 2.地址数据本地化 3.自定义对话框 4.支付从xml文件,或对象文件加载 部分代码: package com.demo.selector; import java.io.InputStream; import com.address.selector.City; import com.address.selector.Country; import com.address.selector.Province; import com.address.selector.Region; import com.demo.address.R; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; import android.view.View; import android.view.View.OnClickListener; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.Spinner; import android.widget.AdapterView.OnItemSelectedListener; public class AddressSelectorDialog extends BaseDialog { /**省类型*/ private static final int INDEX_PROVINCE = 1; /**城市类型*/ private static final int INDEX_CITY = 2; /**区县类型*/ private static final int INDEX_REGION = 3; private static Country china = null; private Spinner spnProvince = null; private Spinner spnCity = null; private Spinner spnRegion = null; private ArrayAdapter dptProvince = null; private ArrayAdapter dptCity = null; private ArrayAdapter dptRegion = null; private SpinnerSelectedListener proListener = new SpinnerSelectedListener(); private SpinnerSelectedListener cityListener = new SpinnerSelectedListener(); private SpinnerSelectedListener regionListener = new SpinnerSelectedListener(); //当前选择地区的索引值 private int provinceId = -1; private int cityId = -1; private int regionId = -1; /**选择按钮*/ private Button btnSelect = null; /**回调用事件*/ private OnSelectorCancel selectorCancel = null; @Override public void init(Context context) { // TODO Auto-generated method stub this.context = context; dialog = new Dialog(this.context,R.style.CustomDialog); dialog.setContentView(R.layout.adress_selector); spnProvince = (Spinner)dialog.findViewById(R.id.spin_province); spnProvince.setTag(new Integer(INDEX_PROVINCE)); spnCity = (Spinner)dialog.findViewById(R.id.spin_city); spnCity.setTag(new Integer(INDEX_CITY)); spnRegion = (Spinner)dialog.findViewById(R.id.spin_region); spnRegion.setTag(new Integer(INDEX_REGION)); btnSelect = (Button)dialog.findViewById(R.id.btn_select); btnSelect.setOnClickListener(new OnClickListener(){ @Override public void onClick(View arg0) { // TODO Auto-generated method stub selectAddress(); } }); //当对话框隐藏后调用[回调事件] dialog.setOnCancelListener(new OnCancelListener(){ @Override public void onCancel(DialogInterface arg0) { // TODO Auto-generated method stub if (selectorCancel != null) { String proName = ""; String cityName = ""; String regName = ""; try { Province tmpPro = china.provinceLst.get(provinceId); proName = tmpPro.areaName; City tmpCity = tmpPro.cityLst.get(cityId); cityName = tmpCity.areaName; Region tmpReg = tmpCity.regionLst.get(regionId); regName = tmpReg.areaName; } catch (Exception e) { android.util.Log.e("selectorCancel???", e.getMessage()); } selectorCancel.onSelectorResult(proName, cityName, regName); } } }); //加载数据 if (china == null) { loadCountryAddressData(); } //加载完后,初始化适配器 initAdapters(); } @Override public void update(Object obj) { // TODO Auto-generated method stub } private void loadCountryAddressData() { try { /** * 反序化的包名,类名,版本ID必须一致,否则ClassNotFoundException */ InputStream stream = this.context.getAssets().open("china-city.obj"); china = Country.loadFromStream(stream); //mHandler.sendEmptyMessage(0); } catch (Exception e) { android.util.Log.e("error", e.getMessage()); } } class SpinnerSelectedListener implements OnItemSelectedListener{ @Override public void onItemSelected (AdapterView parent, View view, int position, long id) { // TODO Auto-generated method stub //android.util.Log.i("", String.format("[pos=%d]",position)); int index = (Integer)parent.getTag(); //ArrayAdapter obj = (ArrayAdapter)(parent.getAdapter()); //String value = obj.getItem(position); //android.util.Log.i("", "value=" + value); if (index == INDEX_PROVINCE) { provinceId = position; if (isValideProvinceIndex(provinceId)) { //改变城市选择 Province tmpProvince = china.provinceLst.get(provinceId); String lstNames[] = tmpProvince.listNames(); if (lstNames == null) { //没有城市,当然没区县 provinceId = -1; spnCity.setVisibility(View.INVISIBLE); regionId = -1; spnRegion.setVisibility(View.INVISIBLE); return ; } else { if (spnCity.getVisibility() == View.INVISIBLE) { spnCity.setVisibility(View.VISIBLE); } if (spnRegion.getVisibility() == View.INVISIBLE) { spnRegion.setVisibility(View.INVISIBLE); } } dptCity = new ArrayAdapter(context, android.R.layout.simple_spinner_item, lstNames); dptCity.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spnCity.setAdapter(dptCity); spnCity.setOnItemSelectedListener(cityListener); } } else if (index == INDEX_CITY) { cityId = position; if (isValideCityIndex(provinceId,cityId)) { //改变区选择 Province tmpProvince = china.provinceLst.get(provinceId); City tmpCity = tmpProvince.cityLst.get(cityId); String lstNames[] = tmpCity.listNames(); if (lstNames == null) { //没有区县 regionId = -1; spnRegion.setVisibility(View.INVISIBLE); return ; } else if (spnRegion.getVisibility() == View.INVISIBLE) { spnRegion.setVisibility(View.VISIBLE); } dptRegion = new ArrayAdapter(context, android.R.layout.simple_spinner_item, lstNames); dptRegion.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spnRegion.setAdapter(dptRegion); spnRegion.setOnItemSelectedListener(regionListener); } } else if (index == INDEX_REGION) { regionId = position; } } @Override public void onNothingSelected(AdapterView parent) { // TODO Auto-generated method stub } } private void initAdapters() { dptProvince = new ArrayAdapter(this.context, android.R.layout.simple_spinner_item, china.listNames()); //设置下拉列表的风格 dptProvince.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); //设置适配器 spnProvince.setAdapter(dptProvince); //设置侦听事件 spnProvince.setOnItemSelectedListener(proListener); } private boolean isValideProvinceIndex(int index) { return (china != null) && (index >= 0 && index = 0) && (china.provinceLst.get(provinceIndex).cityLst.size() > cityIndex); } return false; } private void selectAddress() { hide(); } /** * 调协回调事件 * @param selectorCancel */ public void setOnSelectorCancel(OnSelectorCancel selectorCancel) { this.selectorCancel = selectorCancel; } /** * 选择器关闭后,调用回调事件接口 */ public interface OnSelectorCancel { /** * @param proName 省份名 * @param cityName 城市名 * @param regName 区县名 */ public void onSelectorResult(String proName,String cityName,String regName); }; }
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值