Android实现简单日历
Android实现简单打卡日历
- 日历
- 需求
- 解决方法
- 效果图
- 代码实现
- activity_main布局
- 布局效果图
- MainActivity
- ViewPager2的适配器
- CalenderView(主要是Grid View)
- 布局
- 逻辑
- DateBean
- GridView高度测量
- 完整代码
- GridView的Item的适配器
- Item布局
- GitHub地址
日历
主要通过ViewPager2+GridView实现打卡日历
需求
服务器记录用户一年打卡状态,返回数据给客户端,客户端根据数据来显示用户近一年打卡日历.并且日历高度要自适应
解决方法
1.ViewPager2高度不能自适应,需要测量ViewPager2当前位置的高度,然后再进行设置高度。
2.GridView的高度不能自适应,日历肯定是7列,计算出有多少行,用行数*GridView item的高度,得到GridView高度
3.ViewPager2+GridView来日历展示
效果图
代码实现
activity_main布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="match_parent"android:layout_height="match_parent"android:layout_marginStart="12dp"android:layout_marginEnd="12dp"android:orientation="vertical"tools:context=".MainActivity"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="39dp"><ImageViewandroid:id="@+id/arrow_left"android:layout_width="16dp"android:layout_height="16dp"android:layout_marginStart="28dp"android:layout_marginTop="12dp"android:src="@mipmap/icon_arrow_pink"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/date_tv"android:layout_width="wrap_content"android:layout_height="22dp"android:layout_marginTop="9dp"android:fontFamily="sans-serif-medium"android:textColor="#FE6484"android:textSize="14dp"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /><ImageViewandroid:id="@+id/arrow_right"android:layout_width="16dp"android:layout_height="16dp"android:layout_marginTop="12dp"android:layout_marginEnd="28dp"android:src="@mipmap/icon_arrow_grey"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:background="#FFE8E9" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="20dp"android:orientation="horizontal"><TextViewandroid:layout_width="0dp"android:layout_height="16dp"android:layout_marginTop="4dp"android:layout_weight="1"android:gravity="center"android:text="日"android:textColor="#FFB3BB"android:textSize="12dp"android:textStyle="normal" /><TextViewandroid:layout_width="0dp"android:layout_height="16dp"android:layout_marginTop="4dp"android:layout_weight="1"android:gravity="center"android:text="一"android:textColor="#FFB3BB"android:textSize="12dp"android:textStyle="normal" /><TextViewandroid:layout_width="0dp"android:layout_height="16dp"android:layout_marginTop="4dp"android:layout_weight="1"android:gravity="center"android:text="二"android:textColor="#FFB3BB"android:textSize="12dp"android:textStyle="normal" /><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginTop="4dp"android:layout_weight="1"android:gravity="center"android:text="三"android:textColor="#FFB3BB"android:textSize="12dp"android:textStyle="normal" /><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginTop="4dp"android:layout_weight="1"android:gravity="center"android:text="四"android:textColor="#FFB3BB"android:textSize="12dp"android:textStyle="normal" /><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginTop="4dp"android:layout_weight="1"android:gravity="center"android:text="五"android:textColor="#FFB3BB"android:textSize="12dp"android:textStyle="normal" /><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginTop="4dp"android:layout_weight="1"android:gravity="center"android:text="六"android:textColor="#FFB3BB"android:textSize="12dp"android:textStyle="normal" /></LinearLayout><androidx.viewpager2.widget.ViewPager2android:id="@+id/vpContainer"android:layout_width="match_parent"android:layout_height="wrap_content" /><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:background="#FFE8E9" /></LinearLayout>
布局效果图
MainActivity
数据:
通过Calendar类得到当天年月日,通过 calendar.add(Calendar.MONTH, -i) 并且通过ArrayList来记录1年内的Calendar
public class MainActivity extends AppCompatActivity {private ViewPager2 viewPager2;private CalendarAdapter calendarAdapter;private TextView dateTv;private ImageView arrowLeftImg, arrowRightImg;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);init();initData();}private void init() {viewPager2 = findViewById(R.id.vpContainer);//viewPager2的适配器calendarAdapter = new CalendarAdapter();viewPager2.setAdapter(calendarAdapter);dateTv = findViewById(R.id.date_tv);arrowLeftImg = findViewById(R.id.arrow_left);arrowRightImg = findViewById(R.id.arrow_right);}private void initData() {List<Calendar> data = new ArrayList<>();//for (int i = 11; i >= 0; i--) {Calendar calendar = Calendar.getInstance();calendar.add(Calendar.MONTH, -i);data.add(calendar);}calendarAdapter.refreshData(data);viewPager2.setCurrentItem(11, false);dateTv.setText(data.get(data.size() - 1).get(Calendar.YEAR) + "-" + (data.get(data.size() - 1).get(Calendar.MONTH) + 1));viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {@Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {super.onPageScrolled(position, positionOffset, positionOffsetPixels);}@Overridepublic void onPageSelected(int position) {super.onPageSelected(position);int year = data.get(position).get(Calendar.YEAR);int month = data.get(position).get(Calendar.MONTH) + 1;dateTv.setText(year + "-" + month);RecyclerView recyclerView = (RecyclerView) viewPager2.getChildAt(0);View view = recyclerView.getLayoutManager().findViewByPosition(position);if (view != null)updatePagerHeightForChild(view, viewPager2);}@Overridepublic void onPageScrollStateChanged(int state) {super.onPageScrollStateChanged(state);}public void updatePagerHeightForChild(View view, ViewPager2 pager) {view.post(() -> {int weightMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.getWidth(), View.MeasureSpec.EXACTLY);int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);view.measure(weightMeasureSpec, heightMeasureSpec);if (pager.getLayoutParams().height != view.getMeasuredHeight()) {ViewGroup.LayoutParams layoutParams = pager.getLayoutParams();layoutParams.height = view.getMeasuredHeight();pager.setLayoutParams(layoutParams);}});}});arrowRightImg.setOnClickListener(V -> {arrowRightImg.post(() -> {if (viewPager2.getCurrentItem() != 11)viewPager2.setCurrentItem(viewPager2.getCurrentItem() + 1, false);});});arrowLeftImg.setOnClickListener(V -> {arrowLeftImg.post(() -> {if (viewPager2.getCurrentItem() != 0)viewPager2.setCurrentItem(viewPager2.getCurrentItem() - 1, false);});});}
测量ViewPager2的高度
ViewPager2的主要通过RecyclerView进行实现,通过得到RecyclerView的位置,来得到相应ViewPager2当前位置的View
RecyclerView recyclerView = (RecyclerView) viewPager2.getChildAt(0);
View view = recyclerView.getLayoutManager().findViewByPosition(position);
通过对ViewPager2监听,来得到当前位置j的view,然后要等view绘制完毕,来计算高度
public void updatePagerHeightForChild(View view, ViewPager2 pager) {view.post(() -> {int weightMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.getWidth(), View.MeasureSpec.EXACTLY);int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);view.measure(weightMeasureSpec, heightMeasureSpec);if (pager.getLayoutParams().height != view.getMeasuredHeight()) {ViewGroup.LayoutParams layoutParams = pager.getLayoutParams();layoutParams.height = view.getMeasuredHeight();pager.setLayoutParams(layoutParams);}});}
ViewPager2的适配器
public class CalendarAdapter extends RecyclerView.Adapter<CalendarView> {private List<Calendar> calendar = new ArrayList<>();public CalendarAdapter() {}@NonNull@Overridepublic CalendarView onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {return new CalendarView(LayoutInflater.from(parent.getContext()).inflate(R.layout.calender_view, parent, false));}@Overridepublic void onBindViewHolder(@NonNull CalendarView holder, int position) {if (calendar.size() != 0)holder.initData(calendar.get(position));}@Overridepublic int getItemCount() {return calendar.size();}public void refreshData(List<Calendar> data) {for (int i = 0; i < data.size(); i++) {calendar.add(data.get(i));}notifyDataSetChanged();}
}
CalenderView(主要是Grid View)
布局
<?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"><GridViewandroid:id="@+id/wgvCalendar"android:layout_width="match_parent"android:layout_height="wrap_content"android:numColumns="7"android:stretchMode="columnWidth" />
</LinearLayout>
逻辑
主要是GridView的处理
主要是通过Calendar得到每个月第一天为星期几和每个月有几天,通过集合对每个月数据进行添加,通过每个月第一天为星期几,来判断需要填充多少天空白数据
public void initData(Calendar calendar) {ArrayList<DateBean> data = new ArrayList<>();//获取第一天是星期几然后计算出需要填充的空白数据for (int i = 0; i < getMonthOneDayWeek(calendar); i++) {//填充空白的data.add(new DateBean(0, 0, 0));}//填充数据for (int i = 0; i < getMonthMaxData(calendar); i++) {data.add(new DateBean(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1, i + 1));}calendarDateAdapter = new CalendarDateAdapter(context, data);gridView.setAdapter(calendarDateAdapter);setGridViewHeight(gridView, data.size());}//获取每个月第一天为星期几private int getMonthOneDayWeek(Calendar calendar) {calendar.set(Calendar.DAY_OF_MONTH, 1);return calendar.get(Calendar.DAY_OF_WEEK) - 1;}//获取当月有几天private int getMonthMaxData(Calendar calendar) {return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);}
DateBean
public class DateBean {int year;int month;int day;public DateBean(int year, int month, int day) {this.year = year;this.month = month;this.day = day;}public int getYear() {return year;}public int getMonth() {return month;}public int getDay() {return day;}}
GridView高度测量
通过得到item的高度*行数 计算出GridView高度
public static void setGridViewHeight(GridView gridView, int size) {// 获取listview的adapterListAdapter listAdapter = gridView.getAdapter();if (listAdapter == null) {return;}int col = 7;int totalHeight = 0;int hang;if (size % col == 0) {hang = size / col;} else {hang = size / col + 1;}View listItem = listAdapter.getView(8, null, gridView);listItem.measure(0, 0);totalHeight = listItem.getMeasuredHeight() * hang;ViewGroup.LayoutParams params = gridView.getLayoutParams();params.height = totalHeight;gridView.setLayoutParams(params);}
完整代码
public class CalendarView extends RecyclerView.ViewHolder {private GridView gridView;private int year = 0;private int month = 0;private CalendarDateAdapter calendarDateAdapter;private Context context;public CalendarView(@NonNull View itemView) {super(itemView);gridView = itemView.findViewById(R.id.wgvCalendar);gridView.setSelector(new ColorDrawable(Color.TRANSPARENT));context = itemView.getContext();initEvent();}private void initEvent() {gridView.getSelectedItem();gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {for (int i = 0; i < adapterView.getCount(); i++) {View v = adapterView.getChildAt(i);if (i == position) {view.setBackgroundColor(Color.parseColor("#F3F3F3"));} else {v.setBackground(null);}}}});}public void initData(Calendar calendar) {ArrayList<DateBean> data = new ArrayList<>();//获取第一天是星期几然后计算出需要填充的空白数据for (int i = 0; i < getMonthOneDayWeek(calendar); i++) {//填充空白的data.add(new DateBean(0, 0, 0));}//填充数据for (int i = 0; i < getMonthMaxData(calendar); i++) {data.add(new DateBean(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1, i + 1));}calendarDateAdapter = new CalendarDateAdapter(context, data);gridView.setAdapter(calendarDateAdapter);setGridViewHeight(gridView, data.size());}//获取第一天为星期几private int getMonthOneDayWeek(Calendar calendar) {calendar.set(Calendar.DAY_OF_MONTH, 1);return calendar.get(Calendar.DAY_OF_WEEK) - 1;}//获取当月有几天private int getMonthMaxData(Calendar calendar) {return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);}public static void setGridViewHeight(GridView gridView, int size) {// 获取listview的adapterListAdapter listAdapter = gridView.getAdapter();if (listAdapter == null) {return;}int col = 7;int totalHeight = 0;int hang;if (size % col == 0) {hang = size / col;} else {hang = size / col + 1;}View listItem = listAdapter.getView(8, null, gridView);listItem.measure(0, 0);totalHeight = listItem.getMeasuredHeight() * hang;ViewGroup.LayoutParams params = gridView.getLayoutParams();params.height = totalHeight;gridView.setLayoutParams(params);}}
GridView的Item的适配器
ublic class CalendarDateAdapter extends BaseAdapter {private Context context;private List<DateBean> mData;public CalendarDateAdapter(Context context, List<DateBean> mData) {this.context = context;this.mData = mData;}@Overridepublic int getCount() {return mData.size();}@Overridepublic Object getItem(int i) {return mData.get(i);}@Overridepublic long getItemId(int i) {return 0;}@Overridepublic View getView(int i, View view, ViewGroup viewGroup) {ViewHolder viewHolder = null;if (view == null) {view = View.inflate(context, R.layout.calender_data_item, null);viewHolder = new ViewHolder();viewHolder.dateTv = view.findViewById(R.id.date_tv);viewHolder.heartState = view.findViewById(R.id.heart_state);view.setTag(viewHolder);} else {viewHolder = (ViewHolder) view.getTag();}DateBean data = mData.get(i);if (data.getDay() != 0) {viewHolder.dateTv.setText("" + data.getDay());} else {viewHolder.dateTv.setText("");viewHolder.heartState.setVisibility(View.GONE);}//选中日期 表示为今天Calendar calendar = Calendar.getInstance();if (data.getYear() == calendar.get(Calendar.YEAR) && data.getMonth() == (calendar.get(Calendar.MONTH) + 1) && data.getDay() == calendar.get(Calendar.DAY_OF_MONTH)) {viewHolder.dateTv.setText("今天");}return view;}static class ViewHolder {public TextView dateTv;public ImageView heartState;}
}
Item布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="48dp"android:layout_height="62dp"><ImageViewandroid:id="@+id/heart_state"android:layout_width="24dp"android:layout_height="24dp"android:src="@mipmap/nest_icon_calender_heart_highlight"android:layout_gravity="center_horizontal"android:layout_marginTop="8dp"/><TextViewandroid:id="@+id/date_tv"android:layout_width="wrap_content"android:layout_height="18dp"android:textColor="#FFB3BB"android:text="5.1"android:textSize="14dp"android:layout_marginTop="4dp"android:layout_gravity="center_horizontal"/>
</LinearLayout>
爱心可以换成自己喜欢的图片
GitHub地址
https://github.com/yeluo329/CalenderView.git
如果觉得不错可以点个赞
Android实现简单日历相关推荐
- 调用Android自带日历功能(日历列表单、添加一个日历事件)
调用Android自带日历功能 觉得这篇文章不错,转载过来. 转载:http://blog.csdn.net/djy1992/article/details/9948393 Android手机配备有 ...
- Android 系统简单介绍
Android 系统简单介绍 2011年11月20日 写这篇文章的目的是为了给那些刚刚入手安 卓手机的新手们一些参考,希望他们能快速的上手 [第一期]ANDROID基础知识1~20 [第二期]继续泡! ...
- Android向系统日历添加事件提醒
项目场景: 在项目开发过程中,需要使用系统日历来辅助提醒.通过向系统日历中写入事件.设置提醒方式,实现到达某个特定的时间自动提醒的功能 解决方案: 1. 请求权限 //Android6.0以上需要动态 ...
- 对Android手机系统日历数据增删改查操作详解
Android手机系统日历数据增删改查详解 前段时间需要开发提取手机系统的日历数据的功能,自己开始研究了一下,刚开始还是比较懵逼的,经过仔细研究还是能够完全贯通了. 如果不想细细研究,可以直接下载我的 ...
- Android Studio简单设置
2019独角兽企业重金招聘Python工程师标准>>> Android Studio 简单设置 界面设置 默认的 Android Studio 为灰色界面,可以选择使用炫酷的黑色界面 ...
- 简单Android手机APP地图,android最简单手机地图APP(只需5分钟)
android最简单手机地图APP--只有三部分. 第一部分 首先建立一个MapActivity在setContentView(R.layout.activity_map);中创建一个代码如下. [h ...
- android 人生日历,android版人生日历日子怎么用 安卓版人生日历日子使用教程
人生日历android版新发3.3.05.10版本,新增日子功能,那么android版人生日历日子怎么用呢?今天小编就为大家分享安卓版人生日历日子使用教程,一起来看看吧! 人生日历的日子,设计成四叶草 ...
- android仿微信的activity平滑水平切换动画,Android实现简单底部导航栏 Android仿微信滑动切换效果...
Android实现简单底部导航栏 Android仿微信滑动切换效果 发布时间:2020-10-09 19:48:00 来源:脚本之家 阅读:96 作者:丶白泽 Android仿微信滑动切换最终实现效果 ...
- 【转】Android Studio简单设置
原文网址:http://ask.android-studio.org/?/article/14 Android Studio 简单设置 界面设置 默认的 Android Studio 为灰色界面,可以 ...
- Android JNI简单实例(android 调用C/C++代码)
转载自 xiechengfa 最终编辑 xiechengfa Android JNI简单实例关键字: android.jni Android的jni实例 android 的应用程序(Dalvik VM ...
最新文章
- 例5.12 输入一串字符,字符个数不超过100,且以.结束。 (信息学奥赛一本通)...
- Linux下安装Elasticsearch2.x
- IOS之AFNetworking,SDWebImage,Kingfisher,Alamofire,FMDB框架的使用
- 导出Excel神器最终版
- 【maven3学习之三】maven构建一个简单的Hello World
- P4980-[模板]Pólya定理
- commons-fileupload、smartUpload和commons-net-ftp
- hpunix查看oracle监听,hp-ux 网络查看
- explain是mysql的关键字吗_Mysql Explain 关键字
- 高质量解读《高性能mysql》——第1章 MySQL架构与历史
- mtu设置失败_为什么华为路由器修改MTU值失败
- python 爬取种子_Python爬虫框架Scrapy 学习笔记 2 ----- 爬取Mininova网站种子文件信息...
- 有限Abel群的一维复表示
- 下载阿里云免费ssl证书
- 路由器实现不同VLAN间通信
- centos7 操作记录
- Apache Tomcat 8配置参考 HTTP连接器
- 协同过滤(collaborative filtering)
- 如何还原服务器db文件的原有模式,dbPaaS该如何进行备份恢复管理?
- 计算一个有向图中出度为零和入度为零的顶点个数