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

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

给看下最终的效果图:
使用Android-PickerView实现地址选择器时间选择器_第1张图片
这个是那个大兄弟的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 databases = new HashMap();  

    // 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; iif(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必须一致

使用Android-PickerView实现地址选择器时间选择器_第2张图片

2、省份就一层数据
使用Android-PickerView实现地址选择器时间选择器_第3张图片
3、城市需要先外部嵌套一层省份

使用Android-PickerView实现地址选择器时间选择器_第4张图片
4、第二层list中的才是每个省份所对应的城市
使用Android-PickerView实现地址选择器时间选择器_第5张图片
5、区域最外层(跟省份size一致)
使用Android-PickerView实现地址选择器时间选择器_第6张图片
6、区域第二层(跟对应省份的对应城市的size一致)
使用Android-PickerView实现地址选择器时间选择器_第7张图片
7、区域第三层(跟对应省份的对应城市的对应区域的size)
使用Android-PickerView实现地址选择器时间选择器_第8张图片

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 options1Items = new ArrayList<>();//省
    private ArrayList> options2Items = new ArrayList<>();//市
    private ArrayList>> options3Items = new ArrayList<>();//区
    private ArrayList Provincestr = new ArrayList<>();//省
    private ArrayList> Citystr = new ArrayList<>();//市
    private ArrayList>> 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 cityBeanList = new ArrayList<>();
            ArrayList cityStr = new ArrayList<>();
            //地区集合的集合(注意这里要的是当前省份下面,当前所有城市的地区集合我去)
            ArrayList> options3Items_03 = new ArrayList<>();
            ArrayList> 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 areaBeanList = new ArrayList<>();
                ArrayList 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路)