Android View 滾輪控制元件LoopView+自定義Dialog [時間地域選擇器] Picker
阿新 • • 發佈:2019-02-20
發現了一些好的東西:
https://github.com/weidongjian/androidWheelView
曾經找到過 WheelView。當時江湖救急,直接用了。資料來源太大的話會導致效能降低。
當時有吐槽如果有使用自定義view或者繼承ListView、RecyclerView的就好。
今日找到了使用自定義view方法寫的。
紅色為原始碼, 藍色為哥寫的對應擴充套件。
核心原始碼為LoopView,本來想看看實現原理方法,奈何原始碼被下了毒。
不過值得慶幸的是核心api還在
有了這些東西那麼就可以自己進行一些定製了。
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; } } }
那麼在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進行控制。
maxDaySyncListener 將呼叫 loopDay.setCurrentItem() 修正日期的錯誤。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; }
理所當然在年份和月份上加上這個檢測。
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:
{
"北京": ["東城區","西城區","海淀區",...],
"新疆": ["烏魯木齊","克拉瑪依","阿勒泰"],
"重慶": ["渝中區","大渡口區", ...
....
}
最後程式碼在 dialog-picker中,大家可以進行自己的定製哦,UI 邏輯什麼的。
程式碼地址
https://github.com/iielse/pickerDialog另外推薦
https://github.com/AigeStudio/WheelPicker