Android 实现四级联动地址选择器

效果如下:
在这里插入图片描述
代码传送阵:

address_selector.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/ll_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:alpha="0"
    android:background="#b2000000"
    android:orientation="vertical"
    tools:alpha="1">

    <android.support.constraint.ConstraintLayout
        android:id="@+id/cl_container"
        android:clickable="true"
        android:focusable="true"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/selector_bg">

        <TextView
            android:id="@+id/textViewProvince"
            android:layout_width="wrap_content"
            android:layout_height="@dimen/dp_42"
            android:layout_marginStart="@dimen/dp_18"
            android:ellipsize="end"
            android:paddingTop="@dimen/dp_13"
            android:maxWidth="@dimen/dp_80"
            android:maxLines="1"
            android:text="请选择"
            android:textColor="@drawable/selector_item_text_color"
            android:textSize="@dimen/sp_14"
            android:textStyle="bold"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="请选择请选择"
            tools:visibility="visible" />

        <TextView
            android:id="@+id/textViewCity"
            android:layout_width="wrap_content"
            android:layout_height="@dimen/dp_44"
            android:layout_marginStart="@dimen/dp_21"
            android:ellipsize="end"
            android:paddingTop="@dimen/dp_13"
            android:maxWidth="@dimen/dp_80"
            android:maxLines="1"
            android:text="请选择"
            android:textColor="@drawable/selector_item_text_color"
            android:textSize="@dimen/sp_14"
            android:textStyle="bold"
            android:visibility="invisible"
            app:layout_constraintLeft_toRightOf="@id/textViewProvince"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="请选择请选择"
            tools:visibility="visible" />

        <TextView
            android:id="@+id/textViewArea"
            android:layout_width="wrap_content"
            android:layout_height="@dimen/dp_44"
            android:layout_marginStart="@dimen/dp_21"
            android:ellipsize="end"
            android:paddingTop="@dimen/dp_13"
            android:maxWidth="@dimen/dp_80"
            android:maxLines="1"
            android:text="请选择"
            android:textColor="@drawable/selector_item_text_color"
            android:textSize="@dimen/sp_14"
            android:textStyle="bold"
            android:visibility="invisible"
            app:layout_constraintLeft_toRightOf="@id/textViewCity"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="请选择请选择"
            tools:visibility="visible" />

        <TextView
            android:id="@+id/textViewStreet"
            android:layout_width="wrap_content"
            android:layout_height="@dimen/dp_44"
            android:layout_marginStart="@dimen/dp_21"
            android:ellipsize="end"
            android:paddingTop="@dimen/dp_13"
            android:maxWidth="@dimen/dp_80"
            android:maxLines="1"
            android:text="请选择"
            android:textColor="@drawable/selector_item_text_color"
            android:textSize="@dimen/sp_14"
            android:textStyle="bold"
            android:visibility="invisible"
            app:layout_constraintLeft_toRightOf="@id/textViewArea"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="请选择请选择"
            tools:visibility="visible" />

        <android.support.v7.widget.AppCompatImageView
            android:id="@+id/iv_close"
            android:layout_width="@dimen/dp_23"
            android:layout_height="@dimen/dp_23"
            android:layout_marginTop="@dimen/dp_12"
            android:layout_marginEnd="@dimen/dp_12"
            android:padding="@dimen/dp_5"
            android:src="@drawable/address_close"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <View
            android:id="@+id/indicator"
            android:layout_width="@dimen/dp_24"
            android:layout_height="@dimen/dp_2"
            android:background="#000000"
            app:layout_constraintLeft_toLeftOf="@id/textViewProvince"
            app:layout_constraintTop_toBottomOf="@id/textViewProvince" />

        <View
            android:id="@+id/divider"
            android:layout_width="match_parent"
            android:layout_height="0.5dp"
            android:background="#e5e5e5"
            app:layout_constraintTop_toBottomOf="@id/indicator" />

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toBottomOf="@id/divider" />
    </android.support.constraint.ConstraintLayout>

</LinearLayout>

AddressDialog.java

public class AddressDialog extends DialogFragment {
    @BindView(R.id.textViewProvince)
    TextView textViewProvince;
    @BindView(R.id.textViewCity)
    TextView textViewCity;
    @BindView(R.id.textViewArea)
    TextView textViewArea;
    @BindView(R.id.textViewStreet)
    TextView textViewStreet;
    @BindView(R.id.iv_close)
    AppCompatImageView ivClose;
    @BindView(R.id.indicator)
    View indicator;
    @BindView(R.id.divider)
    View divider;
    @BindView(R.id.recycler_view)
    RecyclerView recyclerView;
    @BindView(R.id.cl_container)
    ConstraintLayout clContainer;
    @BindView(R.id.ll_container)
    LinearLayout llContainer;
    Unbinder unbinder;
    private CompositeDisposable compositeDisposable = new CompositeDisposable();
    private float measuredHeight = 0F;
    // 当前tab  0省1市2区
    private int currentTab = 0;
    // 省
    private ArrayList<County> proviceList = new ArrayList<>();
    // 市
    private ArrayList<County> cityList = new ArrayList<>();
    private ConcurrentHashMap<String, ArrayList<County>> cityMap = new ConcurrentHashMap<>();
    // 区
    private ArrayList<County> areaList = new ArrayList<>();
    private ConcurrentHashMap<String, ArrayList<County>> areaMap = new ConcurrentHashMap<>();
    //街道
    private ArrayList<County> streetList = new ArrayList<>();
    private ConcurrentHashMap<String, ArrayList<County>> streetMap = new ConcurrentHashMap<>();

    private BaseQuickAdapter<County, BaseViewHolder> mAdapter;
    private OnSelectListener listener;
    private String textProvince = "";
    private String textCity = "";
    private String textArea = "";
    private String textStreet = "";
    private ProgressDialog mProgressDialog;
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.address_selector, container, false);
        unbinder = ButterKnife.bind(this, view);
        return view;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStyle(STYLE_NO_TITLE, R.style.Style_Dialog_Transparent);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        textViewProvince.setSelected(true);
        float measureText = textViewProvince.getPaint().measureText("请选择");
        indicator.getLayoutParams().width = (int) measureText;
        LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)clContainer.getLayoutParams();
        layoutParams.topMargin = listener.getHeight() == 0 ? UIUtil.dip2px(MyApplication.applicationContext,180f) :listener.getHeight();
        clContainer.setLayoutParams(layoutParams);
        initData();
        initClick();

        ViewUtils.postMeasured(clContainer, new Runnable() {
            @Override
            public void run() {
                llContainer.animate().alpha(1f).setDuration(150).start();

                float measuredHeight = (float)clContainer.getMeasuredHeight();
                ObjectAnimator translationY = ObjectAnimator.ofFloat(clContainer, "translationY", measuredHeight, 0f);
                translationY.setDuration(150);
                translationY.setInterpolator(new LinearInterpolator());
                translationY.start();
            }
        });

    }

    private void initData() {
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        mAdapter=new BaseQuickAdapter<County, BaseViewHolder>(R.layout.item_area) {
            @Override
            protected void convert(@NonNull BaseViewHolder helper, final County item) {
                helper.setText(R.id.textView,item.name);
                helper.getConvertView().setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        switch (currentTab) {
                            case 0:
                                getCityList(new Consumer<ArrayList<County>>() {
                                    @Override
                                    public void accept(ArrayList<County> counties) throws Exception {
                                        // 本地是否存在
                                        if (cityMap.containsKey(item.code)){
                                            ArrayList<County> list = cityMap.get(item.code);
                                            if (list==null) {
                                                list=new ArrayList<>();
                                            }
                                            notifyAdapter(list);
                                        }else {
                                            for (County next : cityList) {
                                                if (cityMap.containsKey(item.code) && item.code.equals(next.provinceCode)) {
                                                    ArrayList<County> list = cityMap.get(item.code);
                                                    if (list != null) {
                                                        list.add(next);
                                                        cityMap.put(next.provinceCode, list);
                                                    }
                                                } else {
                                                    @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
                                                    ArrayList<County> listCounty = new ArrayList<>();
                                                    listCounty.add(next);
                                                    cityMap.put(next.provinceCode, listCounty);
                                                }
                                            }
                                        }
                                        ArrayList<County> list = cityMap.get(item.code);
                                        notifyAdapter(list);
                                    }
                                });
                                break;
                            case 1:
                                getAreaList(new Consumer<ArrayList<County>>() {
                                    @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
                                    @Override
                                    public void accept(ArrayList<County> list) throws Exception {
                                        if (areaMap.containsKey(item.code)) {
                                            ArrayList<County> arrayList = areaMap.get(item.code);
                                            if (arrayList==null) {
                                                arrayList =new ArrayList<>();
                                            }
                                            notifyAdapter(arrayList);
                                        }else {
                                            for (County county : areaList) {
                                                if (areaMap.containsKey(item.code) && item.code.equals(county.cityCode)) {
                                                    ArrayList<County> countyArrayList = areaMap.get(item.code);
                                                    if (countyArrayList != null) {
                                                        countyArrayList.add(county);
                                                        areaMap.put(county.code, countyArrayList);
                                                    }
                                                }else {
                                                    ArrayList<County> arrayList = new ArrayList<>();
                                                    arrayList.add(county);
                                                    areaMap.put(county.cityCode,arrayList);
                                                }
                                            }
                                        }
                                        ArrayList<County> arrayList = areaMap.get(item.code);
                                        notifyAdapter(arrayList);
                                    }
                                });
                                break;
                            case 2:

                                if (mProgressDialog == null) {
                                    mProgressDialog = new ProgressDialog(getActivity());
                                }

                                mProgressDialog.setCanceledOnTouchOutside(false);
                                mProgressDialog.setCancelable(true);
                                mProgressDialog.show();

                                getStreetList(new Consumer<ArrayList<County>>() {
                                    @Override
                                    public void accept(ArrayList<County> list) throws Exception {

                                        if (streetMap.containsKey(item.code)) {
                                            ArrayList<County> arrayList = streetMap.get(item.code);
                                            if (arrayList==null) {
                                                arrayList =new ArrayList<>();
                                            }
                                            notifyAdapter(arrayList);
                                        }else {
                                            for (County county : streetList) {
                                                if (streetMap.containsKey(item.code) && item.code.equals(county.areaCode)) {
                                                    ArrayList<County> countyArrayList = streetMap.get(item.code);
                                                    if (countyArrayList != null) {
                                                        countyArrayList.add(county);
                                                        streetMap.put(county.code, countyArrayList);
                                                    }
                                                }else {
                                                    ArrayList<County> arrayList = new ArrayList<>();
                                                    arrayList.add(county);
                                                    streetMap.put(county.areaCode,arrayList);
                                                }
                                            }
                                        }
                                        ArrayList<County> arrayList = streetMap.get(item.code);
                                        notifyAdapter(arrayList);

                                        if (mProgressDialog != null) {
                                            ThreadHelper.getInstance().runOnUiPostDelayed(new Runnable() {
                                                @Override
                                                public void run() {
                                                    mProgressDialog.dismiss();
                                                }
                                            },1000);

                                        }
                                    }
                                });
                        }

                        upTabStatus(item.name);
                        indicatorAnim();
                    }
                });

            }
        };

        recyclerView.setAdapter(mAdapter);
        getProvinceList(new Consumer<ArrayList<County>>() {
            @Override
            public void accept(ArrayList<County> list) throws Exception {
                notifyAdapter(proviceList);
            }
        });
    }



    private void initClick() {
        ivClose.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                closeDialog();
            }
        });

        llContainer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                closeDialog();
            }
        });

    }

    private void notifyAdapter(ArrayList<County> list){
        mAdapter.getData().clear();
        mAdapter.addData(list);
    }

    //indicator 动画
    private void indicatorAnim(){
        switch (currentTab){
            case 1:
                float text = textViewProvince.getPaint().measureText(textProvince);
                indicator.animate().translationX(text+UIUtil.dip2pxf(Objects.requireNonNull(getContext()),21f)).setDuration(200).start();
                break;
            case 2:
                float measureText = textViewProvince.getPaint().measureText(textProvince);
                float measureText1 = textViewCity.getPaint().measureText(textCity);
                indicator.animate().translationX(measureText+measureText1+UIUtil.dip2pxf(Objects.requireNonNull(getContext()),42f)).setDuration(200).start();
                break;
            case 3:
                float measure = textViewProvince.getPaint().measureText(textProvince);
                float measure1 = textViewCity.getPaint().measureText(textCity);
                float measure2 = textViewArea.getPaint().measureText(textArea);
                indicator.animate().translationX(measure+measure1+measure2+UIUtil.dip2pxf(Objects.requireNonNull(getContext()),63f)).setDuration(200).start();

        }
    }

    private void upTabStatus(String name){
        switch (currentTab){
            case 0:
                textProvince=name;
                textViewProvince.setText(name);
                textViewCity.setVisibility(View.VISIBLE);
                currentTab =1;
                break;
            case 1:
                textCity=name;
                textViewCity.setText(name);
                textViewArea.setVisibility(View.VISIBLE);
                currentTab =2;
                break;
            case 2:
                textArea=name;
                textViewArea.setText(name);
                textViewStreet.setVisibility(View.VISIBLE);
                currentTab=3;
                break;
            case 3:
                textStreet=name;
                textViewStreet.setText(name);
                dismissAllowingStateLoss();
                break;

        }
        textViewProvince.setSelected(currentTab==0);
        textViewCity.setSelected(currentTab==1);
        textViewArea.setSelected(currentTab==2);
        textViewStreet.setSelected(currentTab==3);
    }

    private void getProvinceList(Consumer<ArrayList<County>> consumer){
        Disposable subscribe1 = Observable.create((new ObservableOnSubscribe() {
            public final void subscribe(@NonNull ObservableEmitter it) {
                if (proviceList.isEmpty()) {
                    String json = getJson("address/provinces.json", getContext());
                    ArrayList<County>  list = fromJson(json,new TypeToken<ArrayList<County>>(){}.getType());
                    if (list==null) {
                        list =new ArrayList<>();
                    }
                    proviceList.addAll(list);
                }

                if (!proviceList.isEmpty()) {
                    it.onNext(proviceList);
                }
            }
        })).observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()).subscribe(consumer, new Consumer<Throwable>() {
            @Override
            public void accept(Throwable throwable) throws Exception {

            }
        });
        compositeDisposable.add(subscribe1);
    }

    private void getCityList(Consumer<ArrayList<County>> consumer){
        Disposable subscribe1 = Observable.create((new ObservableOnSubscribe() {
            public final void subscribe(@NonNull ObservableEmitter it) {
                if (cityList.isEmpty()) {
                    String json = getJson("address/cities.json", getContext());
                    ArrayList<County>  list = fromJson(json,new TypeToken<ArrayList<County>>(){}.getType());
                    if (list==null) {
                        list =new ArrayList<>();
                    }
                    cityList.addAll(list);
                }

                if (!cityList.isEmpty()) {
                    it.onNext(cityList);
                }
            }
        })).observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()).subscribe(consumer, new Consumer<Throwable>() {
            @Override
            public void accept(Throwable throwable) throws Exception {

            }
        });

        compositeDisposable.add(subscribe1);
    }

    private void getAreaList(Consumer<ArrayList<County>> consumer){
        Disposable subscribe1 = Observable.create((new ObservableOnSubscribe() {
            public final void subscribe(@NonNull ObservableEmitter it) {
                if (areaList.isEmpty()) {
                    String json = getJson("address/areas.json", getContext());
                    ArrayList<County>  list = fromJson(json,new TypeToken<ArrayList<County>>(){}.getType());
                    if (list==null) {
                        list =new ArrayList<>();
                    }
                    areaList.addAll(list);
                }

                if (!areaList.isEmpty()) {
                    it.onNext(areaList);
                }
            }
        })).observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()).subscribe(consumer, new Consumer<Throwable>() {
            @Override
            public void accept(Throwable throwable) throws Exception {

            }
        });
        compositeDisposable.add(subscribe1);
    }

    private void getStreetList(Consumer<ArrayList<County>> consumer){
        Disposable subscribe1 = Observable.create((new ObservableOnSubscribe() {
            public final void subscribe(@NonNull ObservableEmitter it) {
                if (streetList.isEmpty()) {
                    String json = getJson("address/streets.json", getContext());
                    ArrayList<County>  list = fromJson(json,new TypeToken<ArrayList<County>>(){}.getType());
                    if (list==null) {
                        list =new ArrayList<>();
                    }
                    streetList.addAll(list);
                }

                if (!streetList.isEmpty()) {
                    it.onNext(streetList);
                }
            }
        })).observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()).subscribe(consumer, new Consumer<Throwable>() {
            @Override
            public void accept(Throwable throwable) throws Exception {

            }
        });
        compositeDisposable.add(subscribe1);

    }


    public  String getJson(String fileName, Context context) {
        StringBuilder stringBuilder = new StringBuilder();
        try {
            InputStream is = context.getAssets().open(fileName);
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                stringBuilder.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return stringBuilder.toString();
    }


    @Nullable
    public  <T> T fromJson(String json, Type typeOfT) {
        try {
            return initGson().fromJson(json, typeOfT);
        } catch (JsonSyntaxException e) {
            e.printStackTrace();
        }
        return null;
    }


    private  Gson initGson(){
        GsonBuilder gsonBuilder = new GsonBuilder();
        try {
            Class builder = gsonBuilder.getClass();
            Field f = builder.getDeclaredField("instanceCreators");
            f.setAccessible(true);
            Map<Type, InstanceCreator<?>> val = (Map<Type, InstanceCreator<?>>) f.get(gsonBuilder);//得到此属性的值
            //注册数组的处理器
            ConstructorConstructor constructorConstructor = new ConstructorConstructor(val);
//            gsonBuilder.registerTypeAdapterFactory(new ReflectiveTypeAdapterFactory(constructorConstructor, FieldNamingPolicy.IDENTITY,
//                    Excluder.DEFAULT,new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor)));
            gsonBuilder.registerTypeAdapterFactory(new MapTypeAdapterFactory(constructorConstructor,false));
            gsonBuilder.registerTypeAdapterFactory(new CollectionTypeAdapterFactory(constructorConstructor));
//            gsonBuilder.registerTypeAdapter(String.class,new StringTypeAdapter());
            gsonBuilder.registerTypeAdapter(int.class,new IntegerTypeAdapter());
            gsonBuilder.registerTypeAdapter(Integer.class,new IntegerTypeAdapter());
//            gsonBuilder.registerTypeAdapter(long.class,new LongTypeAdapter());
//            gsonBuilder.registerTypeAdapter(Long.class,new LongTypeAdapter());
//            gsonBuilder.registerTypeAdapter(double.class,new DoubleTypeAdapter());
//            gsonBuilder.registerTypeAdapter(Double.class,new DoubleTypeAdapter());
//            gsonBuilder.registerTypeAdapter(boolean.class,new BooleanTypeAdapter());
//            gsonBuilder.registerTypeAdapter(Boolean.class,new BooleanTypeAdapter());
//            gsonBuilder.registerTypeAdapter(float.class,new FloadTypeAdapter());
//            gsonBuilder.registerTypeAdapter(Float.class,new FloadTypeAdapter());

        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return gsonBuilder.enableComplexMapKeySerialization().create();
    }


    public void closeDialog() {
            clContainer.animate().translationY(measuredHeight).setDuration(150).withEndAction(new Runnable() {
                @Override
                public void run() {
                    dismissAllowingStateLoss();
                }
            }).start();
    }


    public void dismissAllowingStateLoss() {
        super.dismissAllowingStateLoss();
        if (!TextUtils.isEmpty(textProvince) && !TextUtils.isEmpty(textCity) && !TextUtils.isEmpty(textArea)&&!TextUtils.isEmpty(textStreet)) {
            listener.onSelect(textProvince, textCity, textCity,textStreet);
        }
        listener.isMissing();
        compositeDisposable.dispose();
    }


    public void setOnSelectListener(OnSelectListener listener) {
        this.listener = listener;
    }

    public void setHeight(int top) {

    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        unbinder.unbind();
    }

    public interface OnSelectListener {
        int getHeight();

        void onSelect(String province, String city, String area,String street);

        void isMissing();
    }

}

使用方法

 private void addressDialog() {
        AddressDialog addressDialog =new AddressDialog();
        addressDialog.setOnSelectListener(new AddressDialog.OnSelectListener() {
            @Override
            public int getHeight() {
                return 0;
            }

            @SuppressLint("SetTextI18n")
            @Override
            public void onSelect(String province, String city, String area, String street) {
                tvPositionRtrCommunity.setText(province+"  "+city+"  "+area+"  "+street);
            }

            @Override
            public void isMissing() {
                isShowDialog = true;
            }
        });
        addressDialog.show(getSupportFragmentManager(),AddressDialog.class.getSimpleName());
    }

资源下载

猜你喜欢

转载自blog.csdn.net/qq_42795723/article/details/107740471
今日推荐