Android 自定义日历控件
2018-05-10 本文已影响0人
木小伍
废话不多说,先直接上最终的效果图(完整代码链接在底部)
日历.png实现思路
首先,我们将效果图实现的过程,拆分:
1,将整个布局进行拆分,分别实现
2,用系统calendar类实现数据填充
3,附属数据的填充(比如日消费什么的)
第一步,界面铺设
瞄一眼,就很容易得出该日历由三个部分组成,
a.月份标题
b.星期数
c.日历数据
以下是我的实现布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp">
<ImageView
android:id="@+id/btn_calendar_pre"
android:layout_width="60dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:src="@mipmap/arrow_pre" />
<TextView
android:id="@+id/tv_calendar_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="5月18"
android:textColor="#000000"
android:textSize="20sp" />
<ImageView
android:id="@+id/btn_calendar_next"
android:layout_width="60dp"
android:layout_height="30dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:src="@mipmap/arrow_next" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_weight="1"
android:gravity="center"
android:text="日"
android:textColor="#000"
android:textSize="16sp" />
//..........
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recy_calendar"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
效果如下,主要功能在日历数据的展示上面,我采用的是recycleview布局
布局.png
第二步,日历数据的填充
private void initData(Context context) {
dateList.clear();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy - MM");
//年月--设置标题栏的数据
String dateTitle = sdf.format(mCalendar.getTime());
tvDateTitle.setText(dateTitle);
//表格中的数据
Calendar calendar = (Calendar) mCalendar.clone();
calendar.set(Calendar.DAY_OF_MONTH, 1); //设置时间到当前月份的第一天
//1---代表周日 2---代表周一
int firstDay = calendar.get(Calendar.DAY_OF_WEEK);//获取日期的偏移量
int preDays = firstDay - 1; //因为角标是从0开始的
//仅仅美观操作,下面代码可加可不加 效果参见pc系统的日历月份调至 2018-4
preDays = preDays == 0 ? 7 : preDays; //为了保证第一行一定是 上个月+这个月(可能没有) 的数据 ,
//最后一行一定是 这个月(可能没有)+下个月 的数据
calendar.add(Calendar.DAY_OF_MONTH, -preDays);//将偏移量移至上个月,把上个月的几天添加到本月的日历中
int maxDays = 6 * 7;//直接写死,6行7列,至于为什么,请参考pc右下角日历
ClendarInfo clendarInfo;
Date time = null;
sdf = new SimpleDateFormat("yyyy-MM-dd");
boolean needDoThing = dataMap.size() > 0; //表示当月有数据
for (int i = 0; i < maxDays; i++) {
time = calendar.getTime();
clendarInfo = new ClendarInfo();
//-------第三步----------添加附属数据开始----------------------------
String key = sdf.format(new Date(time.getYear(), time.getMonth() + 1, time.getDate()));
if (needDoThing && dataMap.containsKey(key)) { //将本月需要做的事添加到集合中,而且的当前月份
String thing = dataMap.get(key);
if (!TextUtils.isEmpty(thing))
clendarInfo.setDoThing(thing);
}
//-------第三步----------添加附属数据结束----------------------------
clendarInfo.setDate(time);
dateList.add(clendarInfo);
calendar.add(Calendar.DAY_OF_MONTH, 1);
}
// Log.i("TAG", "dataMap=" + dataMap.toString());
//将日历数据填充到recycleview中
if (adapter == null) {
adapter = new DateAdapter(context, R.layout.item_calendar_layout, dateList);
adapter.setOnItemClickListener(this);
recyclerView.setAdapter(adapter);
} else {
adapter.notifyDataSetChanged();
}
}
第二步中,主要的要注意的地方是:
a.星期天的角标是0,
b.一月份的角标是0
c.Calendar.add(Calendar.DAY_OF_MONTH, 1);可以理解为在月份中,参数 1 表示日期往后挪一天,-1 表示往前面挪一天。
Calendar.add(Calendar.MONTH, +1); //当前月加1,即下个月
Calendar.add(Calendar.MONTH, -1);//当前月减1,即上个月
以上方法就是生成日历数据的核心代码了。代码的注释也写的很详细,如果还不清楚,可在文末将完整代码复制下来,或者在文本末,下载该博客的demo
第三步,添加附属数据
实现的主要方法,就是步骤二中的代码中注释掉的部分了。
至于,不是当前月的日期显示灰色,当天日期标红,当前月黑色显示,就是在recycleview的adapter中实现了,这些都很简单,就写出来了。
Map<String, String> dataMap = new HashMap<>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
dataMap.put(sdf.format(new Date(2018-1900, 5, 1)), "吃饭");
dataMap.put(sdf.format(new Date(2018-1900, 5, 11)), "睡觉");
dataMap.put(sdf.format(new Date(2018-1900, 5, 16)), "打豆豆");
dataMap.put(sdf.format(new Date(2018-1900, 5, 21)), "继续睡觉");
dataMap.put(sdf.format(new Date(2018-1900, 5, 9)), "来啊,继续浪");
dataMap.put(sdf.format(new Date(2018-1900, 5, 30)), "打豆豆");
clendarView.setDataMap(dataMap);
上面这段代码是从activity中传入的附属数据,具体情况,要按照需求来决定,仅仅作为参考。最后附上demo地址