时间选择器

一个自定义的时间选择器,逻辑并不复杂,涉及到两个RecyclerView的交互还有Calendar时间类的使用,今天从项目中剥离出来,供大家参考指正。

效果如图:

效果如上图所示,大致实现方式就是上下两个RecyclerView分别实现日期和时间的选择,然后以底部弹窗的方式展示出来,废话不多说,上代码。

/**
 * Created by YHang on 18/5/28.
 */

public class TimeBottomDialog extends Dialog implements View.OnClickListener, DataAdapter.OnItemClickListener, TimeAdapter.OnTimeClickListener {
    private ImageView close;
    private Context mContext;
    private RecyclerView dateList, timeList;
    private DataAdapter mDataAdapter;
    private TimeAdapter mTimeAdapter;

    public void setMyTime(OnUpdateMyTime myTime) {
        this.myTime = myTime;
    }

    private OnUpdateMyTime myTime;

    public TimeBottomDialog(Context context) {
        //重写dialog默认的主题
        this(context, R.style.bottom_dialog);
        this.mContext = context;
    }

    public TimeBottomDialog(Context context, int themeResId) {
        super(context, themeResId);
        this.mContext = context;
        View convertView = getLayoutInflater().inflate(R.layout.time_bottom_dialog, null);
        close = (ImageView) convertView.findViewById(R.id.iv_close);
        dateList = convertView.findViewById(R.id.date_list);
        timeList = convertView.findViewById(R.id.time_list);
        close.setOnClickListener(this);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(convertView);
        setCanceledOnTouchOutside(false);

        LinearLayoutManager ms = new LinearLayoutManager(mContext);
        ms.setOrientation(LinearLayoutManager.HORIZONTAL);
        dateList.setLayoutManager(ms);
        dateList.addItemDecoration(new SpacesItemDecoration(30));
        mDataAdapter = new DataAdapter(mContext);
        mDataAdapter.setItemClickListener(this);
        dateList.setAdapter(mDataAdapter);

        GridLayoutManager gm = new GridLayoutManager(mContext, 4);
        gm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int position) {
                return mTimeAdapter.getItemViewType(position);
            }
        });
        timeList.setLayoutManager(gm);
        mTimeAdapter = new TimeAdapter(mContext);
        mTimeAdapter.setTimeClickListener(this);
        timeList.setAdapter(mTimeAdapter);
    }


    @Override
    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        getWindow().setGravity(Gravity.BOTTOM);
        setCanceledOnTouchOutside(true);
        DisplayMetrics dm = new DisplayMetrics();
        getWindow().getWindowManager().getDefaultDisplay().getMetrics(dm);
        WindowManager.LayoutParams p = getWindow().getAttributes();
        p.width = dm.widthPixels;
        getWindow().setAttributes(p);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.iv_close:
                dismiss();
                break;
        }
    }

    @Override
    public void onItemClick(int position) {
        mDataAdapter.setSelectedPos(position);
        if (mTimeAdapter != null) {
            mTimeAdapter.setSelectDateMillis(mDataAdapter.getSelectedMillis());
        }
    }

    @Override
    public void onTimeClick(int position) {
        mTimeAdapter.setSelectPosition(position);
        String selectedDate = mDataAdapter.getSelectedDate();
        String selectTime = mTimeAdapter.getSelectTime();

        Calendar c = Calendar.getInstance();
        c.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
        String year = String.valueOf(c.get(Calendar.YEAR));
        StringBuilder builder = new StringBuilder();
        builder.append(year)
                .append("-")
                .append(selectedDate)
                .append(" ")
                .append(selectTime);
        if (myTime != null) {
            myTime.updateMyTime(builder);
        }
        dismiss();
    }

    public interface OnUpdateMyTime {
        void updateMyTime(StringBuilder builder);
    }

    class SpacesItemDecoration extends RecyclerView.ItemDecoration {
        private int space;

        public SpacesItemDecoration(int space) {
            this.space = space;
        }

        @Override
        public void getItemOffsets(Rect outRect, View child,
                                   RecyclerView parent, RecyclerView.State state) {
            outRect.right = space;
        }
    }

}

 

这是toast主体类的内容,其中用来获取时间回调的:

1.日期回调:

    @Override
    public void onItemClick(int position) {
        mDataAdapter.setSelectedPos(position);
        if (mTimeAdapter != null) {
            mTimeAdapter.setSelectDateMillis(mDataAdapter.getSelectedMillis());
        }
    }

2.时间回调

  @Override
    public void onTimeClick(int position) {
        mTimeAdapter.setSelectPosition(position);
        String selectedDate = mDataAdapter.getSelectedDate();
        String selectTime = mTimeAdapter.getSelectTime();

        Calendar c = Calendar.getInstance();
        c.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
        String year = String.valueOf(c.get(Calendar.YEAR));
        StringBuilder builder = new StringBuilder();
        builder.append(year)
                .append("-")
                .append(selectedDate)
                .append(" ")
                .append(selectTime);
        if (myTime != null) {
            myTime.updateMyTime(builder);
        }
        dismiss();
    }

通过以上的两个回调来最终获取数据,这里我在日期回调中使用

   if (mTimeAdapter != null) {
            mTimeAdapter.setSelectDateMillis(mDataAdapter.getSelectedMillis());
        }

时间选择Adapter的setSelectDateMillis方法实现时间和日期的同步。

然后就是两个RecyclerView的Adapter了,其中DataAdapter相对简单就不贴代码了,有兴趣可以到我的Github中查看源码,这里贴一下TimeAdapter的代码:

/**
 * Created by YHang on 18/5/28.
 */
public class TimeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    public static final long LENGTH_OF_HALF_HOUR_IN_MILLISECONDS = 1800000L;
    private Context context;
    private List<String> times = new ArrayList<>();
    private List<Long> timesMillis = new ArrayList<>();
    private long dateMillis;
    private int selectPosition = 0;

    private OnTimeClickListener mItemClickListener;

    public interface OnTimeClickListener {
        void onTimeClick(int position);
    }

    public void setTimeClickListener(OnTimeClickListener itemClickListener) {
        mItemClickListener = itemClickListener;
    }

    public TimeAdapter(Context context) {
        this.context = context;
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
        long time = dateToMilliseconds("08:30", sdf);
        for (int i = 0; i < 19; i++) {
            timesMillis.add(time);
            String timeStr = sdf.format(time);
            time += LENGTH_OF_HALF_HOUR_IN_MILLISECONDS;
            times.add(timeStr);
        }
    }

    public void setSelectDateMillis(long dateMillis) {
        this.dateMillis = dateMillis;
        notifyDataSetChanged();
    }

    public void setSelectPosition(int position) {
        selectPosition = position;
        notifyDataSetChanged();
    }

    public String getSelectTime() {
        return times.get(selectPosition);
    }

    /**
     * 获取指定时间的毫秒
     *
     * @param timeNow
     * @param sdf
     * @return
     */
    public long dateToMilliseconds(String timeNow, SimpleDateFormat sdf) {
        long time = 0L;
        try {
            Date date = sdf.parse(timeNow);
            time = date.getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return time;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        switch (viewType) {
            case 4:
                return new TitleViewHolder(LayoutInflater.from(context).inflate(R.layout.time_list_item_title, parent, false));
            default:
                return new ViewHolder(LayoutInflater.from(context).inflate(R.layout.time_list_item, parent, false));
        }
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof ViewHolder) {
            ViewHolder mHolder = (ViewHolder) holder;
            if (position > 0) position--;
            if (position > 8) position--;
            mHolder.mTvTime.setText(times.get(position));
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm");
            String format = simpleDateFormat.format(new Date());
            long time = dateToMilliseconds(format, simpleDateFormat);
            if (dateMillis <= System.currentTimeMillis()) {
                if (timesMillis.get(position) < time) {
                    mHolder.mTvTime.setEnabled(false);
                } else {
                    mHolder.mTvTime.setEnabled(true);
                }
            } else {
                mHolder.mTvTime.setEnabled(true);
            }
            final int finalPosition = position;
            mHolder.mTvTime.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mItemClickListener != null) {
                        mItemClickListener.onTimeClick(finalPosition);
                        selectPosition = finalPosition;
                    }
                }
            });
        } else if (holder instanceof TitleViewHolder) {
            TitleViewHolder mHolder = (TitleViewHolder) holder;
            String time = null;
            if (position == 0) {
                time = "上午";
            } else if (position == 9) {
                time = "下午";
            }
            mHolder.mTvTimeTitle.setText(time);
        }
    }

    @Override
    public int getItemCount() {
        return 21;
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        TextView mTvTime;

        ViewHolder(View view) {
            super(view);
            this.mTvTime = view.findViewById(R.id.tv_time);
        }
    }

    class TitleViewHolder extends RecyclerView.ViewHolder {
        TextView mTvTimeTitle;

        TitleViewHolder(View view) {
            super(view);
            this.mTvTimeTitle = view.findViewById(R.id.tv_time_title);
        }
    }

    @Override
    public int getItemViewType(int position) {
        switch (position) {
            case 0:
            case 9:
                return 4;
            default:
                return 1;
        }
    }
}

这里用到了RecyclerView的getItemViewType方法为指定的position分配不同的布局类型,以便在onCreateViewHolder根据不同的viewType对布局进行分类加载。最后需要注意一点

 @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof ViewHolder) {
            ViewHolder mHolder = (ViewHolder) holder;
            if (position > 0) position--;
            if (position > 8) position--;
            .
            .
            .

这里虽然是两种布局,但是不管是什么布局他们的position仍然是按照顺序来排的,所以在开头 我们在固定的position上使用position--来重排item的position顺序,最后依旧通过自定义回调把数据传输回去。

好了,这个时间选择器大概就是这样了,项目做的急,还没来的急优化,有兴趣的同学可以做一下优化,或者自己的定制。最后,欢迎大家指正。

奉上项目地址:

项目地址

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值