效果图
activity中使用
new DateDialog.Builder(this)
// 设置日期
//.setDate("2018-12-31")
//.setDate("20181231")
//.setDate(1546263036137)
// 设置年份
//.setYear(2018)
// 设置月份
//.setMonth(2)
// 设置天数
//.setDay(20)
// 不选择天数
//.setIgnoreDay()
.setListener(new DateDialog.OnListener() {
@Override
public void onSelected(int year, int month, int day) {
tv_date.setText(year + "-" + month + "-" + day );
}
@Override
public void onCancel() {
}
})
.show();
完整代码
1、DateDialog.java
public final class DateDialog {
public static final class Builder
extends Dialog
implements PickerLayoutManager.OnPickerListener, View.OnClickListener {
private final int mStartYear;
private final RecyclerView mYearView;
private final RecyclerView mMonthView;
private final RecyclerView mDayView;
private TextView tv_sure,tv_cancel;
private final PickerLayoutManager mYearManager;
private final PickerLayoutManager mMonthManager;
private final PickerLayoutManager mDayManager;
private final PickerAdapter mYearAdapter;
private final PickerAdapter mMonthAdapter;
private final PickerAdapter mDayAdapter;
private OnListener mListener;
public Builder(Context context) {
this(context, Calendar.getInstance(Locale.CHINA).get(Calendar.YEAR) - 100);
}
public Builder(Context context, int startYear) {
this(context, startYear, Calendar.getInstance(Locale.CHINA).get(Calendar.YEAR));
}
public Builder(Context context, int startYear, int endYear) {
super(context);
mStartYear = startYear;
setContentView(R.layout.dialog_date);
mYearView = findViewById(R.id.rv_date_year);
mMonthView = findViewById(R.id.rv_date_month);
mDayView = findViewById(R.id.rv_date_day);
tv_sure = findViewById(R.id.tv_ui_confirm);
tv_cancel = findViewById(R.id.tv_ui_cancel);
tv_cancel.setOnClickListener(this);
tv_sure.setOnClickListener(this);
mYearAdapter = new PickerAdapter(context);
mMonthAdapter = new PickerAdapter(context);
mDayAdapter = new PickerAdapter(context);
// 生产年份
ArrayList<String> yearData = new ArrayList<>(10);
for (int i = mStartYear; i <= endYear; i++) {
yearData.add(i + " 年" );
}
// 生产月份
ArrayList<String> monthData = new ArrayList<>(12);
for (int i = 1; i <= 12; i++) {
monthData.add(i + " 月" );
}
Calendar calendar = Calendar.getInstance(Locale.CHINA);
int day = calendar.getActualMaximum(Calendar.DATE);
// 生产天数
ArrayList<String> dayData = new ArrayList<>(day);
for (int i = 1; i <= day; i++) {
dayData.add(i + " 日");
}
mYearAdapter.setData(yearData);
mMonthAdapter.setData(monthData);
mDayAdapter.setData(dayData);
mYearManager = new PickerLayoutManager.Builder(context)
.build();
mMonthManager = new PickerLayoutManager.Builder(context)
.build();
mDayManager = new PickerLayoutManager.Builder(context)
.build();
mYearView.setLayoutManager(mYearManager);
mMonthView.setLayoutManager(mMonthManager);
mDayView.setLayoutManager(mDayManager);
mYearView.setAdapter(mYearAdapter);
mMonthView.setAdapter(mMonthAdapter);
mDayView.setAdapter(mDayAdapter);
setYear(calendar.get(Calendar.YEAR));
setMonth(calendar.get(Calendar.MONTH) + 1);
setDay(calendar.get(Calendar.DAY_OF_MONTH));
mYearManager.setOnPickerListener(this);
mMonthManager.setOnPickerListener(this);
}
public Builder setListener(OnListener listener) {
mListener = listener;
return this;
}
/**
* 不选择天数
*/
public Builder setIgnoreDay() {
mDayView.setVisibility(View.GONE);
return this;
}
public Builder setDate(long date) {
if (date > 0) {
setDate(new SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(new Date(date)));
}
return this;
}
public Builder setDate(String date) {
if (date.matches("\\d{8}")) {
// 20190519
setYear(date.substring(0, 4));
setMonth(date.substring(4, 6));
setDay(date.substring(6, 8));
} else if (date.matches("\\d{4}-\\d{2}-\\d{2}")) {
// 2019-05-19
setYear(date.substring(0, 4));
setMonth(date.substring(5, 7));
setDay(date.substring(8, 10));
}
return this;
}
public Builder setYear(String year) {
return setYear(Integer.parseInt(year));
}
public Builder setYear(int year) {
int index = year - mStartYear;
if (index < 0) {
index = 0;
} else if (index > mYearAdapter.getItemCount() - 1) {
index = mYearAdapter.getItemCount() - 1;
}
mYearView.scrollToPosition(index);
onPicked(mYearView, index);
return this;
}
public Builder setMonth(String month) {
return setMonth(Integer.parseInt(month));
}
public Builder setMonth(int month) {
int index = month - 1;
if (index < 0) {
index = 0;
} else if (index > mMonthAdapter.getItemCount() - 1) {
index = mMonthAdapter.getItemCount() - 1;
}
mMonthView.scrollToPosition(index);
onPicked(mMonthView, index);
return this;
}
public Builder setDay(String day) {
return setDay(Integer.parseInt(day));
}
public Builder setDay(int day) {
int index = day - 1;
if (index < 0) {
index = 0;
} else if (index > mDayAdapter.getItemCount() - 1) {
index = mDayAdapter.getItemCount() - 1;
}
mDayView.scrollToPosition(index);
onPicked(mDayView, index);
return this;
}
/**
* {@link PickerLayoutManager.OnPickerListener}
*
* @param recyclerView RecyclerView 对象
* @param position 当前滚动的位置
*/
@Override
public void onPicked(RecyclerView recyclerView, int position) {
// 获取这个月最多有多少天
Calendar calendar = Calendar.getInstance(Locale.CHINA);
calendar.set(mStartYear + mYearManager.getPickedPosition(), mMonthManager.getPickedPosition(), 1);
int day = calendar.getActualMaximum(Calendar.DATE);
if (mDayAdapter.getItemCount() != day) {
ArrayList<String> dayData = new ArrayList<>(day);
for (int i = 1; i <= day; i++) {
dayData.add(i + " 日");
}
mDayAdapter.setData(dayData);
}
}
@Override
public void onClick(View view) {
int viewId = view.getId();
if (viewId == R.id.tv_ui_confirm) {
dismiss();
if (mListener != null) {
mListener.onSelected(mStartYear + mYearManager.getPickedPosition(), mMonthManager.getPickedPosition() + 1, mDayManager.getPickedPosition() + 1);
}
} else if (viewId == R.id.tv_ui_cancel) {
dismiss();
if (mListener != null) {
mListener.onCancel();
}
}
}
private static final class PickerAdapter extends RecyclerView.Adapter<PickerAdapter.ViewHolder> {
public PickerAdapter(Context context) {
}
private List<String> list = new ArrayList<>();
public void setData(List<String> list){
this.list = list;
notifyDataSetChanged();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.picker_item,null);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.mPickerView.setText(list.get(position));
}
@Override
public int getItemCount() {
return list.size();
}
final class ViewHolder extends RecyclerView.ViewHolder {
TextView mPickerView;
public ViewHolder(@NonNull View itemView) {
super(itemView);
mPickerView = (TextView) itemView.findViewById(R.id.tv_picker_name);
}
}
}
}
public interface OnListener {
/**
* 选择完日期后回调
*
* @param year 年
* @param month 月
* @param day 日
*/
void onSelected( int year, int month, int day);
/**
* 点击取消时回调
*/
default void onCancel() {}
}
}
2、dialog_date.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 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:layout_width="260dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:cardBackgroundColor="@color/white"
app:cardCornerRadius="10dp"
app:cardElevation="0px">
<LinearLayout
android:id="@+id/ll_ui_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="20dp">
<TextView
android:id="@+id/tv_ui_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textColor="#333333"
android:textSize="16sp"
android:textStyle="bold"
android:text="选择日期"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingHorizontal="10dp"
android:paddingTop="15dp"
android:paddingBottom="5dp">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_date_year"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="1.2"
tools:itemCount="3"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_date_month"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="1"
tools:itemCount="3" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_date_day"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="1"
tools:itemCount="3" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_ui_cancel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:focusable="true"
android:gravity="center"
android:paddingVertical="15dp"
android:text="取消"
android:textColor="#787676"
android:textSize="14sp" />
<View
android:id="@+id/v_ui_line"
android:layout_width="1px"
android:layout_height="match_parent"
android:background="#ECECEC" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_ui_confirm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:focusable="true"
android:gravity="center"
android:paddingVertical="15dp"
android:text="确定"
android:textColor="#007AFF"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
3、picker_item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.AppCompatTextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/tv_picker_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingVertical="10dp"
android:singleLine="true"
android:textColor="@color/black"
android:textSize="18sp"
android:textStyle="bold"
tools:text="2018年" />
4、选择器布局管理器PickerLayoutManager.java
public final class PickerLayoutManager extends LinearLayoutManager {
private final LinearSnapHelper mLinearSnapHelper;
private final int mOrientation;
private final int mMaxItem;
private final float mScale;
private final boolean mAlpha;
private RecyclerView mRecyclerView;
private OnPickerListener mListener;
private PickerLayoutManager(Context context, int orientation, boolean reverseLayout, int maxItem, float scale, boolean alpha) {
super(context, orientation, reverseLayout);
mLinearSnapHelper = new LinearSnapHelper();
mMaxItem = maxItem;
mOrientation = orientation;
mAlpha = alpha;
mScale = scale;
}
@Override
public void onAttachedToWindow(RecyclerView recyclerView) {
super.onAttachedToWindow(recyclerView);
mRecyclerView = recyclerView;
// 设置子控件的边界可以超过父布局的范围
mRecyclerView.setClipToPadding(false);
// 添加 LinearSnapHelper
mLinearSnapHelper.attachToRecyclerView(mRecyclerView);
}
@Override
public void onDetachedFromWindow(RecyclerView recyclerView, RecyclerView.Recycler recycler) {
super.onDetachedFromWindow(recyclerView, recycler);
mRecyclerView = null;
}
@Override
public boolean isAutoMeasureEnabled() {
return mMaxItem == 0;
}
@Override
public void onMeasure(@NonNull RecyclerView.Recycler recycler, @NonNull RecyclerView.State state, int widthSpec, int heightSpec) {
int width = RecyclerView.LayoutManager.chooseSize(widthSpec,
getPaddingLeft() + getPaddingRight(),
ViewCompat.getMinimumWidth(mRecyclerView));
int height = RecyclerView.LayoutManager.chooseSize(heightSpec,
getPaddingTop() + getPaddingBottom(),
ViewCompat.getMinimumHeight(mRecyclerView));
if (state.getItemCount() != 0 && mMaxItem != 0) {
View itemView = recycler.getViewForPosition(0);
measureChildWithMargins(itemView, widthSpec, heightSpec);
if (mOrientation == HORIZONTAL) {
int measuredWidth = itemView.getMeasuredWidth();
int paddingHorizontal = (mMaxItem - 1) / 2 * measuredWidth;
mRecyclerView.setPadding(paddingHorizontal,0, paddingHorizontal,0);
width = measuredWidth * mMaxItem;
} else if (mOrientation == VERTICAL) {
int measuredHeight = itemView.getMeasuredHeight();
int paddingVertical = (mMaxItem - 1) / 2 * measuredHeight;
mRecyclerView.setPadding(0, paddingVertical,0, paddingVertical);
height = measuredHeight * mMaxItem;
}
}
setMeasuredDimension(width, height);
}
@Override
public void onScrollStateChanged(int state) {
super.onScrollStateChanged(state);
// 当 RecyclerView 停止滚动时
if (state == RecyclerView.SCROLL_STATE_IDLE) {
if (mListener != null) {
mListener.onPicked(mRecyclerView, getPickedPosition());
}
}
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
super.onLayoutChildren(recycler, state);
if (getItemCount() < 0 || state.isPreLayout()) {
return;
}
if (mOrientation == HORIZONTAL) {
scaleHorizontalChildView();
} else if (mOrientation == VERTICAL) {
scaleVerticalChildView();
}
}
@Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
scaleHorizontalChildView();
return super.scrollHorizontallyBy(dx, recycler, state);
}
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
scaleVerticalChildView();
return super.scrollVerticallyBy(dy, recycler, state);
}
/**
* 横向情况下的缩放
*/
private void scaleHorizontalChildView() {
float mid = getWidth() / 2.0f;
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
if (childView != null) {
float childMid = (getDecoratedLeft(childView) + getDecoratedRight(childView)) / 2.0f;
float scale = 1.0f + (-1 * (1 - mScale)) * (Math.min(mid, Math.abs(mid - childMid))) / mid;
childView.setScaleX(scale);
childView.setScaleY(scale);
if (mAlpha) {
childView.setAlpha(scale);
}
}
}
}
/**
* 竖向方向上的缩放
*/
private void scaleVerticalChildView() {
float mid = getHeight() / 2.0f;
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
if (childView != null) {
float childMid = (getDecoratedTop(childView) + getDecoratedBottom(childView)) / 2.0f;
float scale = 1.0f + (-1 * (1 - mScale)) * (Math.min(mid, Math.abs(mid - childMid))) / mid;
childView.setScaleX(scale);
childView.setScaleY(scale);
if (mAlpha) {
childView.setAlpha(scale);
}
}
}
}
/**
* 获取选中的位置
*/
public int getPickedPosition() {
View itemView = mLinearSnapHelper.findSnapView(this);
if(itemView != null) {
return getPosition(itemView);
}
return 0;
}
/**
* 设置监听器
*/
public void setOnPickerListener(OnPickerListener listener) {
mListener = listener;
}
public interface OnPickerListener {
/**
* 滚动停止时触发的监听
*
* @param recyclerView RecyclerView 对象
* @param position 当前滚动的位置
*/
void onPicked(RecyclerView recyclerView, int position);
}
public static final class Builder {
private final Context mContext;
private int mOrientation = VERTICAL;
private boolean mReverseLayout;
private OnPickerListener mListener;
private int mMaxItem = 3;
private float mScale = 0.6f;
private boolean mAlpha = true;
public Builder(Context context) {
mContext = context;
}
/**
* 设置布局摆放器方向
*/
public Builder setOrientation(@RecyclerView.Orientation int orientation) {
mOrientation = orientation;
return this;
}
/**
* 设置是否反向显示
*/
public Builder setReverseLayout(boolean reverseLayout) {
mReverseLayout = reverseLayout;
return this;
}
/**
* 设置最大显示条目数
*/
public Builder setMaxItem(int maxItem) {
mMaxItem = maxItem;
return this;
}
/**
* 设置缩放比例
*/
public Builder setScale(float scale) {
mScale = scale;
return this;
}
/**
* 设置透明开关
*/
public Builder setAlpha(boolean alpha) {
mAlpha = alpha;
return this;
}
public Builder setOnPickerListener(OnPickerListener listener) {
mListener = listener;
return this;
}
/**
* 构建布局管理器
*/
public PickerLayoutManager build() {
PickerLayoutManager layoutManager = new PickerLayoutManager(mContext, mOrientation, mReverseLayout, mMaxItem, mScale, mAlpha);
if (mListener != null) {
layoutManager.setOnPickerListener(mListener);
}
return layoutManager;
}
/**
* 应用到 RecyclerView
*/
public void into(RecyclerView recyclerView) {
recyclerView.setLayoutManager(build());
}
}