我正在学习android开发,我编写了一个任务调度程序应用程序。我有一个侧边栏,其中的日期至少有 1 个任务。主要活动有一个回收视图,当单击侧边栏中的日期和启动应用程序并选择当前日期时,该视图都会显示任务。如果我在打开文件时更改任务,那么一切都会正常。但是,如果我更改日期,那么在更新时,RecycleView 开始滞后,给出今天的日期,然后给出我在面板中选择的日期的任务。我使用 LiveData 通过 Room 处理数据。如何修复滞后,以便在切换 RecycleView 面板中的日期时正确显示。 主要活动:
class MainActivity : AppCompatActivity() {
private lateinit var drawerLayout: DrawerLayout
private var bottomSheetNewTask: NewTaskFragment? = null
private lateinit var recyclerView: RecyclerView
private lateinit var taskAdapter: TaskAdapter
private lateinit var taskViewModel: TaskViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setAppTheme()
enableEdgeToEdge()
setContentView(R.layout.activity_main)
setupWindowInsets()
setupViewModel()
setupRecyclerView()
setupObservers()
setupNavigationDrawer()
setupAddTaskButton()
}
private fun setAppTheme() {
val sharedPreferences = getSharedPreferences("app_preferences", MODE_PRIVATE)
val isDarkMode = sharedPreferences.getBoolean("dark_mode", false)
AppCompatDelegate.setDefaultNightMode(if (isDarkMode) AppCompatDelegate.MODE_NIGHT_YES else AppCompatDelegate.MODE_NIGHT_NO)
}
private fun setupWindowInsets() {
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
}
private fun setupViewModel() {
val repository = TaskRepository(TaskDatabase.getDatabase(this).taskDao())
val viewModelFactory = TaskViewModelFactory(repository)
taskViewModel = ViewModelProvider(this, viewModelFactory)[TaskViewModel::class.java]
}
private fun setupRecyclerView() {
recyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
taskAdapter = TaskAdapter(emptyList(), taskViewModel) { task -> openTaskDetails(task) }
recyclerView.adapter = taskAdapter
}
private fun setupObservers() {
val today = System.currentTimeMillis()
observeTasks(today)
taskViewModel.getAllTaskDates().observe(this) { dates -> updateSidePanel(dates) }
}
private fun setupNavigationDrawer() {
drawerLayout = findViewById(R.id.drawer_layout)
val navigationView: NavigationView = findViewById(R.id.navigation_view)
navigationView.setNavigationItemSelectedListener { menuItem ->
when (menuItem.itemId) {
R.id.nav_stat -> startActivity(Intent(this, StatisticsActivity::class.java))
R.id.nav_sett -> startActivity(Intent(this, SettingsActivity::class.java))
}
drawerLayout.closeDrawers()
true
}
findViewById<ImageButton>(R.id.menuBtn).setOnClickListener {
if (!drawerLayout.isDrawerOpen(navigationView)) {
drawerLayout.openDrawer(navigationView)
} else {
drawerLayout.closeDrawer(navigationView)
}
}
}
private fun setupAddTaskButton() {
findViewById<ImageButton>(R.id.addTask).setOnClickListener {
if (bottomSheetNewTask == null) {
bottomSheetNewTask = NewTaskFragment()
}
bottomSheetNewTask?.show(supportFragmentManager, bottomSheetNewTask?.tag)
}
}
override fun onBackPressed() {
if (bottomSheetNewTask?.isVisible == true) {
bottomSheetNewTask?.dismiss()
} else {
super.onBackPressed()
}
}
private fun openTaskDetails(task: Task) {
val bottomSheet = TaskFragment.newInstance(task)
bottomSheet.show(supportFragmentManager, "TaskDetails")
}
private fun updateSidePanel(dates: List<String>) {
val navigationView = findViewById<NavigationView>(R.id.navigation_view)
val menu = navigationView.menu
val dynamicGroupId = 1
menu.removeGroup(dynamicGroupId)
dates.forEach { date ->
menu.add(dynamicGroupId, Menu.NONE, Menu.NONE, date).setOnMenuItemClickListener {
onDateSelected(date)
true
}
}
}
private fun onDateSelected(date: String) {
val dateInMillis = convertDateToMillis(date)
observeTasks(dateInMillis)
}
private fun convertDateToMillis(dateString: String): Long {
val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
return dateFormat.parse(dateString)?.time ?: throw IllegalArgumentException("Invalid date format")
}
private fun observeTasks(dateInMillis: Long) {
taskViewModel.getTasksByDate(dateInMillis).observe(this) { tasks ->
taskAdapter.updateTasks(tasks)
findViewById<LinearLayout>(R.id.start_text).isVisible = tasks.isEmpty()
}
}
}
我的TaskDao:
@Dao
interface TaskDao {
@Query("SELECT * FROM tasks WHERE createdAt BETWEEN :startOfDay AND :endOfDay ORDER BY isCompleted ASC, priority DESC")
fun getTasksByDate(startOfDay: Long, endOfDay: Long): LiveData<List<Task>>
@Query("SELECT DISTINCT STRFTIME('%Y-%m-%d', createdAt / 1000, 'unixepoch') FROM tasks ORDER BY createdAt DESC")
fun getAllTaskDates(): LiveData<List<String>>
@Query("SELECT createdAt FROM tasks GROUP BY createdAt ORDER BY COUNT(*) DESC LIMIT 1")
fun getBusiestDay() : LiveData<Long>
@Query("SELECT color, COUNT(*) AS count FROM tasks WHERE color IN ('blue', 'green', 'yellow', 'red') GROUP BY color")
fun getTaskCountByColor(): LiveData<List<ColorCount>>
@Query("SELECT COUNT(*) FROM tasks WHERE isCompleted = 1")
fun getCompletedTaskCount(): LiveData<Int>
@Query("SELECT COUNT(*) FROM tasks WHERE isCompleted = 0")
fun getIncompleteTaskCount(): LiveData<Int>
@Query("SELECT COUNT(*) FROM tasks WHERE priority = 1")
fun getFirstPriorCount(): LiveData<Int>
@Query("SELECT COUNT(*) FROM tasks WHERE priority = 2")
fun getSecondPriorCount(): LiveData<Int>
@Query("SELECT COUNT(*) FROM tasks WHERE priority = 3")
fun getThirdPriorCount(): LiveData<Int>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertTask(task: Task)
@Update
suspend fun updateTask(task: Task)
}
data class ColorCount(
val color: String,
val count: Int
)
我的任务适配器:
class TaskAdapter(
private var tasks: List<Task>,
private val taskViewModel: TaskViewModel,
private val onTaskClick: (Task) -> Unit
) : RecyclerView.Adapter<TaskAdapter.TaskViewHolder>() {
fun updateTasks(newTasks: List<Task>) {
val diffCallback = TaskDiffCallback(tasks, newTasks)
val diffResult = DiffUtil.calculateDiff(diffCallback)
tasks = newTasks
diffResult.dispatchUpdatesTo(this)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TaskViewHolder {
val itemView = LayoutInflater.from(parent.context)
.inflate(R.layout.task_card, parent, false)
return TaskViewHolder(itemView)
}
override fun onBindViewHolder(holder: TaskViewHolder, position: Int) {
holder.bind(tasks[position])
}
override fun getItemCount() = tasks.size
inner class TaskViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val checkBox: CheckBox = itemView.findViewById(R.id.isCompl)
private val cardView: CardView = itemView.findViewById(R.id.card)
private val titleView: TextView = itemView.findViewById(R.id.title)
private val descView: TextView = itemView.findViewById(R.id.desc)
private val firstPrior: ImageView = itemView.findViewById(R.id.first_prior)
private val secondPrior: ImageView = itemView.findViewById(R.id.second_prior)
private val thirdPrior: ImageView = itemView.findViewById(R.id.third_prior)
fun bind(task: Task) {
checkBox.setOnCheckedChangeListener(null) // Удаляем слушатель
checkBox.isChecked = task.isCompleted
checkBox.setOnCheckedChangeListener { _, isChecked ->
task.isCompleted = isChecked
taskViewModel.updateTask(task)
// Обновляем только текущий элемент
notifyItemChanged(adapterPosition)
}
titleView.text = task.title
descView.text = task.description
setPrior(task.priority)
cardView.setCardBackgroundColor(if (task.isCompleted) Color.GRAY else getTaskColor(task.color))
// Обрабатываем нажатие на весь элемент
itemView.setOnClickListener { onTaskClick(task) }
}
private fun getTaskColor(color: String): Int {
return when (color) {
"blue" -> Color.rgb(24, 136, 255)
"green" -> Color.rgb(34, 133, 7)
"yellow" -> Color.rgb(237, 178, 0)
"red" -> Color.rgb(225, 7, 7)
else -> Color.rgb(24, 136, 255)
}
}
private fun setPrior(prior: Int) {
firstPrior.setImageResource(if (prior >= 1) R.drawable.star else R.drawable.star_outline)
secondPrior.setImageResource(if (prior >= 2) R.drawable.star else R.drawable.star_outline)
thirdPrior.setImageResource(if (prior >= 3) R.drawable.star else R.drawable.star_outline)
}
}
}
我尝试更改 TaskViewHolder 中的绑定并使用 TaskDiffCallback 更新 TaskAdapter 中的任务。我认为问题是我同时显示当前日期和侧边栏的日期,但我不知道如何解决它。
在我的TaskRepository中使用distinctUntilChanged()解决了问题
fun getTasksByDate(startOfDay: Long, endOfDay: Long): LiveData<List<Task>> {
return taskDao.getTasksByDate(startOfDay, endOfDay).distinctUntilChanged()
}