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实现简单日历相关推荐

  1. 调用Android自带日历功能(日历列表单、添加一个日历事件)

    调用Android自带日历功能  觉得这篇文章不错,转载过来. 转载:http://blog.csdn.net/djy1992/article/details/9948393 Android手机配备有 ...

  2. Android 系统简单介绍

    Android 系统简单介绍 2011年11月20日 写这篇文章的目的是为了给那些刚刚入手安 卓手机的新手们一些参考,希望他们能快速的上手 [第一期]ANDROID基础知识1~20 [第二期]继续泡! ...

  3. Android向系统日历添加事件提醒

    项目场景: 在项目开发过程中,需要使用系统日历来辅助提醒.通过向系统日历中写入事件.设置提醒方式,实现到达某个特定的时间自动提醒的功能 解决方案: 1. 请求权限 //Android6.0以上需要动态 ...

  4. 对Android手机系统日历数据增删改查操作详解

    Android手机系统日历数据增删改查详解 前段时间需要开发提取手机系统的日历数据的功能,自己开始研究了一下,刚开始还是比较懵逼的,经过仔细研究还是能够完全贯通了. 如果不想细细研究,可以直接下载我的 ...

  5. Android Studio简单设置

    2019独角兽企业重金招聘Python工程师标准>>> Android Studio 简单设置 界面设置 默认的 Android Studio 为灰色界面,可以选择使用炫酷的黑色界面 ...

  6. 简单Android手机APP地图,android最简单手机地图APP(只需5分钟)

    android最简单手机地图APP--只有三部分. 第一部分 首先建立一个MapActivity在setContentView(R.layout.activity_map);中创建一个代码如下. [h ...

  7. android 人生日历,android版人生日历日子怎么用 安卓版人生日历日子使用教程

    人生日历android版新发3.3.05.10版本,新增日子功能,那么android版人生日历日子怎么用呢?今天小编就为大家分享安卓版人生日历日子使用教程,一起来看看吧! 人生日历的日子,设计成四叶草 ...

  8. android仿微信的activity平滑水平切换动画,Android实现简单底部导航栏 Android仿微信滑动切换效果...

    Android实现简单底部导航栏 Android仿微信滑动切换效果 发布时间:2020-10-09 19:48:00 来源:脚本之家 阅读:96 作者:丶白泽 Android仿微信滑动切换最终实现效果 ...

  9. 【转】Android Studio简单设置

    原文网址:http://ask.android-studio.org/?/article/14 Android Studio 简单设置 界面设置 默认的 Android Studio 为灰色界面,可以 ...

  10. Android JNI简单实例(android 调用C/C++代码)

    转载自 xiechengfa 最终编辑 xiechengfa Android JNI简单实例关键字: android.jni Android的jni实例 android 的应用程序(Dalvik VM ...

最新文章

  1. 例5.12 输入一串字符,字符个数不超过100,且以.结束。 (信息学奥赛一本通)...
  2. Linux下安装Elasticsearch2.x
  3. IOS之AFNetworking,SDWebImage,Kingfisher,Alamofire,FMDB框架的使用
  4. 导出Excel神器最终版
  5. 【maven3学习之三】maven构建一个简单的Hello World
  6. P4980-[模板]Pólya定理
  7. commons-fileupload、smartUpload和commons-net-ftp
  8. hpunix查看oracle监听,hp-ux 网络查看
  9. explain是mysql的关键字吗_Mysql Explain 关键字
  10. 高质量解读《高性能mysql》——第1章 MySQL架构与历史
  11. mtu设置失败_为什么华为路由器修改MTU值失败
  12. python 爬取种子_Python爬虫框架Scrapy 学习笔记 2 ----- 爬取Mininova网站种子文件信息...
  13. 有限Abel群的一维复表示
  14. 下载阿里云免费ssl证书
  15. 路由器实现不同VLAN间通信
  16. centos7 操作记录
  17. Apache Tomcat 8配置参考 HTTP连接器
  18. 协同过滤(collaborative filtering)
  19. 如何还原服务器db文件的原有模式,dbPaaS该如何进行备份恢复管理?
  20. 计算一个有向图中出度为零和入度为零的顶点个数

热门文章

  1. Android 第三方登录之支付宝登录
  2. 曲苑杂坛--清除维护计划产生的日志文件
  3. 利用 EFS 快速搭建 NFS 文件系统
  4. 易灵思FPGA-下载器选择指南
  5. 华为路由器虚拟服务器怎么设置方法,华为路由器PPPoE配置案例-华为路由器设置...
  6. 四位企业家 一种“地头力”
  7. 多线程下载王者荣耀高清壁纸
  8. C#读取网卡地址的几种方法
  9. 为什么科技互联网公司越来越重视数学?
  10. SNMP——使用MIB-Browser 并编写 MIB库