IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 日期倒计时App——SpecialDay -> 正文阅读

[移动开发]日期倒计时App——SpecialDay

声明:本App仅用于学习,禁止用于一切商业用途。

之前复习了一下Android的基础知识,自己又没有完全开发过一个能用的App,都是写一些小Demo。于是就想写一个简单的App来巩固自己的知识。于是SpecialDay就诞生了。

?

一、需求分析

作为一款倒计时的App,最主要的功能就是用户添加提醒事件,然后在App主页面向用户展示事件的剩余天数。

并且有一些事件属于是每年重复或每月重复的事件,用户在添加事件的时候可以选择重复类型。App需要调整重复事件的事件。

对于已有的事件用户也可以进行修改和删除。

二、实现

1.数据库设计

一个事件应该至少有标题,类型(方便后续可分类查看), 日期,重复类型的属性,所以数据库设计如下

列名类型注释
idinteger主键,自增
titletext标题
typeinteger事件类型,对应于type表格
event_datedate事件日期
repeatinteger重复类型,0-不重复,1-每年重复,2-每月重复

同时需要一个type表格存储事件类型

列名类型注释
idinteger主键
nametext类型名称

2.界面设计

2.1 主页面

在主页面里,需要展示的是标题栏和事件列表。

标题栏用一个RelativeLayout实现即可,这里排列的是三张图片,源于网络。而事件列表的展示我选用了RecyclerView。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <RelativeLayout
        android:id="@+id/main_menu"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        tools:ignore="UselessParent"
        android:gravity="center">

        <ImageView
            android:id="@+id/setting"
            android:layout_width="25dp"
            android:layout_height="25dp"
            android:src="@drawable/setting"
            android:layout_gravity="center_vertical"
            android:background="@color/white"/>

        <ImageView
            android:id="@+id/title"
            android:layout_width="150dp"
            android:layout_height="30dp"
            android:layout_gravity="center_vertical"
            android:layout_marginLeft="50dp"
            android:layout_marginStart="50dp"
            android:src="@drawable/title"
            android:layout_toEndOf="@id/setting"
            android:layout_toRightOf="@id/setting" />


        <ImageView
            android:id="@+id/add"
            android:layout_width="25dp"
            android:layout_height="25dp"
            android:layout_gravity="center_vertical"
            android:layout_marginLeft="50dp"
            android:layout_marginStart="50dp"
            android:src="@drawable/add"
            android:layout_toRightOf="@id/title"
            android:layout_toEndOf="@id/title"
            />
    </RelativeLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/main_menu">

    </androidx.recyclerview.widget.RecyclerView>

</RelativeLayout>

而RecyclerView具体的item也是用RelativeLayout和LinearLayout搭配使用实现布局。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 ? ?android:layout_width="wrap_content"
 ? ?android:orientation="horizontal"
 ? ?android:paddingTop="10dp"
 ? ?android:layout_height="wrap_content">
?
?
 ? ?<ImageView
 ? ? ? ?android:id="@+id/iv_icon"
 ? ? ? ?android:layout_width="25dp"
 ? ? ? ?android:layout_height="25dp"
 ? ? ? ?android:layout_marginTop="15dp"
 ? ? ? ?android:layout_marginLeft="15dp"
 ? ? ? ?android:layout_marginStart="15dp"
 ? ? ? ?android:layout_marginBottom="15dp"
 ? ? ? ?android:src="@drawable/calendar"/>
?
 ? ?<LinearLayout
 ? ? ? ?android:layout_width="match_parent"
 ? ? ? ?android:layout_height="55dp"
 ? ? ? ?android:orientation="vertical">
?
?
 ? ? ? ?<RelativeLayout
 ? ? ? ? ? ?android:layout_width="match_parent"
 ? ? ? ? ? ?android:layout_height="50dp"
 ? ? ? ? ? ?android:gravity="center_vertical"
 ? ? ? ? ? ?>
?
 ? ? ? ? ? ?<LinearLayout
 ? ? ? ? ? ? ? ?android:id="@+id/date_layout"
 ? ? ? ? ? ? ? ?android:layout_width="150dp"
 ? ? ? ? ? ? ? ?android:layout_height="match_parent"
 ? ? ? ? ? ? ? ?android:orientation="vertical">
?
 ? ? ? ? ? ? ? ?<TextView
 ? ? ? ? ? ? ? ? ? ?android:id="@+id/tv_title"
 ? ? ? ? ? ? ? ? ? ?android:layout_width="match_parent"
 ? ? ? ? ? ? ? ? ? ?android:layout_height="30dp"
 ? ? ? ? ? ? ? ? ? ?android:text="123"
 ? ? ? ? ? ? ? ? ? ?android:textSize="20sp"
 ? ? ? ? ? ? ? ? ? ?android:textColor="@color/black"
 ? ? ? ? ? ? ? ? ? ?android:gravity="center_vertical"
 ? ? ? ? ? ? ? ? ? ?android:paddingLeft="10dp"/>
?
 ? ? ? ? ? ? ? ?<TextView
 ? ? ? ? ? ? ? ? ? ?android:id="@+id/tv_date"
 ? ? ? ? ? ? ? ? ? ?android:layout_width="match_parent"
 ? ? ? ? ? ? ? ? ? ?android:layout_height="20dp"
 ? ? ? ? ? ? ? ? ? ?android:text="2021-01-04"
 ? ? ? ? ? ? ? ? ? ?android:textSize="15sp"
 ? ? ? ? ? ? ? ? ? ?android:paddingLeft="10dp"/>
?
?
 ? ? ? ? ? ?</LinearLayout>
?
?
 ? ? ? ? ? ?<TextView
 ? ? ? ? ? ? ? ?android:id="@+id/tv_countDown"
 ? ? ? ? ? ? ? ?android:layout_width="180dp"
 ? ? ? ? ? ? ? ?android:layout_height="match_parent"
 ? ? ? ? ? ? ? ?android:paddingRight="10dp"
 ? ? ? ? ? ? ? ?android:paddingTop="5dp"
 ? ? ? ? ? ? ? ?android:text="就在今天"
 ? ? ? ? ? ? ? ?android:textSize="20dp"
 ? ? ? ? ? ? ? ?android:gravity="end"
 ? ? ? ? ? ? ? ?android:paddingVertical="10dp"
 ? ? ? ? ? ? ? ?android:textColor="#3FBFBF"
 ? ? ? ? ? ? ? ?android:layout_toRightOf="@id/date_layout"
 ? ? ? ? ? ? ? ?android:layout_toEndOf="@id/date_layout"/>
?
?
 ? ? ? ?</RelativeLayout>
?
 ? ? ? ?<View
 ? ? ? ? ? ?android:layout_width="match_parent"
 ? ? ? ? ? ?android:layout_height="1dp"
 ? ? ? ? ? ?android:layout_marginTop="4dp"
 ? ? ? ? ? ?android:layout_marginLeft="5dp"
 ? ? ? ? ? ?android:background="#000000"
 ? ? ? ? ? ?android:layout_marginStart="5dp" />
?
 ? ?</LinearLayout>
?
?
</LinearLayout>

这样主页面的布局就完成了。

2.2 添加事件页面

先看一下效果。

?

同样是通过RelativeLayout和LinearLayout就能实现这个布局。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 ? ?android:orientation="vertical"
 ? ?xmlns:tools="http://schemas.android.com/tools"
 ? ?android:layout_width="match_parent"
 ? ?android:layout_height="match_parent"
 ? ?tools:context=".AddEventActivity">
?
 ? ?<RelativeLayout
 ? ? ? ?android:layout_width="match_parent"
 ? ? ? ?android:layout_height="60dp"
 ? ? ? ?android:gravity="center"
 ? ? ? ?tools:ignore="UselessParent">
?
 ? ? ? ?<ImageView
 ? ? ? ? ? ?android:id="@+id/icon_return"
 ? ? ? ? ? ?android:layout_width="25dp"
 ? ? ? ? ? ?android:layout_height="25dp"
 ? ? ? ? ? ?android:src="@drawable/icon_return"
 ? ? ? ? ? ?android:layout_gravity="center_vertical"
 ? ? ? ? ? ?android:background="@color/white"/>
?
 ? ? ? ?<ImageView
 ? ? ? ? ? ?android:id="@+id/title"
 ? ? ? ? ? ?android:layout_width="150dp"
 ? ? ? ? ? ?android:layout_height="30dp"
 ? ? ? ? ? ?android:layout_gravity="center_vertical"
 ? ? ? ? ? ?android:layout_marginLeft="50dp"
 ? ? ? ? ? ?android:layout_marginStart="50dp"
 ? ? ? ? ? ?android:src="@drawable/title"
 ? ? ? ? ? ?android:layout_toRightOf="@id/icon_return"
 ? ? ? ? ? ?android:layout_toEndOf="@id/icon_return"/>
?
?
 ? ? ? ?<ImageView
 ? ? ? ? ? ?android:id="@+id/submit"
 ? ? ? ? ? ?android:layout_width="25dp"
 ? ? ? ? ? ?android:layout_height="25dp"
 ? ? ? ? ? ?android:layout_gravity="center_vertical"
 ? ? ? ? ? ?android:layout_marginLeft="50dp"
 ? ? ? ? ? ?android:layout_marginStart="50dp"
 ? ? ? ? ? ?android:src="@drawable/submit"
 ? ? ? ? ? ?android:layout_toRightOf="@id/title"
 ? ? ? ? ? ?android:layout_toEndOf="@id/title"
 ? ? ? ? ? ?/>
 ? ?</RelativeLayout>
?
 ? ?<LinearLayout
?
 ? ? ? ?android:layout_width="match_parent"
 ? ? ? ?android:layout_height="50dp"
 ? ? ? ?>
?
 ? ? ? ?<TextView
 ? ? ? ? ? ?android:layout_width="80dp"
 ? ? ? ? ? ?android:layout_height="match_parent"
 ? ? ? ? ? ?android:layout_marginLeft="10dp"
 ? ? ? ? ? ?android:text="标题"
 ? ? ? ? ? ?android:textSize="15sp"
 ? ? ? ? ? ?android:gravity="center"
 ? ? ? ? ? ?android:textColor="@color/black"/>
?
 ? ? ? ?<EditText
 ? ? ? ? ? ?android:id="@+id/add_et_title"
 ? ? ? ? ? ?android:layout_width="match_parent"
 ? ? ? ? ? ?android:layout_height="match_parent"
 ? ? ? ? ? ?android:background="@null"
 ? ? ? ? ? ?android:layout_marginRight="40dp"
 ? ? ? ? ? ?android:layout_marginEnd="40dp"
 ? ? ? ? ? ?android:gravity="end"
 ? ? ? ? ? ?android:maxLines="1"
 ? ? ? ? ? ?android:maxLength="15"
 ? ? ? ? ? ?android:layout_marginTop="5dp"
 ? ? ? ? ? ?android:textSize="15sp"
 ? ? ? ? ? ?/>
?
 ? ?</LinearLayout>
?
 ? ?<LinearLayout
?
 ? ? ? ?android:layout_width="match_parent"
 ? ? ? ?android:layout_height="50dp">
?
 ? ? ? ?<TextView
 ? ? ? ? ? ?android:layout_width="80dp"
 ? ? ? ? ? ?android:layout_height="match_parent"
 ? ? ? ? ? ?android:layout_marginLeft="10dp"
 ? ? ? ? ? ?android:text="日期"
 ? ? ? ? ? ?android:textSize="15sp"
 ? ? ? ? ? ?android:gravity="center"
 ? ? ? ? ? ?android:textColor="@color/black"/>
?
 ? ? ? ?<TextView
 ? ? ? ? ? ?android:id="@+id/add_tv_date"
 ? ? ? ? ? ?android:layout_width="match_parent"
 ? ? ? ? ? ?android:layout_height="match_parent"
 ? ? ? ? ? ?android:background="@null"
 ? ? ? ? ? ?android:layout_marginRight="40dp"
 ? ? ? ? ? ?android:layout_marginEnd="40dp"
 ? ? ? ? ? ?android:gravity="end"
 ? ? ? ? ? ?android:maxLines="1"
 ? ? ? ? ? ?android:textSize="15sp"
 ? ? ? ? ? ?android:paddingTop="8dp"
 ? ? ? ? ? ?android:text="2022-1-4"
 ? ? ? ? ? ?android:textColor="@color/black"
 ? ? ? ? ? ?/>
?
 ? ?</LinearLayout>
?
 ? ?<LinearLayout
?
 ? ? ? ?android:layout_width="match_parent"
 ? ? ? ?android:layout_height="50dp">
?
 ? ? ? ?<TextView
 ? ? ? ? ? ?android:layout_width="80dp"
 ? ? ? ? ? ?android:layout_height="match_parent"
 ? ? ? ? ? ?android:layout_marginLeft="10dp"
 ? ? ? ? ? ?android:text="分类"
 ? ? ? ? ? ?android:textSize="15sp"
 ? ? ? ? ? ?android:gravity="center"
 ? ? ? ? ? ?android:textColor="@color/black"/>
?
 ? ? ? ?<Spinner
 ? ? ? ? ? ?android:id="@+id/select_type"
 ? ? ? ? ? ?android:layout_width="140dp"
 ? ? ? ? ? ?android:layout_height="match_parent"
 ? ? ? ? ? ?android:layout_marginLeft="140dp"
 ? ? ? ? ? ?android:layout_marginStart="140dp"
 ? ? ? ? ? ?android:layout_gravity="center_vertical"/>
?
 ? ?</LinearLayout>
?
 ? ?<LinearLayout
 ? ? ? ?android:orientation="horizontal"
 ? ? ? ?android:layout_width="match_parent"
 ? ? ? ?android:layout_height="50dp">
?
 ? ? ? ?<TextView
 ? ? ? ? ? ?android:layout_width="80dp"
 ? ? ? ? ? ?android:layout_height="match_parent"
 ? ? ? ? ? ?android:layout_marginLeft="10dp"
 ? ? ? ? ? ?android:text="重复"
 ? ? ? ? ? ?android:textSize="15sp"
 ? ? ? ? ? ?android:gravity="center"
 ? ? ? ? ? ?android:textColor="@color/black"/>
?
 ? ? ? ?<Spinner
 ? ? ? ? ? ?android:id="@+id/select_repeat"
 ? ? ? ? ? ?android:layout_marginLeft="140dp"
 ? ? ? ? ? ?android:layout_width="140dp"
 ? ? ? ? ? ?android:layout_height="match_parent"
 ? ? ? ? ? ?android:layout_gravity="center_vertical"
 ? ? ? ? ? ?android:layout_marginStart="140dp" />
?
 ? ?</LinearLayout>
?
 ? ?
</LinearLayout>

3.具体实现

3.1 EventItemAdapter

RecyclerView需要Adapter去填充数据,这里Adapter主要做的事情就是把数据绑定到UI控件上去。

class EventItemAdapter(private val list: List<EventItem>) : RecyclerView.Adapter<EventItemAdapter.ItemViewHolder>() {
?
?
 ? ?inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) {
 ? ? ? ?val icon: ImageView = view.findViewById(R.id.iv_icon)
 ? ? ? ?val title: TextView = view.findViewById(R.id.tv_title)
 ? ? ? ?val date: TextView = view.findViewById(R.id.tv_date)
 ? ? ? ?val countDown: TextView = view.findViewById(R.id.tv_countDown)
 ?  }
?
 ? ?override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
 ? ? ? ?val view = LayoutInflater.from(parent.context)
 ? ? ? ? ?  .inflate(R.layout.special_day_item, parent, false)
 ? ? ? ?return ItemViewHolder(view)
 ?  }
?
 ? ?@SuppressLint("SetTextI18n", "SimpleDateFormat")
 ? ?override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
 ? ? ? ?val event = list[position]
 ? ? ? ?holder.title.text = event.title
 ? ? ? ?holder.date.text = event.date
 ? ? ? ?val day = DateUtil.getCountDown(event.date)
 ? ? ? ?if (day > 0) {
 ? ? ? ? ? ?holder.countDown.text = "还有 $day 天"
 ? ? ? ? ? ?holder.countDown.setTextColor(android.graphics.Color.BLUE)
 ? ? ?  } else if (day == 0) {
 ? ? ? ? ? ?holder.countDown.text = "就在今天!"
 ? ? ?  } else if (day < -365){
 ? ? ? ? ? ?holder.countDown.text = "已经过了 ${abs(day/365)} 年"
 ? ? ? ? ? ?holder.countDown.setTextColor(android.graphics.Color.RED)
 ? ? ?  } else {
 ? ? ? ? ? ?holder.countDown.text = "已经过了 ${abs(day)} 天"
 ? ? ? ? ? ?holder.countDown.setTextColor(android.graphics.Color.RED)
 ? ? ?  }
 ?  }
?
 ? ?override fun getItemCount(): Int = list.size
 ? ?
?
}

其中需要用到一个工具类去计算目标时间和系统时间相差的天数。由于用毫秒相减的方式对于需求来说并不适合,这里计算的方法是硬算有多少天。

   @SuppressLint("SimpleDateFormat")
 ? ?fun getCountDown(date: String): Int {
 ? ? ? ?val nowYear = getYear()
 ? ? ? ?val nowDay = getDayOfYear()
 ? ? ? ?val target = Calendar.getInstance()
 ? ? ? ?val ft = SimpleDateFormat("yyyy-MM-dd")
 ? ? ? ?val targetDate:Date?
 ? ? ? ?try {
 ? ? ? ? ? ?targetDate = ft.parse(date)!!
 ? ? ?  }catch (e: ParseException) {
 ? ? ? ? ? ?e.printStackTrace()
 ? ? ? ? ? ?return 0
 ? ? ?  }
 ? ? ? ?target.time = targetDate
 ? ? ? ?val targetYear = target.get(Calendar.YEAR)
 ? ? ? ?val targetDay = target.get(Calendar.DAY_OF_YEAR)
 ? ? ? ?if (nowYear == targetYear) {
 ? ? ? ? ? ?return targetDay - nowDay
 ? ? ?  } else if (targetYear < nowYear) {
 ? ? ? ? ? ?return (targetYear - nowYear) * 365
 ? ? ?  } else {
 ? ? ? ? ? ?var days = 0
 ? ? ? ? ? ?for (i in nowYear..targetYear) {
 ? ? ? ? ? ? ? ?if (i == nowYear) {
 ? ? ? ? ? ? ? ? ? ?days += if(GregorianCalendar().isLeapYear(nowYear)) {
 ? ? ? ? ? ? ? ? ? ? ? ?365 - nowDay
 ? ? ? ? ? ? ? ? ?  } else {
 ? ? ? ? ? ? ? ? ? ? ? ?366 - nowDay
 ? ? ? ? ? ? ? ? ?  }
 ? ? ? ? ? ? ?  } else if (i == targetYear) {
 ? ? ? ? ? ? ? ? ? ?days += targetDay
 ? ? ? ? ? ? ?  } else {
 ? ? ? ? ? ? ? ? ? ?days += if(GregorianCalendar().isLeapYear(i)) {
 ? ? ? ? ? ? ? ? ? ? ? ?365
 ? ? ? ? ? ? ? ? ?  } else {
 ? ? ? ? ? ? ? ? ? ? ? ?366
 ? ? ? ? ? ? ? ? ?  }
 ? ? ? ? ? ? ?  }
 ? ? ? ? ?  }
 ? ? ? ? ? ?return days
 ? ? ?  }
 ?  }

3.2 MainActivity

这是整个app的主界面。他要做的工作如下(这里其实应该使用MVVM的方式,但是因为还没有深入了解过MVVM框架,这些事暂时由Activity完成):

  1. 在启动app的时候,判断一下是否有需要修改的事件日期。比如昨天的一个事件是每月重复,就需要先更新。

  2. 然后去数据库里读取数据,把数据传递给Adapter。

  3. 给控件添加点击事件

class MainActivity : AppCompatActivity() , View.OnClickListener{
?
 ? ?var width: Int = 0
 ? ?lateinit var myHelper: MyDatabaseHelper
 ? ?private val databaseName = "specialDay.db"
 ? ?private lateinit var db: SQLiteDatabase
?
 ? ?override fun onCreate(savedInstanceState: Bundle?) {
 ? ? ? ?super.onCreate(savedInstanceState)
 ? ? ? ?supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
 ? ? ? ?setContentView(R.layout.activity_main)
 ? ? ? ?setting.setOnClickListener(this)
 ? ? ? ?add.setOnClickListener(this)
 ? ? ? ?width = DeviceUtils.getScreenWidth(this)
 ? ? ? ?myHelper = MyDatabaseHelper(this, databaseName, 1)
 ? ? ? ?db = myHelper.writableDatabase
 ? ? ? ?checkRepeat()
 ? ? ? ?initContent()
 ?  }
?
 ? ?override fun onClick(v: View) {
 ? ? ? ?when (v.id) {
 ? ? ? ? ? ?R.id.setting -> {
 ? ? ? ? ? ? ? ?val sql = "delete from event"
 ? ? ? ? ? ? ? ?db.execSQL(sql)
 ? ? ? ? ? ? ? ?Toast.makeText(this, "11111", Toast.LENGTH_SHORT).show()
 ? ? ? ? ? ? ? ?Log.d("MainActivity", width.toString())
 ? ? ? ? ?  }
 ? ? ? ? ? ?R.id.add -> {
 ? ? ? ? ? ? ? ?val intent = Intent(this, AddEventActivity::class.java)
 ? ? ? ? ? ? ? ?startActivity(intent)
 ? ? ? ? ? ? ? ?finish()
 ? ? ? ? ?  }
 ? ? ?  }
 ?  }
?
 ? ?private fun initContent() {
 ? ? ? ?val fakeData = getData()
 ? ? ? ?Log.d("MainActivity", fakeData.size.toString())
 ? ? ? ?val adapter = EventItemAdapter(fakeData)
 ? ? ? ?val manager = LinearLayoutManager(this)
 ? ? ? ?rv_content.layoutManager = manager
 ? ? ? ?rv_content.adapter = adapter
 ?  }
?
 ? ?@SuppressLint("SimpleDateFormat")
 ? ?private fun getData(): List<EventItem>{
?
 ? ? ? ?val sql = "select * from event order by event_date"
 ? ? ? ?val cursor = db.rawQuery(sql, null)
 ? ? ? ?val expired = ArrayList<EventItem>()
 ? ? ? ?val unExpired = ArrayList<EventItem>()
 ? ? ? ?val ft = SimpleDateFormat("yyyy-MM-dd")
 ? ? ? ?if (cursor.moveToFirst()) {
 ? ? ? ? ? ?val calendar = Calendar.getInstance()
 ? ? ? ? ? ?do {
 ? ? ? ? ? ? ? ?val id = cursor.getInt(0)
 ? ? ? ? ? ? ? ?val title = cursor.getString(1)
 ? ? ? ? ? ? ? ?val type = cursor.getInt(2)
 ? ? ? ? ? ? ? ?val date = cursor.getString(3)
 ? ? ? ? ? ? ? ?val time = ft.parse(date)!!
 ? ? ? ? ? ? ? ?calendar.time = time
 ? ? ? ? ? ? ? ?val repeat = cursor.getInt(4)
 ? ? ? ? ? ? ? ?val year = calendar.get(Calendar.YEAR)
 ? ? ? ? ? ? ? ?val day = calendar.get(Calendar.DAY_OF_YEAR)
 ? ? ? ? ? ? ? ?if (year < DateUtil.getYear() ||
 ? ? ? ? ? ? ? ? ? ? ?  (year == DateUtil.getYear() && day < DateUtil.getDayOfYear())) {
 ? ? ? ? ? ? ? ? ? ?expired.add(0, EventItem(id, title, type, date, repeat))
 ? ? ? ? ? ? ?  } else {
 ? ? ? ? ? ? ? ? ? ?unExpired.add(EventItem(id, title, type, date, repeat))
 ? ? ? ? ? ? ?  }
 ? ? ? ? ?  }while (cursor.moveToNext())
 ? ? ?  }
 ? ? ? ?cursor.close()
 ? ? ? ?return unExpired + expired
 ?  }
?
 ? ?@SuppressLint("SimpleDateFormat")
 ? ?private fun checkRepeat() {
 ? ? ? ?val nowDate = "${DateUtil.getYear()}-${DateUtil.getMonth()}-${DateUtil.getDayOfMonth()}"
 ? ? ? ?val sql = "select * from event where event_date < ? and repeat != 0"
 ? ? ? ?val db = myHelper.writableDatabase
 ? ? ? ?val cursor = db.rawQuery(sql, arrayOf(nowDate))
 ? ? ? ?if (cursor.moveToFirst()) {
 ? ? ? ? ? ?val calendar = Calendar.getInstance()
 ? ? ? ? ? ?val ft = SimpleDateFormat("yyyy-MM-dd")
 ? ? ? ? ? ?do {
 ? ? ? ? ? ? ? ?val id = cursor.getInt(0)
 ? ? ? ? ? ? ? ?val repeat = cursor.getInt(4)
 ? ? ? ? ? ? ? ?Log.d("MainActivity", repeat.toString())
 ? ? ? ? ? ? ? ?val date = cursor.getString(3)
 ? ? ? ? ? ? ? ?Log.d("MainActivity", "date: ? $date")
 ? ? ? ? ? ? ? ?Log.d("MainActivity", "id: ? $id")
 ? ? ? ? ? ? ? ?val time = ft.parse(date)!!
 ? ? ? ? ? ? ? ?calendar.time = time
 ? ? ? ? ? ? ? ?var year = calendar.get(Calendar.YEAR)
 ? ? ? ? ? ? ? ?var month = calendar.get(Calendar.MONTH) + 1
 ? ? ? ? ? ? ? ?val day = calendar.get(Calendar.DAY_OF_MONTH)
 ? ? ? ? ? ? ? ?if (repeat == 1) {
 ? ? ? ? ? ? ? ? ? ?year += 1
 ? ? ? ? ? ? ?  } else if (repeat == 2) {
 ? ? ? ? ? ? ? ? ? ?month += 1
 ? ? ? ? ? ? ? ? ? ?Log.d("MainActivity", "月份+1")
 ? ? ? ? ? ? ?  }
 ? ? ? ? ? ? ? ?val newDate = "$year-$month-$day"
 ? ? ? ? ? ? ? ?Log.d("MainActivity", "newDate ? $newDate")
 ? ? ? ? ? ? ? ?val updateSql = "update event set event_date = ? where id = ?"
 ? ? ? ? ? ? ? ?db.execSQL(updateSql, arrayOf(newDate, id))
 ? ? ? ? ?  } while (cursor.moveToNext())
 ? ? ?  }
 ? ? ? ?cursor.close()
 ?  }

3.3 AddEventActivity

添加事件方面,有选择日期和选择类型、是否重复的选项。

3.3.1 选择日期

选择日期主要用到了DatePicker控件,然后利用AlertDialog弹出一个对话框的形式,效果如下:

?

首先,我们要先获取当前系统时间,用全局变量存储。

    private fun initDateTime() {
 ? ? ? ?year = DateUtil.getYear()
 ? ? ? ?month = DateUtil.getMonth()
 ? ? ? ?day = DateUtil.getDayOfMonth()
 ?  }

然后添加点击事件弹出对话框。

 private fun chooseDate() {
 ? ? ? ?val dateStr = StringBuffer()
 ? ? ? ?val dialogView = View.inflate(this, R.layout.dialog_date, null)
 ? ? ? ?val datePicker: DatePicker = dialogView.findViewById(R.id.datePicker)!!
 ? ? ? ?datePicker.init(year, month - 1, day, this)
 ? ? ? ?AlertDialog.Builder(this).apply {
 ? ? ? ? ? ?setPositiveButton("设置") {dialog, _ ->
 ? ? ? ? ? ? ? ?mDate = dateStr.append(year.toString()).append("-")
 ? ? ? ? ? ? ? ? ?  .append(month.toString()).append("-").append(day.toString()).toString()
 ? ? ? ? ? ? ? ?add_tv_date.text = mDate
 ? ? ? ? ? ? ? ?dialog.dismiss()
 ? ? ? ? ?  }
 ? ? ? ? ? ?setNegativeButton("取消") {dialog, _ ->
 ? ? ? ? ? ? ? ?dialog.dismiss()
 ? ? ? ? ?  }
 ? ? ? ? ? ?setTitle("选择日期")
 ? ? ? ? ? ?create()
 ? ? ? ? ? ?setView(dialogView)
 ? ? ? ? ? ?show()
 ? ? ?  }
 ?  }

同时还需要实现接口DatePicker.OnDateChangedListener,重写方法onDateChanged

    override fun onDateChanged(view: DatePicker?, year: Int, monthOfYear: Int, dayOfMonth: Int) {
 ? ? ? ?this.year = year
 ? ? ? ?this.month = monthOfYear + 1
 ? ? ? ?this.day = dayOfMonth
 ?  }

3.3.2 选择类型和是否重复

这两个都是用了下拉框Spinner实现。

后续类型应该是从数据库中读取,方便用户自定义类型。不过这里一开始是写死的。在arrays.xml里写死了两个Spinner的数据。

<?xml version="1.0" encoding="utf-8"?>
<resources>
 ? ?
 ? ?<string-array name="repeat">
 ? ? ? ?<item>不重复</item>
 ? ? ? ?<item>每年</item>
 ? ? ? ?<item>每月</item>
 ? ?</string-array>
?
 ? ?<string-array name="type">
 ? ? ? ?<item>事件</item>
 ? ? ? ?<item>生日</item>
 ? ? ? ?<item>爱情</item>
 ? ? ? ?<item>生活</item>
 ? ? ? ?<item>节日</item>
 ? ? ? ?<item>娱乐</item>
 ? ? ? ?<item>学习</item>
 ? ? ? ?<item>工作</item>
 ? ?</string-array>
</resources>

然后写两个内部类实现接口AdapterView.OnItemSelectedListener来监听选择的内容

?
 
? ?inner class RepeatSelectListener: AdapterView.OnItemSelectedListener {
 ? ? ? ?override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
 ? ? ? ? ? ?repeat = position
 ? ? ?  }
?
 ? ? ? ?override fun onNothingSelected(parent: AdapterView<*>?) {
 ? ? ? ? ? ?repeat = 0
 ? ? ?  }
 ?  }
?
 ? ?inner class TypeSelectListener: AdapterView.OnItemSelectedListener {
 ? ? ? ?override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
 ? ? ? ? ? ?mType = position
 ? ? ?  }
?
 ? ? ? ?override fun onNothingSelected(parent: AdapterView<*>?) {
 ? ? ? ? ? ?mType = 0
 ? ? ?  }
 ?  }

三、总结

这篇文章是实现app过程的笔记。

其实整个还有很多的不足。比如可以采用MVVM框架,让Activity的任务不要那么繁重;后续添加又滑删除事件的操作,点击标题可以选择分类进行查看事件等功能。

前面也声明了这只是一个用来巩固知识学习的练手app,如果想要源码的小伙伴可以戳这里:SpecialDay: 一个简单的Android应用。用于记录各种特殊日子。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-01-16 13:11:02  更:2022-01-16 13:12:50 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:56:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码