Android View 滚轮控件LoopView+自定义Dialog [时间地域选择器] Picker

Android View 滚轮控件LoopView+自定义Dialog [时间地域选择器] Picker_第1张图片


发现了一些好的东西: 

https://github.com/weidongjian/androidWheelView


曾经找到过 WheelView。当时江湖救急,直接用了。数据源太大的话会导致性能降低。
当时有吐槽如果有使用自定义view或者继承ListView、RecyclerView的就好。
今日找到了使用自定义view方法写的。

Android View 滚轮控件LoopView+自定义Dialog [时间地域选择器] Picker_第2张图片


红色为源码, 蓝色为哥写的对应扩展。
核心源码为LoopView,本来想看看实现原理方法,奈何源码被下了毒。
不过值得庆幸的是核心api还在


Android View 滚轮控件LoopView+自定义Dialog [时间地域选择器] Picker_第3张图片


有了这些东西那么就可以自己进行一些定制了。


public class DataPickerDialog extends Dialog {

    private Params params;

    public DataPickerDialog(Context context, int themeResId) {
        super(context, themeResId);
    }

    private void setParams(DataPickerDialog.Params params) {
        this.params = params;
    }

    public interface OnDataSelectedListener {
        void onDataSelected(String itemValue);
    }

    private static final class Params {
        private boolean shadow = true;
        private boolean canCancel = true;
        private LoopView loopData;
        private String title; 
        private String unit;
        private int initSelection;
        private OnDataSelectedListener callback;
        private final List<String> dataList = new ArrayList<>();
    }

    public static class Builder {
        private final Context context;
        private final DataPickerDialog.Params params;

        public Builder(Context context) {
            this.context = context;
            params = new DataPickerDialog.Params();
        }

        private final String getCurrDateValue() {
            return params.loopData.getCurrentItemValue();
        }

        public Builder setData(List<String> dataList) {
            params.dataList.clear();
            params.dataList.addAll(dataList);
            return this;
        }

        public Builder setTitle(String title) {
            params.title = title;
            return this;
        }

        public Builder setUnit(String unit) {
            params.unit = unit;
            return this;
        }

        public Builder setSelection(int selection) {
            params.initSelection = selection;
            return this;
        }

        public Builder setOnDataSelectedListener(OnDataSelectedListener onDataSelectedListener) {
            params.callback = onDataSelectedListener;
            return this;
        }


        public DataPickerDialog create() {
            final DataPickerDialog dialog = new DataPickerDialog(context, params.shadow ? R.style.Theme_Light_NoTitle_Dialog : R.style.Theme_Light_NoTitle_NoShadow_Dialog);
            View view = LayoutInflater.from(context).inflate(R.layout.layout_picker_data, null);

            if (!TextUtils.isEmpty(params.title)) {
                TextView txTitle = (TextView) view.findViewById(R.id.tx_title);
                txTitle.setText(params.title);
            }
            if (!TextUtils.isEmpty(params.unit)) {
                TextView txUnit = (TextView) view.findViewById(R.id.tx_unit);
                txUnit.setText(params.unit);
            }

            final LoopView loopData = (LoopView) view.findViewById(R.id.loop_data);
            loopData.setArrayList(params.dataList);
            loopData.setNotLoop();
            if (params.dataList.size() > 0) loopData.setCurrentItem(params.initSelection);
            view.findViewById(R.id.tx_finish).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    dialog.dismiss();
                    if (params.callback != null) params.callback.onDataSelected(getCurrDateValue());
                }
            });


            Window win = dialog.getWindow();
            win.getDecorView().setPadding(0, 0, 0, 0);
            WindowManager.LayoutParams lp = win.getAttributes();
            lp.width = WindowManager.LayoutParams.MATCH_PARENT;
            lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
            win.setAttributes(lp);
            win.setGravity(Gravity.BOTTOM);
            win.setWindowAnimations(R.style.Animation_Bottom_Rising);

            dialog.setContentView(view);
            dialog.setCanceledOnTouchOutside(params.canCancel);
            dialog.setCancelable(params.canCancel);

            params.loopData = loopData;
            dialog.setParams(params);

            return dialog;
        }
    }
}



啊Builder - 自定义Dialog 标配代码走起。

那么在Activity中

private final void showDialog() {
        DataPickerDialog.Builder builder = new DataPickerDialog.Builder(this);
        List<String> data = Arrays.asList(new String[]{"a", "b", "c", "d", "e", "f", "g", "h"});

        DataPickerDialog dialog = builder.setUnit("单位").setData(data).setSelection(1).setTitle("标题")
                .setOnDataSelectedListener(new DataPickerDialog.OnDataSelectedListener() {
                    @Override
                    public void onDataSelected(String itemValue) {
                        Toast.makeText(getApplicationContext(), itemValue, Toast.LENGTH_SHORT).show();
                    }
                }).create();

        dialog.show();
    }
以上标准通用的选择器就可以直接用上啦。
时间选择器
ta的不同之处在于日期方面需要联动。选择了月份后,日期可能要变。
比如选择了31号后切换月份到2月。
这里需要实现LoopListener来监听一些逻辑,再使用Calender进行控制。

public DatePickerDialog create() {
            final DatePickerDialog dialog = new DatePickerDialog(context, params.shadow ? R.style.Theme_Light_NoTitle_Dialog : R.style.Theme_Light_NoTitle_NoShadow_Dialog);
            View view = LayoutInflater.from(context).inflate(R.layout.layout_picker_date, null);

            final LoopView loopDay = (LoopView) view.findViewById(R.id.loop_day);
            loopDay.setArrayList(d(1, 30));
            loopDay.setCurrentItem(15);
            loopDay.setNotLoop();

            Calendar c = Calendar.getInstance();
            int year = c.get(Calendar.YEAR);
            final LoopView loopYear = (LoopView) view.findViewById(R.id.loop_year);
            loopYear.setArrayList(d(MIN_YEAR, year - MIN_YEAR + 1));
            loopYear.setCurrentItem(year - MIN_YEAR - 25);
            loopYear.setNotLoop();

            final LoopView loopMonth = (LoopView) view.findViewById(R.id.loop_month);
            loopMonth.setArrayList(d(1, 12));
            loopMonth.setCurrentItem(6);
            loopMonth.setNotLoop();

            final LoopListener maxDaySyncListener = new LoopListener() {
                @Override
                public void onItemSelect(int item) {
                    Calendar c = Calendar.getInstance();
                    c.set(Integer.parseInt(loopYear.getCurrentItemValue()), Integer.parseInt(loopMonth.getCurrentItemValue()) - 1, 1);
                    c.roll(Calendar.DATE, false);
                    int maxDayOfMonth = c.get(Calendar.DATE);
                    int fixedCurr = loopDay.getCurrentItem();
                    loopDay.setArrayList(d(1, maxDayOfMonth));
                    // 修正被选中的日期最大值
                    if (fixedCurr > maxDayOfMonth) fixedCurr = maxDayOfMonth - 1;
                    loopDay.setCurrentItem(fixedCurr);
                }
            };
            loopYear.setListener(maxDaySyncListener);
            loopMonth.setListener(maxDaySyncListener);

            view.findViewById(R.id.tx_finish).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    dialog.dismiss();
                    params.callback.onDateSelected(getCurrDateValues());
                }
            });

            Window win = dialog.getWindow();
            win.getDecorView().setPadding(0, 0, 0, 0);
            WindowManager.LayoutParams lp = win.getAttributes();
            lp.width = WindowManager.LayoutParams.MATCH_PARENT;
            lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
            win.setAttributes(lp);
            win.setGravity(Gravity.BOTTOM);
            win.setWindowAnimations(R.style.Animation_Bottom_Rising);

            dialog.setContentView(view);
            dialog.setCanceledOnTouchOutside(params.canCancel);
            dialog.setCancelable(params.canCancel);

            params.loopYear = loopYear;
            params.loopMonth = loopMonth;
            params.loopDay = loopDay;
            dialog.setParams(params);

            return dialog;
}
maxDaySyncListener  将调用 loopDay.setCurrentItem() 修正日期的错误。
理所当然在年份和月份上加上这个检测。
loopYear.setListener(maxDaySyncListener);
loopMonth.setListener(maxDaySyncListener);


地域选择器
ta的不同之处在于数据源,然后联动。
Map<String, List<String>>
Key表示城市.value表示区域

public Builder(Context context) {
            this.context = context;
            params = new RegionPickerDialog.Params();

            try {
                InputStreamReader inputReader = new InputStreamReader(context.getAssets().open("city_data.json"));
                BufferedReader bufReader = new BufferedReader(inputReader);
                String line = "";
                StringBuffer result = new StringBuffer();
                while ((line = bufReader.readLine()) != null) {
                    result.append(line);
                }
                params.dataList = new Gson().fromJson(result.toString(), new TypeToken<Map<String, List<String>>>() {
                }.getType());
            } catch (Exception e) {
                Log.e("RegionPickerDialog", "The Region source file does not exist or has been damaged");
                params.dataList = new HashMap<>();
            }
}

那这里我从 assets 中取去数据源。 
city_data.json中的数据 like this:

{
    "北京": ["东城区","西城区","海淀区",...],
    "新疆": ["乌鲁木齐","克拉玛依","阿勒泰"],
    "重庆": ["渝中区","大渡口区", ...
    ....
}


Android View 滚轮控件LoopView+自定义Dialog [时间地域选择器] Picker_第4张图片

最后代码在 dialog-picker中,大家可以进行自己的定制哦,UI 逻辑什么的。

代码地址

https://github.com/iielse/pickerDialog


另外推荐 

https://github.com/AigeStudio/WheelPicker

你可能感兴趣的:(UI,android,view,自定义,界面)