自定义组合控件之省市区三级联动选择

来源:互联网 发布:应用搬家软件 编辑:程序博客网 时间:2024/05/18 03:58

自定义组合控件之省市区三级联动选择

需求

  • 一般的购物网站都会有收件地址的填写,为了让用户快速输入自己想要的地址,将全国省市区地址预定好,用户只要动动手指选择就可以。
  • 做这个组合控件之前,是由于项目的需要,就是做地址管理的模块,当时想模仿京东商城的实现,但时间和水平有限,所以将就的做了如下实现。
  • 先罗列当时开发前后的设计思路

设计思路

  • 地址管理模块

    功能:    增删该查    三级联动    默认地址
  • 状态1:用户未登录

    入口1:在商品界面,显示:显示发送至:地址显示-->省,市,区;整个视图属于自定义组合控件,点击整个控件监听点击事件,显示三级地址选项,选择顺序:省,市,区数据加载和缓存    持久化缓存:本地    非持久化缓存:内存动画:点击后从屏幕下方往上弹出动画,或者往下弹出可选择的listView,可以放在Poupwindow中显示或者是alertDialog选择:选择 区 完成后自动关闭弹出框,将数据显示到自定义控件中的Textview中数据回显:用户选择后,保存在sharepreferences中,每次进入商品详情界面,就读取数据回显。(后面发现是在存在服务器上)数据传送:用户结算时将数据传递给用户入口2:在我的中心界面,看不到链接
  • 状态2:用户登录:

    入口1:在商品界面,显示用户已有的地址列表    下面可以显示其他地址:三级联动-->省市区入口2:在我的中心界面,只有用户登录状态可见账户管理连接,进入账户管理-->地址管理连接数据加载和缓存    持久化缓存:本地,服务器数据库    非持久化缓存:内存地址管理界面:头部应该是可以在框架里固定,中间listview展示不同的地址条目,底部显示新建按钮Listview条目信息:收件人姓名,联系电话,联系地址    设为默认值,编辑,删除新建地址的界面:    收货人:    手机号码:    选择联系人:需要读取联系人列表,activity的跳转选择联系人,intent传递意图返回数据    所在地区:又是三级联动:省市区    详情地址:
  • 分析:

    发现弹出选择省市区的弹框可以设置为自定义控件,里面水平放置四个东西,由左向右 //(后面发现可以只用一个listview)    1.文本提示:配送至/地址    2.地址指示小图标    3.省市区地址    4.展开dialog小图标下方可能还有个Textview,显示几点之前下单完成,预计什么时候到达
  • 小结:

    1.点击默认时,有个小bug    重新去数据库获取最新的数据,拿到最新的数据,    解决新增条目设置默认无效的问题,因为如果不去获取数据库最新的数据    集合中新增的条目还没有分配到id,这个id是数据库自动分配的    所以点击设置为默认时,实际上数据库的数据没有改变,必须重新获取最新数据设置2.从数据库拿回来的数据时,最新的数据排在集合最后,让客户看到最新数据,跳到最后一条3.省市区三级联动自定义控件的实现    自定义dialog(去掉ationBar,布局在屏幕底部,相对屏幕高度0.6倍,动态适配不同屏幕分辨率)    + listView:展示省市区的数据    + 本地json数据:全国省市区json字符串    使用一个listview,点击条目时,修改数据集合,更新适配器,达到三级联动效果4.可以优化的地方    1.自定义控件选择后的文字动画效果可以添加,增强动感    2.dialog弹出时可以做成动画    3.读取手机联系人时,应该判断是否为空

具体实现

  • 首先,因为是组合控件,所以自定义的view要继承viewGrup,这里继承LinearLayout

    package skxy.dev.addresslib;import android.app.Activity;import android.app.Dialog;import android.content.Context;import android.os.Handler;import android.util.AttributeSet;import android.view.Display;import android.view.Gravity;import android.view.View;import android.view.Window;import android.view.WindowManager;import android.widget.AdapterView;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.ListView;import android.widget.ProgressBar;import android.widget.TextView;import com.google.gson.Gson;import com.google.gson.reflect.TypeToken;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.util.ArrayList;import java.util.List;import skxy.dev.addresslib.adapter.ProvinceAdapter;import skxy.dev.addresslib.bean.AddressBean;/** * ClassName ReceviceAdressView * Created by skxy on 2016/8/30. * DES 自定义的收货地址视图 */public class ReceviceAdressView extends LinearLayout implements View.OnClickListener, AdapterView.OnItemClickListener {    private Context mContext;    private ImageView mAddressView;    private View mInflatView;    private ListView mListView;    private List<String> mTitleDatas;    private Dialog mDialog;    private List<AddressBean.CityBean> mCityDatas;    private List<AddressBean> mProvinceDatas;    private TextView mAddressTv;    private List<String> mCurrentDatas = new ArrayList<>();//存放当前的数据    private ProvinceAdapter maddressAdapter;    private TextView mTvContent;    private ImageView ivCloase;    private ProgressBar mPb;    //设置地址图标不可见    public void addressIvToggle(boolean isShow) {        if (isShow) {            mAddressView.setVisibility(View.GONE);        } else {            mAddressView.setVisibility(View.VISIBLE);        }    }    /**     * 设置省市区地址内容    */    public void setAddress(String content) {        mAddressTv.setText(content);    }    /**     * 获取省市区数据     *     * @param     */    public String getAddress() {       return sb.toString().replaceAll(">","");    }    public ReceviceAdressView(Context context) {        super(context);    }    public ReceviceAdressView(Context context, AttributeSet attrs) {        super(context, attrs);        this.mContext = context;        initView(context);        initData();        initEvent();    }    /**     * 初始化数据     * 外界触发加载     */    public void initData() {        //标题        mTitleDatas = new ArrayList<>();        //省级        mProvinceDatas = new ArrayList<>();        //市级        mCityDatas = new ArrayList<>();    }    private void initEvent() {        this.setOnClickListener(this);        mListView.setOnItemClickListener(this);        ivCloase.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View view) {                mDialog.dismiss();            }        });    }    //初始化视图    private void initView(Context context) {        View view = View.inflate(context, R.layout.receive_address_view, this);        mAddressTv = (TextView) view.findViewById(R.id.receive_tv_address);        mAddressView = (ImageView) view.findViewById(R.id.receive_iv_address);        //listView        mInflatView = View.inflate(mContext, R.layout.listview, null);        mListView = (ListView) mInflatView.findViewById(R.id.receive_listview);        mListView.setDividerHeight(0);        mTvContent = (TextView) mInflatView.findViewById(R.id.dialog_tv_content);        ivCloase = (ImageView) mInflatView.findViewById(R.id.dialog_iv_close);        mPb = (ProgressBar) mInflatView.findViewById(R.id.receive_progress);        maddressAdapter = new ProvinceAdapter(mContext);        mListView.setAdapter(maddressAdapter);    }    @Override    public void onClick(View view) {        showDialog();    }    Handler handler = new Handler();    /**     * 显示弹出对话框     */    private void showDialog() {        //初始化数据        mTvContent.setText("请选择");        sb.delete(0, sb.length());        index = 0;        mTitleDatas.clear();        mCurrentDatas.clear();        if (mDialog == null) {            mDialog = new Dialog(mContext, R.style.dialog);        }        mDialog.setContentView(mInflatView);        mDialog.setTitle("收货地址");        //设置对其方式        Window dialogWindow = mDialog.getWindow();        WindowManager.LayoutParams lp = dialogWindow.getAttributes();        dialogWindow.setGravity(Gravity.BOTTOM);        Activity mAct = (Activity) mContext;        WindowManager m = mAct.getWindowManager();        Display d = m.getDefaultDisplay(); // 获取屏幕宽、高用        WindowManager.LayoutParams p = dialogWindow.getAttributes(); // 获取对话框当前的参数值        p.height = (int) (d.getHeight() * 0.6); // 高度设置为屏幕的0.6        p.width = (int) (d.getWidth()); // 宽度设置为屏幕的0.65        dialogWindow.setAttributes(p);        mDialog.show();        //子线程请求数据        mPb.setVisibility(View.VISIBLE);        new Thread(new Runnable() {            @Override            public void run() {                //子线程去加载数据                loadDatas();            }        }).start();    }    private void loadDatas() {        //context.getClass().getClassLoader().getResourceAsStream("assets/"+资源名);        //读取本地Json字符串,解析字符串为对应的bean,赋值给对应集合        if (mProvinceDatas.size() != 0) {            //如果内存中有数据,就不需要再加载            for (AddressBean datas : mProvinceDatas) {                String province = datas.name;                mCurrentDatas.add(province);            }        } else {            String jsonString = getDatas();            //解析            resolveDatas(jsonString);        }        //主线程更新UI        handler.post(new Runnable() {            @Override            public void run() {                mPb.setVisibility(View.GONE);                maddressAdapter.setDatas(mCurrentDatas);            }        });    }    private void resolveDatas(String jsonString) {        if (jsonString != null) {            Gson gson = new Gson();            mProvinceDatas = gson.fromJson(jsonString, new TypeToken<List<AddressBean>>() {            }.getType());            for (AddressBean datas : mProvinceDatas) {                //解析后先将省级名字存储到当前数据集合                String province = datas.name;                mCurrentDatas.add(province);            }        }    }    /**     * 获取本地json数据     */    private String getDatas() {        InputStream in = null;        try {            in = mContext.getClass().getClassLoader().getResourceAsStream("assets/" + "addresslist.json");            ByteArrayOutputStream bao = new ByteArrayOutputStream();            byte[] buffer = new byte[1024*8];            int len = 0;            while ((len = in.read(buffer)) != -1) {                bao.write(buffer, 0, len);            }            return bao.toString();//转为string        } catch (IOException e) {            e.printStackTrace();            return null;        } finally {            if (in != null) {                try {                    in.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }    //存储临时的地址    StringBuilder sb = new StringBuilder();    int index = 0;    //listView条目点击事件    @Override    public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {        String name = "";        switch (index) {            case 0://省级                name = mCurrentDatas.get(i);                AddressBean addressBean = mProvinceDatas.get(i);                mCityDatas = addressBean.city;//市级对应的数据集合                mCurrentDatas.clear();//为了重复使用集合,先将原有的数据清空                for (AddressBean.CityBean cityBean : mCityDatas) {                    mCurrentDatas.add(cityBean.name);                }                break;            case 1://市级                name = mCurrentDatas.get(i);                AddressBean.CityBean cityBean = mCityDatas.get(i);                List<String> area = cityBean.area;                mCurrentDatas.clear();                for (String s : area) {                    mCurrentDatas.add(s);                }                break;            case 2://地区                name = mCurrentDatas.get(i);                break;        }        mTitleDatas.add(name);        //设置地址        if (mTitleDatas.size() == 3) {            sb.append(mTitleDatas.get(mTitleDatas.size() - 1));            mTvContent.setText(sb.toString());//dialog中的地址标题            mAddressTv.setText(sb.toString());//整个控件的地址            mDialog.dismiss();        } else {            sb.append(mTitleDatas.get(mTitleDatas.size() - 1)).append(">");            mTvContent.setText(sb.toString());            mAddressTv.setText(sb.toString());//整个控件的地址            maddressAdapter.setDatas(mCurrentDatas);            mListView.setSelection(0);        }        index++;    }}
  • 将省市区三级地址以json格式保存在assets中,需要的时候读取即可。

  • 另外这里开始我是以一个modle的方式开发的,后面才将其作为库的方式添加到项目中使用,这个过程主要有几个步骤,这里总结为三步骤:

    • 1.build.gradle文件中将

      apply plugin: 'com.android.application'替换为:apply plugin: 'com.android.library'
    • 2.将defaultConfig节点中的applicationId去掉,如

      defaultConfig {    //applicationId "org.skxy.www.healthcat"//这里注释掉    minSdkVersion 15    targetSdkVersion 24    versionCode 1    versionName "1.0"}
    • 3.将AndroidManifest文件中的其他节点全部删除,只留manifest节点,如

          <manifest xmlns:android="http://schemas.android.com/apk/res/android"              package="skxy.dev.addresslib">    </manifest>
  • 这样就把一个普通的Modle做成Lib了。

  • 在需要使用的项目中添加依赖

    compile project(':addresslib')
  • 在项目布局文件中使用

    <skxy.dev.addresslib.ReceviceAdressViewandroid:layout_width="wrap_content"android:layout_height="45dp"/>
  • 项目中获取控件选择的地址

    mAdressView = (ReceviceAdressView) findViewById(R.id.address);//获取自定义控件中的地址String address = mAdressView.getAddress();
  • 设置是否显示图标

    mAdressView.setAddressIv(false);
  • 好了,来看看最终效果


  • 源码地址:https://github.com/skxy2016/AddressView
  • 欢迎探讨
0 0
原创粉丝点击