效果如图:
代码如下:
package com.example.listpickerdialog;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
/**
* Created by nlx on 2018-09-18, at 14:19
*
* date picker
*/
public class DatePickerPop extends PopupWindow {
private static ContentType[] mContentType;
private ArrayList mDayListData;
private MyAdapter mDayListAdapter;
private Calendar mCurrentCalendar;
private onConfirmListener mListener;
private ListView mDayListView;
private String[] mDefaultDate;
public enum ContentType {
YEAR,
DAY,
MONTH;
}
//btn
public static final int BACKGRO_COLOR_BTN = Color.parseColor("#b6bbc2");
//title
public static final int BACKGRO_COLOR_TITLE = Color.parseColor("#D7DCDF");
//item
public static final int HEIGHT = 700;
public static final int ITEM_COUNT = 5;
public static final int ITEM_HEIGHT = HEIGHT / (ITEM_COUNT + 1) + 10;
//text
public static final int TEXT_SIZE_FOCUSING = 28;
public static final int TEXT_SIZE_DEFAULT = 22;
public static final int TEXT_COLOT_DEFAULT = Color.parseColor("#b6bbc2");
public static final int TEXT_COLOR_FOCUSING = Color.parseColor("#1a1a1a");
//content
public static final int YEAR_OFFSET = 50;
private Context mContext;
private TextView mCurrentFocusingViewInMonthList;
private TextView mCurrentFocusingViewInYearList;
private TextView mCurrentFocusingViewInDayList;
public DatePickerPop(Context context) {
super(context);
mContext = context;
setHeight(HEIGHT);
setOutsideTouchable(true);
}
/**
* 内容显示什么,参数顺序决定内容布局
*
* @param type
*/
public DatePickerPop setContentType(ContentType... type) {
mContentType = type;
initView();
return this;
}
/**
* 点击确定的listener
*
* @param listener
* @return
*/
public DatePickerPop setOnConfirmListener(onConfirmListener listener) {
mListener = listener;
return this;
}
/**
* 设置弹出时默认选中的时间,注意与{@link #setContentType(ContentType...)}要匹配
*
* 如果为空那么默认选中当前日期
*
* 如果超过日期范围那么会选中最后一个
*
* @param defaultDate
* @return
*/
public DatePickerPop setDefaultDate(String... defaultDate) {
mDefaultDate = defaultDate;
return this;
}
/**
* 如果使用popWindow的showXXX方法,可能需要将{@linkplain #initView()}的位置放在重写后的方法中
*
* @param parentView
*/
public void showPop(View parentView) {
initView();
this.showAtLocation(parentView, Gravity.BOTTOM, 0, 0);
}
//addChildView
private void initView() {
LinearLayout view = new LinearLayout(mContext);
view.setOrientation(LinearLayout.VERTICAL);
FrameLayout contentPar = new FrameLayout(mContext);
LinearLayout ll_main = new LinearLayout(mContext);
ll_main.setOrientation(LinearLayout.HORIZONTAL);
if (null == mContentType || mContentType.length < 0) {
TextView child = new TextView(mContext);
child.setText("没有设置该显示什么内容,请调用setContentType(...)");
child.setTextColor(Color.RED);
child.setGravity(Gravity.CENTER);
ll_main.addView(child);
}
//set current day
mCurrentCalendar = Calendar.getInstance();
mCurrentCalendar.setTime(new Date());
for (int i = 0; null != mContentType && i < mContentType.length; i++) {
if (mContentType[i] == ContentType.MONTH) {
//MONTH
ArrayList monthLists = new ArrayList<>();
//前后加空item,以便可以选到所有的item
for (int i1 = 0; i1 < ITEM_COUNT / 2; i1++) {
monthLists.add("");
}
for (int j = 1; j < 13; j++) {
monthLists.add(j + "");
}
//前后加空item,以便可以选到所有的item
for (int i1 = 0; i1 < ITEM_COUNT / 2; i1++) {
monthLists.add("");
}
ListView monthListView = new ListView(mContext);
monthListView.setVerticalScrollBarEnabled(false);
monthListView.setDivider(null);
final MyAdapter monthAdapter = new MyAdapter(monthLists, ITEM_HEIGHT);
monthListView.setAdapter(monthAdapter);
LinearLayout.LayoutParams param0 = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT);
param0.weight = 1;
ll_main.addView(monthListView, param0);
monthListView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
//when stop scrolling
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
refreshDayList();
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mCurrentFocusingViewInMonthList = setFocusingViewStyle(firstVisibleItem, visibleItemCount, monthAdapter, mCurrentFocusingViewInMonthList);
}
});
if (null != mDefaultDate && i < mDefaultDate.length && !TextUtils.isEmpty(mDefaultDate[i])) {
int position = 0;
try {
position = Integer.parseInt(mDefaultDate[i]) - 1;
} catch (Exception e) {
e.printStackTrace();
}
monthListView.setSelection(position);
} else {
//current day as default position
monthListView.setSelection(mCurrentCalendar.get(Calendar.MONTH));
}
} else if (mContentType[i] == ContentType.YEAR) {
//YEAR
ArrayList yearList = new ArrayList<>();
int currentYear = mCurrentCalendar.get(Calendar.YEAR);
for (int k = 0; k < YEAR_OFFSET; k++) {
yearList.add(currentYear - YEAR_OFFSET + k + "");
}
for (int l = 0; l < YEAR_OFFSET; l++) {
yearList.add(currentYear + l + "");
}
ListView yearListView = new ListView(mContext);
yearListView.setVerticalScrollBarEnabled(false);
final MyAdapter yearAdapter = new MyAdapter(yearList, ITEM_HEIGHT);
yearListView.setAdapter(yearAdapter);
yearListView.setDivider(null);
LinearLayout.LayoutParams param1 = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT);
param1.weight = 1;
ll_main.addView(yearListView, param1);
yearListView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
//when stop scrolling
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
refreshDayList();
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mCurrentFocusingViewInYearList = setFocusingViewStyle(firstVisibleItem, visibleItemCount, yearAdapter, mCurrentFocusingViewInYearList);
}
});
if (null != mDefaultDate && i < mDefaultDate.length && !TextUtils.isEmpty(mDefaultDate[i])) {
int defaultValue = 0;
try {
defaultValue = Integer.parseInt(mDefaultDate[i]) - currentYear + YEAR_OFFSET;
} catch (Exception e) {
e.printStackTrace();
}
yearListView.setSelection(defaultValue - ITEM_COUNT / 2);
} else {
//current day as default position
yearListView.setSelection(YEAR_OFFSET - ITEM_COUNT / 2);
}
} else if (mContentType[i] == ContentType.DAY) {
//DAY
mDayListView = new ListView(mContext);
mDayListData = new ArrayList<>();
mDayListAdapter = new MyAdapter(mDayListData, ITEM_HEIGHT);
mDayListView.setAdapter(mDayListAdapter);
mDayListView.setDivider(null);
mDayListView.setVerticalScrollBarEnabled(false);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, FrameLayout.LayoutParams.MATCH_PARENT);
params.weight = 1;
ll_main.addView(mDayListView, params);
mDayListView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mCurrentFocusingViewInDayList = setFocusingViewStyle(firstVisibleItem, visibleItemCount, mDayListAdapter, mCurrentFocusingViewInDayList);
}
});
}
}
//default set day list
if (null != mDayListData && null != mDayListAdapter) {
refreshDayList();
boolean setDefaultDay;
for (int i = 0; null != mContentType && i < mContentType.length; i++) {
if (ContentType.DAY == mContentType[i]) {
setDefaultDay = true;
int position = 0;
if (setDefaultDay && null != mDefaultDate && !TextUtils.isEmpty(mDefaultDate[i])) {
try {
position = Integer.parseInt(mDefaultDate[i]) - 1;
} catch (Exception e) {
e.printStackTrace();
}
} else {
//current day as default position
position = mCurrentCalendar.get(Calendar.DAY_OF_MONTH) - ITEM_COUNT / 2 + 1;
}
if (position < 0) {
position = 0;
}
if (position > mDayListData.size()) {
position = mDayListData.size() - 1;
}
mDayListView.setSelection(position);
break;
}
}
}
contentPar.addView(ll_main);
contentPar.addView(new PickerHintView(mContext, ITEM_HEIGHT));
RelativeLayout titleRl = new RelativeLayout(mContext);
Button btn = new Button(mContext);
btn.setText("确定");
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (null != mListener) {
StringBuilder builder = new StringBuilder();
for (int i = 0; null != mContentType && i < mContentType.length; i++) {
if (mContentType[i] == ContentType.DAY) {
if (null != mCurrentFocusingViewInDayList) {
builder.append(mCurrentFocusingViewInDayList.getText());
} else {
builder.append("");
}
} else if (mContentType[i] == ContentType.MONTH) {
if (null != mCurrentFocusingViewInMonthList) {
builder.append(mCurrentFocusingViewInMonthList.getText());
} else {
builder.append("");
}
} else if (mContentType[i] == ContentType.YEAR) {
if (null != mCurrentFocusingViewInYearList) {
builder.append(mCurrentFocusingViewInYearList.getText());
} else {
builder.append("");
}
}
if (i != mContentType.length - 1) {
builder.append("-");
}
}
mListener.onConfirmClicked(builder.toString());
}
DatePickerPop.this.dismiss();
}
});
btn.setGravity(Gravity.CENTER);
RelativeLayout.LayoutParams paramBtn = new RelativeLayout.LayoutParams(150, RelativeLayout.LayoutParams.MATCH_PARENT);
btn.setBackgroundColor(BACKGRO_COLOR_BTN);
paramBtn.addRule(RelativeLayout.ALIGN_PARENT_END);
btn.setGravity(Gravity.CENTER);
paramBtn.setMargins(0, 0, 20, 0);
btn.setTextSize(15 );
titleRl.addView(btn, paramBtn);
titleRl.setBackgroundColor(BACKGRO_COLOR_TITLE);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0);
layoutParams.weight = 1;
view.addView(titleRl, layoutParams);
LinearLayout.LayoutParams layoutParams1 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0);
layoutParams1.weight = (float) (ITEM_COUNT - 0.1);
view.addView(contentPar, layoutParams1);
setContentView(view);
setBackgroundDrawable(null);
}
@Nullable
private TextView setFocusingViewStyle(int firstVisibleItem, int visibleItemCount, MyAdapter adapter, TextView lastFocusedView) {
//加深选中区域的条目显示
int focusingIndex = firstVisibleItem + visibleItemCount / 2;
TextView focusingView = (TextView) adapter.getViewReference(focusingIndex);
if (null != focusingView) {
focusingView.setTextSize(TEXT_SIZE_FOCUSING);
focusingView.setTextColor(TEXT_COLOR_FOCUSING);
}
//revert the style of last focused item
if (null != lastFocusedView && lastFocusedView != focusingView) {
lastFocusedView.setTextColor(TEXT_COLOT_DEFAULT);
lastFocusedView.setTextSize(TEXT_SIZE_DEFAULT);
}
return focusingView;
}
/**
* show days according to current month and year;
*/
private void refreshDayList() {
if (mDayListData == null
|| mDayListData.size() == 1
|| mDayListData.size() == 1
|| mDayListAdapter == null) {
return;
}
mDayListData.clear();
Calendar calendar = Calendar.getInstance();
if (null != mCurrentFocusingViewInMonthList) {
String text = (String) mCurrentFocusingViewInMonthList.getText();
int value = 0;
if (!"".equals(text)) {
try {
value = Integer.parseInt(text) - 1;
} catch (Exception e) {
e.printStackTrace();
}
}
calendar.set(calendar.MONTH, value);
}
if (null != mCurrentFocusingViewInYearList) {
String text = (String) mCurrentFocusingViewInYearList.getText();
int value = 0;
if (!"".equals(text)) {
try {
value = Integer.parseInt(text);
} catch (Exception e) {
e.printStackTrace();
}
}
calendar.set(Calendar.YEAR, value);
}
int maximum = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
//前后加空item,以便可以选到所有的item
for (int i = 0; i < ITEM_COUNT / 2; i++) {
mDayListData.add("");
}
for (int i = 1; i <= maximum; i++) {
mDayListData.add(i + "");
}
for (int i = 0; i < ITEM_COUNT / 2; i++) {
mDayListData.add("");
}
mDayListAdapter.notifyDataSetChanged();
}
static class MyAdapter extends BaseAdapter {
private int mItemHeight;
ArrayList mDatas;
HashMap mViewMap = new HashMap<>();
public MyAdapter(ArrayList mDatas, int itemHeight) {
this.mDatas = mDatas;
this.mItemHeight = itemHeight;
}
@Override
public int getCount() {
return mDatas.size();
}
@Override
public String getItem(int position) {
return mDatas.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = new TextView(parent.getContext());
convertView.setTag(convertView);
}
TextView tv = (TextView) convertView.getTag();
tv.setText(mDatas.get(position));
tv.setGravity(Gravity.CENTER);
tv.setMinHeight(mItemHeight);
tv.setBackgroundColor(Color.WHITE);
tv.setTextColor(TEXT_COLOT_DEFAULT);
tv.setTextSize(TEXT_SIZE_DEFAULT);
mViewMap.put(position, tv);
return convertView;
}
View getViewReference(int position) {
return mViewMap.get(position);
}
}
//两条横线作为选中提示框
static class PickerHintView extends View {
public static final int LINE_PADDING = 25;
private int mItemHeight;
private int mWidth;
private int mHeight;
public PickerHintView(Context context, int itemHeight) {
super(context);
setBackgroundColor(Color.TRANSPARENT);
mItemHeight = itemHeight;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//center y
int line1Y = mHeight / 2 - mItemHeight / 2;
int line2Y = line1Y + mItemHeight;
Paint paint = new Paint();
paint.setColor(Color.GRAY);
paint.setStrokeWidth(3);
if (mContentType.length == 2) {
//left line1
int endXLeft = mWidth / 2 - LINE_PADDING;
canvas.drawLine(LINE_PADDING, line1Y, endXLeft, line1Y, paint);
//left line2
canvas.drawLine(LINE_PADDING, line2Y, endXLeft, line2Y, paint);
//right line1
canvas.drawLine(endXLeft + LINE_PADDING, line1Y, mWidth - LINE_PADDING, line1Y, paint);
//right line2
canvas.drawLine(endXLeft + LINE_PADDING, line2Y, mWidth - LINE_PADDING, line2Y, paint);
} else if (mContentType.length == 3) {
//left line1
int stopXLine1 = mWidth / 3 - LINE_PADDING;
canvas.drawLine(LINE_PADDING, line1Y, stopXLine1, line1Y, paint);
//left line2
canvas.drawLine(LINE_PADDING, line2Y, stopXLine1, line2Y, paint);
//middle line1
canvas.drawLine(stopXLine1 + 2 * LINE_PADDING, line1Y, stopXLine1 + LINE_PADDING + mWidth / 3, line1Y, paint);
//middle lin2
canvas.drawLine(stopXLine1 + 2 * LINE_PADDING, line2Y, stopXLine1 + LINE_PADDING + mWidth / 3, line2Y, paint);
//right line1
canvas.drawLine(mWidth / 3 * 2 + LINE_PADDING, line1Y, mWidth - LINE_PADDING, line1Y, paint);
//right line2
canvas.drawLine(mWidth / 3 * 2 + LINE_PADDING, line2Y, mWidth - LINE_PADDING, line2Y, paint);
} else if (mContentType.length == 1) {
//line1
canvas.drawLine(LINE_PADDING, line1Y, mWidth - LINE_PADDING, line1Y, paint);
//line2
canvas.drawLine(LINE_PADDING, line2Y, mWidth - LINE_PADDING, line2Y, paint);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
}
}
public interface onConfirmListener {
/**
* 按照传入的contentType 返回字符串 用-分隔!
* @param string
*/
void onConfirmClicked(String string);
}
}