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

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/u011102153/article/details/72765941

周日历

地址: 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 刷新界面

猜你喜欢

转载自blog.csdn.net/u011102153/article/details/72765941