按周显示的日历和按月显示的日历,你需要吗

来源:互联网 发布:谢娜主持的网络综艺 编辑:程序博客网 时间:2024/06/05 02:43

周日历

地址: https://github.com/LineChen/Week_Calendar

这里写图片描述

实现

WeekCalendar是一个继承LinearLayout的ViewGroup,而不是一个View,显示方式完全由使用者控制,类似ListView使用。

关于布局: WeekCalendar是继承自LinearLayout,内部主要有两个直接子控件,星期Layout和日期Layout。星期Layout是一个GridView,日期Layout是一个固定高度的ViewPager。为了让使用者有可以无限滑动的感觉,这里的ViewPager需要做成“无限”的。每一个周布局是一个GridView。

这里写图片描述

无限ViewPager:

并不是真的无限。实现是在PagerAdapter的getCount()中返回一个较大的数,这里返回的是1000,然后调用ViewPager的setCurrentItem(int)设置为最大值的一半。当然仅仅这样还是不够的。 Jackwharton借鉴了一下ListView的缓存模式,并使用在ViewPager中,所以再添加一个缓存基本就完成了。

缓存中最重要的一个类是RecycleBin,是直接从官方代码中拿出来的。RecycleBin的官方解释是:RecycleBin有助于在布局之间重用视图。 RecycleBin有两个级别的存储:ActiveViews和ScrapViews。 ActiveView是在布局开始时在屏幕上的那些视图。 通过构造行数,他们正在显示当前的信息。 在布局结束时,ActiveView中的所有视图都将降级为ScrapViews。 ScrapViews是可能被适配器使用的旧视图,以避免不必要地分配视图。

结合PagerAdapter就可以让ViewPager使用缓存了:

重写一个RecyclingPagerAdapter继承PagerAdapter,在instantiateItem和destroyItem使用缓存,详细信息请看源码

@Override    public final Object instantiateItem(ViewGroup container, int position) {        int viewType = getItemViewType(position);        View view = null;        if (viewType != IGNORE_ITEM_VIEW_TYPE) {            //先从缓存中获取视图            view = recycleBin.getScrapView(position, viewType);        }        view = getView(position, view, container);        container.addView(view);        return view;    }
@Override    public final void destroyItem(ViewGroup container, int position, Object object) {        View view = (View) object;        container.removeView(view);        int viewType = getItemViewType(position);        if (viewType != IGNORE_ITEM_VIEW_TYPE) {            //在这里将视图缓存            recycleBin.addScrapView(view, position, viewType);        }    }

使用缓存后,只有在开始的时候创建了三个新的视图。
这里写图片描述

接下来就是显示了~

为了让使用者控制显示布局,定义了一个接口。

public interface GetViewHelper {    View getDayView(int position, View convertView, ViewGroup parent, DateTime dateTime, boolean select);    View getWeekView(int position, View convertView, ViewGroup parent, String week);}

显示星期:

还记得前面说的显示星期的是个GridView,那么在设置Adapter的时候这样写就可以了:

@Override    public View getView(int position, View convertView, ViewGroup parent) {        return getViewHelper.getWeekView(position, convertView, parent, weeks.get(position));    }

显示日期:

日期显示稍微复杂一点,最关键的是重写ViewPager的Adapter。

因为有关时间的处理使用的是jodatime,在初始化PagerAdapter的时候会传今天所在这一周的第一天(从周日开始显示),每滑动一页就可以根据这个时间减去或加上一周,jodatime中刚好有相应的api,非常方便。

关键代码:

@Override    public View getView(int position, View convertView, ViewGroup container) {        WeekViewHolder viewHolder;        if(convertView == null){            convertView = LayoutInflater.from(context).inflate(R.layout.item_calendar, container, false);            viewHolder = new WeekViewHolder(convertView);            convertView.setTag(viewHolder);        } else {            viewHolder = (WeekViewHolder) convertView.getTag();        }        int intervalWeeks = position - centerPosition;        DateTime start = startDateTime.plusWeeks(intervalWeeks);        final DayAdapter dayAdapter = new DayAdapter(start, getViewHelper, selectDateTime);        viewHolder.weekGrid.setAdapter(dayAdapter);        viewHolder.weekGrid.setOnItemClickListener(new AdapterView.OnItemClickListener() {            @Override            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {                selectDateTime = dayAdapter.getItem(position);                dayAdapter.setSelectDateTime(selectDateTime);                notifyDataSetChanged();                if(dateSelectListener != null){                    dateSelectListener.onDateSelect(selectDateTime);                }            }        });        return convertView;    }

根据position,可以计算出显示的这一周的第一天日期。
int intervalWeeks = position - centerPosition;
DateTime start = startDateTime.plusWeeks(intervalWeeks);

显示一周日期:

初始化一周的日期,

    public DayAdapter(DateTime startDateTime, GetViewHelper getViewHelper, DateTime selectDateTime) {        this.getViewHelper = getViewHelper;        this.selectDateTime = selectDateTime;        dateTimes = new ArrayList<>();        for (int i = 0; i < DAYS_OF_WEEK; i++) {            dateTimes.add(new DateTime(startDateTime).plusDays(i));        }    }

日期显示通过接口返回给使用者处理。

@Override    public View getView(int position, View convertView, ViewGroup parent) {        return getViewHelper.getDayView(position, convertView, parent, dateTimes.get(position), CalendarUtil.isSameDay(dateTimes.get(position), selectDateTime));    }

添加一些公有方法:

  • setSelectDateTime(DateTime dateTime) 设置选中日期

在PagerAdapter中有一个成员变量是selectDateTime(默认是今天被选中),在显示一周日期的时候会把selectDateTime作为构造参数传递给DayAdapter,在DayAdapter的getView方法中国会判断显示的这一天是不是选中日期。

@Override    public View getView(int position, View convertView, ViewGroup parent) {        return getViewHelper.getDayView(position, convertView, parent, dateTimes.get(position), CalendarUtil.isSameDay(dateTimes.get(position), selectDateTime));    }

需要注意的是:设置选择日期后要调用PagerAdapter的notifyDataSetChanged()方法,要想这个方法起作用,需要重写PagerAdapter的getItemPosition方法。

    @Override    public int getItemPosition(Object object) {        return POSITION_NONE;    }
  • gotoDate(DateTime dateTime) 跳转到指定日期

相当于重新初始化一遍,只是显示的时间为跳转日期的那一周。

    public void gotoDate(DateTime dateTime){        viewPagerContent.setCurrentItem(centerPosition, true);        calendarPagerAdapter.setStartDateTime(dateTime.minusDays(dateTime.getDayOfWeek()));        onWeekChange(centerPosition);    }
  • getCurrentFirstDay 获取当前页面第一天

根据当前ViewPager的position和centerPosition比较,加上jodatime的api,很简单就实现了。

    public DateTime getCurrentFirstDay(){        int intervalWeeks = viewPagerContent.getCurrentItem() - centerPosition;        return calendarPagerAdapter.getStartDateTime().plusWeeks(intervalWeeks);    }
  • refresh 刷新界面

这个方法是很有必要的,因为你可能要添加事件,而事件是从网络获取的。

    public void refresh(){        calendarPagerAdapter.notifyDataSetChanged();    }

关于监听:
这里提供了两种监听,日期选择监听,周变化监听

public interface DateSelectListener {    void onDateSelect(DateTime selectDate);}
public interface WeekChangeListener {    void onWeekChanged(DateTime firstDayOfWeek);}

实现:

  • 周变化监听

监听ViewPager页面选择监听,根据position计算出当前显示周的第一天日期。

viewPagerContent.addOnPageChangeListener(new CustomPagerChandeListender() {            @Override            public void onPageSelected(int position) {                onWeekChange(position);            }        });private void onWeekChange(int position) {        int intervalWeeks = position - centerPosition;        DateTime firstDayofWeek = calendarPagerAdapter.getStartDateTime().plusWeeks(intervalWeeks);        if(weekChangedListener != null){            weekChangedListener.onWeekChanged(firstDayofWeek);        }    }
  • 日期选择监听

日期选择监听就是监听GridView的item点击。

 viewHolder.weekGrid.setAdapter(dayAdapter);        viewHolder.weekGrid.setOnItemClickListener(new AdapterView.OnItemClickListener() {            @Override            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {                selectDateTime = dayAdapter.getItem(position);                dayAdapter.setSelectDateTime(selectDateTime);                notifyDataSetChanged();                if(dateSelectListener != null){                    dateSelectListener.onDateSelect(selectDateTime);                }            }        });

使用

布局:

    <com.beiing.weekcalendar.WeekCalendar        android:id="@+id/week_calendar"        android:layout_width="match_parent"        android:layout_height="wrap_content"        app:wc_headerBgColor="#ccc"        app:wc_headerHeight="60dp"        app:wc_calendarHeight="55dp" />

代码中:

  • 设置布局显示

必须调用setGetViewHelper方法加载布局,getDayView方法控制每一天显示,
getWeekView方法控制星期显示,使用类似ListView中BaseAdapter中的getView方法。

        weekCalendar.setGetViewHelper(new GetViewHelper() {            @Override            public View getDayView(int position, View convertView, ViewGroup parent, DateTime dateTime, boolean select) {                if(convertView == null){                    convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_day, parent, false);                }                TextView tvDay = (TextView) convertView.findViewById(R.id.tv_day);                tvDay.setText(dateTime.toString("d"));                if(CalendarUtil.isToday(dateTime) && select){                    tvDay.setTextColor(Color.WHITE);                    tvDay.setBackgroundResource(R.drawable.circular_blue);                } else if(CalendarUtil.isToday(dateTime)){                    tvDay.setTextColor(getResources().getColor(R.color.colorTodayText));                    tvDay.setBackgroundColor(Color.TRANSPARENT);                } else if(select){                    tvDay.setTextColor(Color.WHITE);                    tvDay.setBackgroundResource(R.drawable.circular_blue);                } else {                    tvDay.setTextColor(Color.BLACK);                    tvDay.setBackgroundColor(Color.TRANSPARENT);                }                ImageView ivPoint = (ImageView) convertView.findViewById(R.id.iv_point);                ivPoint.setVisibility(View.GONE);                for (DateTime d : eventDates) {                    if(CalendarUtil.isSameDay(d, dateTime)){                        ivPoint.setVisibility(View.VISIBLE);                        break;                    }                }                return convertView;            }            @Override            public View getWeekView(int position, View convertView, ViewGroup parent, String week) {                if(convertView == null){                    convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_week, parent, false);                }                TextView tvWeek = (TextView) convertView.findViewById(R.id.tv_week);                tvWeek.setText(week);                if(position == 0 || position == 6){                    tvWeek.setTextColor(getResources().getColor(R.color.colorAccent));                }                return convertView;            }        });
  • 设置日期选择监听
 weekCalendar.setDateSelectListener(new DateSelectListener() {            @Override            public void onDateSelect(DateTime selectDate) {                String text = "你选择的日期是:" + selectDate.toString("yyyy-MM-dd");                tvSelectDate.setText(text);            }        });
  • 设置周变化监听
weekCalendar.setWeekChangedListener(new WeekChangeListener() {            @Override            public void onWeekChanged(DateTime firstDayOfWeek) {                String text = "本周第一天:" + firstDayOfWeek.toString("yyyy年M月d日")                        + ",本周最后一天:" + new DateTime(firstDayOfWeek).plusDays(6).toString("yyyy年M月d日");                tvWeekChange.setText(text);            }        });

其他方法

  • getSelectDateTime 获取当前选中日期

  • setSelectDateTime(DateTime dateTime) 设置选中日期

  • gotoDate(DateTime dateTime) 跳转到指定日期

  • getCurrentFirstDay 获取当前页面第一天

  • refresh 刷新界面


月日历

地址: https://github.com/LineChen/Month_Calendar

这里写图片描述

设计与周日历一样,不同的是在日期显示中,一个是按周显示,一个是按月显示。实现中处理的不同主要在DayAdapter中,构造方法中要初始化这个月显示的日期。

 public DayAdapter(int calendarHeight, DateTime startDateTime, GetViewHelper getViewHelper, DateTime selectDateTime) {        this.calendarHeight = calendarHeight;        this.getViewHelper = getViewHelper;        this.selectDateTime = selectDateTime;        dateTimes = new ArrayList<>();        final int daysOfMonth = startDateTime.dayOfMonth().getMaximumValue();        int firstDayOfWeek = startDateTime.getDayOfWeek() % DAYS_OF_WEEK;        for (int i = firstDayOfWeek; i >= 1; i--) {            dateTimes.add(new Day(new DateTime(startDateTime).minusDays(i), true));        }        for (int i = 0; i < daysOfMonth; i++) {            dateTimes.add(new Day(new DateTime(startDateTime).plusDays(i), false));        }        DateTime lastDay = dateTimes.get(dateTimes.size() - 1).getDateTime();        int yy = DAYS_OF_WEEK - lastDay.getDayOfWeek() % DAYS_OF_WEEK;        for (int i = 1; i < yy; i++) {            dateTimes.add(new Day(new DateTime(lastDay).plusDays(i), true));        }    }

然后是在getView中会对视图进行高度设置,因为ViewPager高度时固定的,有些月份要显示5行,有些月份显示6行。

@Override    public View getView(int position, View convertView, ViewGroup parent) {        Day day = dateTimes.get(position);        day.setSelect(CalendarUtil.isSameDay(day.getDateTime(), selectDateTime));        View view = getViewHelper.getDayView(position, convertView, parent, day);        ViewGroup.LayoutParams params = view.getLayoutParams();        params.height = calendarHeight / (dateTimes.size() / DAYS_OF_WEEK);        return view;    }

使用

布局:

    <com.beiing.monthcalendar.MonthCalendar        android:id="@+id/month_calendar"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:background="@android:color/white"        app:mc_calendarHeight="@dimen/calender_content_height"/>

代码中:

  • 设置布局显示

必须调用setGetViewHelper方法加载布局,getDayView方法控制每一天显示,
getWeekView方法控制星期显示,使用类似ListView中BaseAdapter中的getView方法。

monthCalendar.setGetViewHelper(new GetViewHelper() {            @Override            public View getDayView(int position, View convertView, ViewGroup parent, Day day) {                if(convertView == null){                    convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_day, parent, false);                }                TextView tvDay = (TextView) convertView.findViewById(R.id.tv_day);                DateTime dateTime = day.getDateTime();                tvDay.setText(dateTime.toString("d"));                boolean select = day.isSelect();                if(CalendarUtil.isToday(dateTime) && select){                    tvDay.setTextColor(Color.WHITE);                    tvDay.setBackgroundResource(R.drawable.circular_blue);                } else if(CalendarUtil.isToday(dateTime)){                    tvDay.setTextColor(getResources().getColor(R.color.colorTodayText));                    tvDay.setBackgroundColor(Color.TRANSPARENT);                } else if(select){                    tvDay.setTextColor(Color.WHITE);                    tvDay.setBackgroundResource(R.drawable.circular_blue);                } else {                    tvDay.setBackgroundColor(Color.TRANSPARENT);                    if(day.isOtherMonth()){                        tvDay.setTextColor(Color.LTGRAY);                    } else {                        tvDay.setTextColor(Color.BLACK);                    }                }                ImageView ivPoint = (ImageView) convertView.findViewById(R.id.iv_point);                ivPoint.setVisibility(View.INVISIBLE);                for (DateTime d : eventDates) {                    if(CalendarUtil.isSameDay(d, dateTime)){                        ivPoint.setVisibility(View.VISIBLE);                        break;                    }                }                return convertView;            }            @Override            public View getWeekView(int position, View convertView, ViewGroup parent, String week) {                if(convertView == null){                    convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_week, parent, false);                }                TextView tvWeek = (TextView) convertView.findViewById(R.id.tv_week);                switch (position){                    case 0:                        week = "日";                        tvWeek.setTextColor(getResources().getColor(R.color.colorAccent));                        break;                    case 1:                        week = "一";                        break;                    case 2:                        week = "二";                        break;                    case 3:                        week = "三";                        break;                    case 4:                        week = "四";                        break;                    case 5:                        week = "五";                        break;                    case 6:                        week = "六";                        tvWeek.setTextColor(getResources().getColor(R.color.colorAccent));                        break;                }                tvWeek.setText(week);                return convertView;            }        });
  • 设置日期选择监听
        monthCalendar.setOnDateSelectListener(new OnDateSelectListener() {            @Override            public void onDateSelect(DateTime selectDate) {                tvSelectDate.setText("你选择的日期:" + selectDate.toString("yyyy-MM-dd"));            }        });
  • 设置月切换监听
monthCalendar.setOnMonthChangeListener(new OnMonthChangeListener() {            @Override            public void onMonthChanged(int currentYear, int currentMonth) {                tvMonthChange.setText(currentYear + "年" + currentMonth + "月");            }        });

其他方法

  • getSelectDateTime 获取当前选中日期

  • setSelectDateTime(DateTime dateTime) 设置选中日期

  • gotoDate(DateTime dateTime) 跳转到指定日期

  • getCurrentYear 获取当前显示年份

  • getCurrentMonth 获取当前显示月份

  • refresh 刷新界面

原创粉丝点击