feat: Rework the event card UI significantly
This commit is contained in:
parent
58957c1792
commit
fb3d7785ba
|
@ -2,7 +2,6 @@ package com.p_vacho.neat_calendar.activities
|
|||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
|
@ -14,12 +13,11 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import com.p_vacho.neat_calendar.MyApplication
|
||||
import com.p_vacho.neat_calendar.R
|
||||
import com.p_vacho.neat_calendar.adapters.CalendarAdapter
|
||||
import com.p_vacho.neat_calendar.adapters.CalendarDayItemAdapter
|
||||
import com.p_vacho.neat_calendar.api.RetrofitClient
|
||||
import com.p_vacho.neat_calendar.api.models.EventResponse
|
||||
import com.p_vacho.neat_calendar.models.CalendarDay
|
||||
import kotlinx.coroutines.launch
|
||||
import java.time.ZoneOffset
|
||||
import java.time.YearMonth
|
||||
import java.time.ZoneId
|
||||
import java.time.format.TextStyle
|
||||
|
@ -152,7 +150,7 @@ class CalendarActivity : AppCompatActivity() {
|
|||
|
||||
// Set up RecyclerView
|
||||
rvCalendar.layoutManager = GridLayoutManager(this@CalendarActivity, 7) // 7 columns for days of the week
|
||||
rvCalendar.adapter = CalendarAdapter(days) { day -> navigateToDayActivity(day) }
|
||||
rvCalendar.adapter = CalendarDayItemAdapter(days) { day -> navigateToDayActivity(day) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.p_vacho.neat_calendar.activities
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.TextView
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
@ -10,8 +9,7 @@ import androidx.core.view.WindowInsetsCompat
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.p_vacho.neat_calendar.R
|
||||
import com.p_vacho.neat_calendar.adapters.EventAdapter
|
||||
import com.p_vacho.neat_calendar.api.models.EventResponse
|
||||
import com.p_vacho.neat_calendar.adapters.EventCardAdapter
|
||||
import com.p_vacho.neat_calendar.models.CalendarDay
|
||||
|
||||
class DayViewActivity : AppCompatActivity() {
|
||||
|
@ -31,12 +29,13 @@ class DayViewActivity : AppCompatActivity() {
|
|||
@Suppress("DEPRECATION")
|
||||
val calendarDay = intent.getParcelableExtra<CalendarDay>("calendarDay")!!
|
||||
|
||||
// Setup the UI
|
||||
val tvDate: TextView = findViewById(R.id.tvDate)
|
||||
val rvEvents: RecyclerView = findViewById(R.id.rvEvents)
|
||||
|
||||
// Set up UI
|
||||
tvDate.text = calendarDay.date.toString()
|
||||
|
||||
rvEvents.layoutManager = LinearLayoutManager(this)
|
||||
rvEvents.adapter = EventAdapter(calendarDay.events)
|
||||
rvEvents.adapter = EventCardAdapter(calendarDay.events)
|
||||
}
|
||||
}
|
|
@ -8,8 +8,8 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import com.p_vacho.neat_calendar.R
|
||||
import com.p_vacho.neat_calendar.models.CalendarDay
|
||||
|
||||
class CalendarAdapter(private val days: List<CalendarDay?>, private val onDayClicked: (CalendarDay) -> Unit) :
|
||||
RecyclerView.Adapter<CalendarAdapter.DayViewHolder>() {
|
||||
class CalendarDayItemAdapter(private val days: List<CalendarDay?>, private val onDayClicked: (CalendarDay) -> Unit) :
|
||||
RecyclerView.Adapter<CalendarDayItemAdapter.DayViewHolder>() {
|
||||
|
||||
class DayViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val tvDay: TextView = itemView.findViewById(R.id.tvDay)
|
|
@ -0,0 +1,32 @@
|
|||
package com.p_vacho.neat_calendar.adapters
|
||||
|
||||
import android.graphics.Color
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.p_vacho.neat_calendar.R
|
||||
import androidx.core.content.ContextCompat
|
||||
|
||||
class CategoryChipAdapter(private val categories: List<CategoryItem>) :
|
||||
RecyclerView.Adapter<CategoryChipAdapter.CategoryViewHolder>() {
|
||||
|
||||
data class CategoryItem(val id: Int, val name: String, val color: Color)
|
||||
|
||||
inner class CategoryViewHolder(val textView: TextView) : RecyclerView.ViewHolder(textView)
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.item_category_chip, parent, false) as TextView
|
||||
return CategoryViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: CategoryViewHolder, position: Int) {
|
||||
val category = categories[position]
|
||||
holder.textView.text = category.name
|
||||
|
||||
holder.textView.backgroundTintList = android.content.res.ColorStateList.valueOf(category.color.toArgb())
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = categories.size
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package com.p_vacho.neat_calendar.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.p_vacho.neat_calendar.R
|
||||
import com.p_vacho.neat_calendar.api.models.EventResponse
|
||||
|
||||
class EventAdapter(private val events: List<EventResponse>) :
|
||||
RecyclerView.Adapter<EventAdapter.EventViewHolder>() {
|
||||
|
||||
class EventViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val tvTitle: TextView = itemView.findViewById(R.id.tvTitle)
|
||||
val tvDescription: TextView = itemView.findViewById(R.id.tvDescription)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EventViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.item_event, parent, false)
|
||||
return EventViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: EventViewHolder, position: Int) {
|
||||
val event = events[position]
|
||||
holder.tvTitle.text = event.title
|
||||
holder.tvDescription.text = event.description
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = events.size
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package com.p_vacho.neat_calendar.adapters
|
||||
|
||||
import android.graphics.Color
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.p_vacho.neat_calendar.R
|
||||
import com.p_vacho.neat_calendar.api.models.EventResponse
|
||||
import java.time.OffsetDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
import java.util.Locale
|
||||
|
||||
class EventCardAdapter(private val events: List<EventResponse>) :
|
||||
RecyclerView.Adapter<EventCardAdapter.EventViewHolder>() {
|
||||
|
||||
inner class EventViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
val tvTitle: TextView = view.findViewById(R.id.tvTitle)
|
||||
val tvTime: TextView = view.findViewById(R.id.tvTime)
|
||||
val tvDescription: TextView = view.findViewById(R.id.tvDescription)
|
||||
val rvCategories: RecyclerView = view.findViewById(R.id.categoryChipRecyclerView)
|
||||
val eventColorIndicator: View = view.findViewById(R.id.eventColorIndicator)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EventViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_event_card, parent, false)
|
||||
return EventViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: EventViewHolder, position: Int) {
|
||||
val event = events[position]
|
||||
|
||||
// Set event details
|
||||
holder.tvTitle.text = event.title
|
||||
holder.tvTime.text = formatEventTime(event.start_time, event.end_time)
|
||||
holder.eventColorIndicator.setBackgroundColor(event.color.toArgb())
|
||||
|
||||
// Handle empty description
|
||||
if (event.description.isEmpty()) {
|
||||
holder.tvDescription.visibility = View.GONE
|
||||
} else {
|
||||
holder.tvDescription.visibility = View.VISIBLE
|
||||
holder.tvDescription.text = event.description
|
||||
}
|
||||
|
||||
// TODO: Fetch the categoreis for this event from the API
|
||||
val categories = listOf(
|
||||
CategoryChipAdapter.CategoryItem(id = 1, name = "Work", color = Color.valueOf(Color.parseColor("#33A733"))), // Red
|
||||
CategoryChipAdapter.CategoryItem(id = 2, name = "Personal", color = Color.valueOf(Color.parseColor("#3498DB"))), // Blue
|
||||
CategoryChipAdapter.CategoryItem(id = 3, name = "Urgent", color = Color.valueOf(Color.parseColor("#E74C3C"))), // Bright Red
|
||||
CategoryChipAdapter.CategoryItem(id = 4, name = "Fitness", color = Color.valueOf(Color.parseColor("#FFB400"))), // Orange
|
||||
CategoryChipAdapter.CategoryItem(id = 5, name = "Travel", color = Color.valueOf(Color.parseColor("#7D3C98"))), // Purple
|
||||
CategoryChipAdapter.CategoryItem(id = 6, name = "Shopping", color = Color.valueOf(Color.parseColor("#85C1E9"))), // Light Blue
|
||||
CategoryChipAdapter.CategoryItem(id = 7, name = "Finance", color = Color.valueOf(Color.parseColor("#2ECC71"))), // Bright Green
|
||||
CategoryChipAdapter.CategoryItem(id = 8, name = "Health", color = Color.valueOf(Color.parseColor("#F39C12"))), // Gold
|
||||
CategoryChipAdapter.CategoryItem(id = 9, name = "Meetings", color = Color.valueOf(Color.parseColor("#C0392B"))), // Crimson
|
||||
CategoryChipAdapter.CategoryItem(id = 10, name = "Hobbies", color = Color.valueOf(Color.parseColor("#8E44AD"))), // Deep Purple
|
||||
CategoryChipAdapter.CategoryItem(id = 11, name = "Study", color = Color.valueOf(Color.parseColor("#1ABC9C"))), // Teal
|
||||
CategoryChipAdapter.CategoryItem(id = 12, name = "Events", color = Color.valueOf(Color.parseColor("#34495E"))), // Navy Blue
|
||||
)
|
||||
|
||||
holder.rvCategories.layoutManager =
|
||||
LinearLayoutManager(holder.itemView.context, LinearLayoutManager.HORIZONTAL, false)
|
||||
holder.rvCategories.adapter = CategoryChipAdapter(categories)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = events.size
|
||||
|
||||
private fun formatEventTime(startTime: OffsetDateTime, endTime: OffsetDateTime): String {
|
||||
val timeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
|
||||
.withLocale(Locale.getDefault())
|
||||
val dateTimeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
|
||||
.withLocale(Locale.getDefault())
|
||||
|
||||
return when {
|
||||
// Instantaneous event
|
||||
startTime == endTime -> startTime.format(timeFormatter)
|
||||
|
||||
// Cross-day event
|
||||
startTime.toLocalDate() != endTime.toLocalDate() -> {
|
||||
"${startTime.format(dateTimeFormatter)} - ${endTime.format(dateTimeFormatter)}"
|
||||
}
|
||||
|
||||
// Same-day event
|
||||
else -> {
|
||||
"${startTime.format(timeFormatter)} - ${endTime.format(timeFormatter)}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
app/src/main/res/drawable/bg_category_chip.xml
Normal file
5
app/src/main/res/drawable/bg_category_chip.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@android:color/transparent" />
|
||||
<stroke android:width="1dp" android:color="?android:attr/textColorPrimary" />
|
||||
<corners android:radius="16dp" />
|
||||
</shape>
|
19
app/src/main/res/drawable/card_border.xml
Normal file
19
app/src/main/res/drawable/card_border.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Outer Border -->
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@android:color/transparent" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="?attr/colorOutline" />
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<!-- Inner Background -->
|
||||
<item android:left="2dp" android:top="2dp" android:right="2dp" android:bottom="2dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="?android:attr/colorBackground" />
|
||||
<corners android:radius="6dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
|
@ -22,5 +22,8 @@
|
|||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvEvents"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent"
|
||||
tools:listitem="@layout/item_event_card"
|
||||
tools:itemCount="5"
|
||||
tools:visibility="visible" />
|
||||
</LinearLayout>
|
17
app/src/main/res/layout/item_category_chip.xml
Normal file
17
app/src/main/res/layout/item_category_chip.xml
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@drawable/bg_category_chip"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:backgroundTint="@android:color/holo_blue_dark"
|
||||
tools:text="Work" />
|
|
@ -1,22 +0,0 @@
|
|||
<?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="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvDescription"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:paddingTop="4dp" />
|
||||
</LinearLayout>
|
83
app/src/main/res/layout/item_event_card.xml
Normal file
83
app/src/main/res/layout/item_event_card.xml
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.cardview.widget.CardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:cardElevation="4dp"
|
||||
app:cardBackgroundColor="?android:attr/colorBackground"
|
||||
android:background="@drawable/card_border">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- Event Title Row -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<!-- Event Color Indicator -->
|
||||
<View
|
||||
android:id="@+id/eventColorIndicator"
|
||||
android:layout_width="8dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@color/event_indicator_color"
|
||||
android:contentDescription="@string/event_color_indicator" />
|
||||
|
||||
<!-- Event Title -->
|
||||
<TextView
|
||||
android:id="@+id/tvTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
tools:text="Event Title" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Event Time -->
|
||||
<TextView
|
||||
android:id="@+id/tvTime"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:paddingTop="4dp"
|
||||
tools:text="10:00 AM - 11:00 AM" />
|
||||
|
||||
<!-- Event Description -->
|
||||
<TextView
|
||||
android:id="@+id/tvDescription"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:paddingTop="8dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
tools:text="This is a sample description for the event. It gives an overview of what the event is about." />
|
||||
|
||||
<!-- Categories Section -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/categoryChipRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
tools:listitem="@layout/item_category_chip"
|
||||
tools:itemCount="3"
|
||||
tools:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:orientation="horizontal" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
|
@ -25,4 +25,5 @@
|
|||
<string name="friday_short">Fr</string>
|
||||
<string name="saturday_short">Sa</string>
|
||||
<string name="sunday_short">Su</string>
|
||||
<string name="event_color_indicator">Event Color Indicator</string>
|
||||
</resources>
|
Loading…
Reference in a new issue