计划管理APP 功能
列表
上拉查询(数据保存在本地的sqlite数据库)
订制
编辑(开始时间、结束时间、文字描述、图片、附件、重要程度、完成情况)
左滑删除
提醒
显示/隐藏已完成
列表功能和数据库实现 数据库实现
列表功能
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return if (viewType == TYPE_ITEM) { val view = LayoutInflater.from(parent.context).inflate(R.layout.listitem, parent, false) ViewHolder(view) } else { val view = LayoutInflater.from(parent.context).inflate(R.layout.footview, parent, false) FooterViewHolder(view) } }2. 在onbindviewholder实现具体逻辑 3. 在mainactivity绑定recyclerview a. 首先获取数据库的所有数据,定义列表为线性布局 b. 注册一个用于处理从Activity返回的结果的回调。如果返回的结果码是RESULT_OK,则调用updateRecyclerView()函数更新RecyclerView。 c. 创建一个新的Recycleadapter,将查询到的数据、结果处理器和一个函数引用(用于设置FloatingActionButton的可见性)传递给它。 private fun setupRecyclerView() { val detailList = queryDataFromDatabase(this) val layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this) recyclerView.layoutManager = layoutManager
// registerForActivityResult()方法的第一个参数是一个ActivityResultContract对象,这个对象负责创建Intent,
// 当Activity结束时,会将Intent传递给onActivityResult()方法。
resultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
updateRecyclerView()
}
}
// Recycleadapter是自己写的一个类,用于将数据和视图绑定,这里的adapter就是Recycleadapter的一个实例
val adapter =
Recycleadapter(detailList, resultLauncher, ::setFloatingActionButtonVisibility)
recyclerView.adapter = adapter
// ItemTouchHelper是一个辅助类,用于实现RecyclerView的拖拽和滑动删除功能
val itemTouchHelperCallback =
object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
// onMove()方法用于实现拖拽功能,这里不需要,所以返回false
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
// onSwiped()方法用于实现滑动删除功能
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.adapterPosition
detailList.removeAt(position)
recyclerView.adapter!!.notifyItemRemoved(position)
// 删除数据库中的数据
deleteDataFromDatabase(this@MainActivity, position)
}
}
// 将ItemTouchHelper和RecyclerView绑定
val itemTouchHelper = ItemTouchHelper(itemTouchHelperCallback)
itemTouchHelper.attachToRecyclerView(recyclerView)
}
查询功能
使用NestedScrollView将主界面的layout包裹起来,实现上滑搜索
设置NestedScrollView的滚动监听。当滚动位置(scrollY)小于等于0时,searchView可见,checkmore按钮不可见。当滚动位置大于100时,searchView不可见,checkmore按钮可见。这样可以在滚动时改变这两个控件的可见性。
设置searchView的查询文本监听。当查询文本改变时,调用updateRecyclerView(newText)函数来更新RecyclerView。这样可以实现搜索功能,即根据用户在searchView中输入的文本来过滤RecyclerView中的数据。
updateRecyclerView调用setData(filteredList)方法将过滤后的数据设置到适配器中,设置showFooter属性的值为filter是否为空或空字符串,然后调用notifyDataSetChanged()方法通知RecyclerView数据已更改 private fun setupSearchView() { val nestedScrollView = findViewById(R.id.NestedScrollView) nestedScrollView.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, _ -> if (scrollY <= 0) { searchView.visibility = View.VISIBLE checkmore.visibility = View.GONE } else if (scrollY > 100) { searchView.visibility = View.GONE checkmore.visibility = View.VISIBLE } })
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
updateRecyclerView(newText)
return false
}
})
}
private fun updateRecyclerView(filter: String? = null) { val detailList = queryDataFromDatabase(this) // 如果filter不为空,就将detailList中的数据过滤,只保留包含filter的数据 // 用于查询功能,不为空就只保存输入框里的内容进行查询 val filteredList = if (filter != null) { detailList.filter { it.title.contains(filter) } } else { detailList } (recyclerView.adapter as Recycleadapter).apply { setData(filteredList) showFooter = filter.isNullOrEmpty() notifyDataSetChanged() } }订制功能/新建提醒事项与编辑功能
方法一:在列表最下方有footview,输入标题后点击最右边的图标,会进入到编辑页面;方法二:通过主页面的底部的悬浮按钮添加新事项
当点击addbutton时,执行以下操作: 获取addtext中的文本,作为标题。如果标题为空,显示一个Toast消息"标题不能为空"。如果DetailList中存在一个未完成的任务,其标题与输入的标题相同,显示一个Toast消息"标题已存在",并清空addtext。否则,创建一个新的detail对象,将其添加到数据库和DetailList中,通知RecyclerView插入了一个新的item,然后清空addtext。
点击事项右侧的图标后(或通过悬浮按钮),跳转到detail activity中,在detail activity中获取到detail实例后进行初始化 a. 如果有标题、开始日期、结束日期、开始时间、结束时间、图片、附件的,则初始化显示 b. 通过DatePickerDialog设置开始日期和结束日期的日期选择功能 c. 通过TimePickerDialog设置开始时间和结束时间的选择功能 d. 通过Spinner设置优先级的选择,首先创建一个array,存放要展示的列表,创建一个ArrayAdapter,用于将数组和spinner绑定,获取用户选择的优先级后更新数据库 // 设置优先级 val priority = findViewById(R.id.priority_spinner) // 创建一个ArrayAdapter,用于将数组和spinner绑定 val adapter_spinner = ArrayAdapter.createFromResource( this, R.array.planets_array, android.R.layout.simple_spinner_item ) // 设置下拉列表的风格 adapter_spinner.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) priority.adapter = adapter_spinner // 设置优先级的默认值 priority.setSelection(detail?.priority ?: 3)
priority.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>,
view: View?, position: Int, id: Long
) {
parent as Spinner
detail?.priority = position // 保存优先级
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
} e. 添加图像与图片,添加附件。
i. 用户选择拍照/选择图片,如果用户选择"拍照",会检查应用是否有相机权限。如果没有,会请求相机权限。如果有,会创建一个新的文件用于存储拍照后的图片,然后启动相机应用。拍照后的图片会保存到刚刚创建的文件中。 如果用户选择"选择图片",代码会启动一个用于选择图片的应用。用户可以从这个应用中选择一张图片,然后这个图片的URI会作为结果返回。 当这两个应用结束后,会调用当前Activity的onActivityResult方法,可以在这个方法中获取拍照后的图片或用户选择的图片。当用户点击添加附件按钮时,会创建一个新的Intent,并设置其动作为Intent.ACTION_GET_CONTENT。这意味着这个Intent将用于获取用户选择的文件。然后,设置Intent的类型为"/",这意味着这个Intent可以选择任何类型的文件。最后,使用startActivityForResult方法启动这个Intent,并等待用户选择文件。当用户选择了文件后,会调用当前Activity的onActivityResult方法,可以在这个方法中获取用户选择的文件。
ii. 在用户选择完照片/拍照/上传完附件后,打开一个输入流,用于读取用户拍摄的照片。 创建一个新的文件,用于保存用户拍摄的照片。打开一个输出流,用于写入用户拍摄的照片。 将输入流的内容复制到输出流,即将用户拍摄的照片写入到文件中。关闭输入流和输出流。
if (requestCode == 1 && resultCode == RESULT_OK) { val userimage = findViewById(R.id.userimage) // 从文件中读取图片 userimage.setImageURI(imageUri) userimage.visibility = View.VISIBLE
// 随机生成文件名
val filename = "userimage${System.currentTimeMillis()}.jpg"
val inputStream = contentResolver.openInputStream(imageUri) //获取图片的输入流
val file = File(filesDir, filename) //创建文件
val outputStream = FileOutputStream(file) //创建输出流
inputStream?.copyTo(outputStream) //将输入流复制到输出流
inputStream?.close() //关闭输入流
outputStream.close() //关闭输出流
// 将文件名保存
val detail = intent.getParcelableExtra<detail>("detail")
if (detail != null) {
detail.imageUrl = filename
// 更新数据库
val id = queryIdFromDatabase(this, detail)
updateDatabase(this, detail, id)
}
}
提醒功能
使用worker进行后台处理,提醒事项的即将开始,和结束。首先从数据库中查询所有的计划,然后过滤出即将开始和已经结束的计划。对于每个即将开始的计划,发送一个"你的计划即将开始"的通知;对于每个已经结束的计划,发送一个"你的计划已经结束,请输入完成情况"的通知。
在mainactivity启动后台任务 override fun doWork(): Result { val allPlans = queryDataFromDatabase(applicationContext) val currenttime = System.currentTimeMillis()
val upcomingPlans = allPlans.filter { plan ->
plan.begindate?.let { it != 0.toLong() && it - currenttime < 86400000 && it > currenttime && !plan.isChecked }
?: false
}
val overPlans = allPlans.filter { plan ->
plan.enddate?.let { it != 0.toLong() && it < currenttime && !plan.isChecked } ?: false
}
upcomingPlans.forEach {
showNotification(
applicationContext,
it,
"你的${it.title}计划即将开始"
)
}
overPlans.forEach {
showNotification(
applicationContext,
it,
"你的${it.title}计划已经结束,请输入完成情况"
)
}
return Result.success()
}左滑删除 创建一个ItemTouchHelper.SimpleCallback对象,这个对象定义了当用户在RecyclerView的item上执行拖拽或滑动操作时应该执行的操作。当一个item被滑动时,会获取这个item的位置,然后从detailList中移除这个item,通知适配器这个item已经被移除,并从数据库中删除这个item private fun setupRecyclerView() { val detailList = queryDataFromDatabase(this) val layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this) recyclerView.layoutManager = layoutManager
// ItemTouchHelper是一个辅助类,用于实现RecyclerView的拖拽和滑动删除功能
val itemTouchHelperCallback =
object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
// onMove()方法用于实现拖拽功能,这里不需要,所以返回false
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
// onSwiped()方法用于实现滑动删除功能
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.adapterPosition
detailList.removeAt(position)
recyclerView.adapter!!.notifyItemRemoved(position)
// 删除数据库中的数据
deleteDataFromDatabase(this@MainActivity, position)
}
}
// 将ItemTouchHelper和RecyclerView绑定
val itemTouchHelper = ItemTouchHelper(itemTouchHelperCallback)
itemTouchHelper.attachToRecyclerView(recyclerView)
}展示/隐藏已完成 当用户点击"showfinished"菜单项时,会切换clickshowfinished的值,然后获取RecyclerView的适配器,并将适配器的setShowFinished属性设置为clickshowfinished的值。然后,根据clickshowfinished的值来更新"showfinished"菜单项的标题。最后,通知适配器数据已更改 // mainactivity private fun setupCheckMoreButton() { var clickshowfinished = false var showFinishedTitle = "显示已完成的任务"
checkmore.setOnClickListener {
val popupMenu = PopupMenu(this, it)
popupMenu.menuInflater.inflate(R.menu.popup_menu, popupMenu.menu)
popupMenu.menu.findItem(R.id.showfinished).title = showFinishedTitle
popupMenu.setOnMenuItemClickListener { menuItem ->
when (menuItem.itemId) {
R.id.showfinished -> {
clickshowfinished = !clickshowfinished
val adapter = recyclerView.adapter as Recycleadapter
adapter.setShowFinished = clickshowfinished
showFinishedTitle =
if (clickshowfinished) "隐藏已完成的任务" else "显示已完成的任务"
adapter.notifyDataSetChanged()
true
}
else -> false
}
}
popupMenu.show()
}
}
//adapter override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { if (holder is ViewHolder) { val detail = DetailList[position] // 默认隐藏已经完成的item,只有点击查看已完成的任务才会显示 if (setShowFinished) { holder.itemView.visibility = View.VISIBLE holder.itemView.layoutParams = RecyclerView.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT ) //设置布局参数,宽度为match_parent,高度为wrap_content }
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。