在RecyclerView中对比JSON返回日期,判断活动是否过期
Android DataBinding: 判断Deals是否过期并处理日期相关问题
针对你用DataBinding填充RecyclerView,需要判断Deals是否过期的需求,我来逐一解答你的疑问,并给出具体示例:
1. 应使用DATE还是String类型存储日期对象?
强烈推荐使用日期类型(比如LocalDateTime)而非String,原因如下:
- String类型的日期无法直接进行比较,每次判断都要先解析成日期对象,重复操作不仅效率低,还容易因格式问题抛出异常;
- 日期类型可以直接调用API进行时间对比,代码更简洁可靠;
- DataBinding支持直接调用日期对象的方法,或者在实体类中封装判断逻辑,绑定到视图上非常方便。
实体类示例(Kotlin)
考虑到你的JSON日期格式是带UTC时区的ISO格式(如2018-04-27T03:06:18.890Z),我们可以用LocalDateTime配合DateTimeFormatter.ISO_OFFSET_DATE_TIME来解析:
import java.time.LocalDateTime import java.time.ZoneOffset import java.time.format.DateTimeFormatter data class DealsPageInfo( val DisplayName: String, val StartDate: String, val ExpiredDate: String, val Url: String, val ImageUrl: String, val ShortDescription: String ) { // 懒加载转换日期对象,避免重复解析 private val startDateTime: LocalDateTime by lazy { LocalDateTime.parse(StartDate, DateTimeFormatter.ISO_OFFSET_DATE_TIME) } private val expiredDateTime: LocalDateTime by lazy { LocalDateTime.parse(ExpiredDate, DateTimeFormatter.ISO_OFFSET_DATE_TIME) } // 对外提供判断是否过期的方法,DataBinding可直接调用 fun isExpired(): Boolean { // 用UTC时间对比,避免时区差异导致判断错误 return expiredDateTime.isBefore(LocalDateTime.now(ZoneOffset.UTC)) } }
如果你是Java开发者,或者需要兼容Android 8.0以下版本,可以引入ThreeTenABP库来使用LocalDateTime,实体类写法类似:
import org.threeten.bp.LocalDateTime; import org.threeten.bp.ZoneOffset; import org.threeten.bp.format.DateTimeFormatter; public class DealsPageInfo { private String DisplayName; private String StartDate; private String ExpiredDate; private String Url; private String ImageUrl; private String ShortDescription; // 转换后的日期对象 private LocalDateTime startDateTime; private LocalDateTime expiredDateTime; public String getDisplayName() { return DisplayName; } public void setDisplayName(String displayName) { DisplayName = displayName; } public String getStartDate() { return StartDate; } public void setStartDate(String startDate) { StartDate = startDate; } public String getExpiredDate() { return ExpiredDate; } public void setExpiredDate(String expiredDate) { ExpiredDate = expiredDate; } // 其他字段的getter/setter... private LocalDateTime getStartDateTime() { if (startDateTime == null) { startDateTime = LocalDateTime.parse(getStartDate(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); } return startDateTime; } private LocalDateTime getExpiredDateTime() { if (expiredDateTime == null) { expiredDateTime = LocalDateTime.parse(getExpiredDate(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); } return expiredDateTime; } public boolean isExpired() { return getExpiredDateTime().isBefore(LocalDateTime.now(ZoneOffset.UTC)); } }
2. 该判断操作应在Fragment还是Adapter中执行?
优先推荐在实体类(Model)中封装判断逻辑,这样完全符合MVVM的设计思想,Fragment和Adapter只需要负责数据传递和视图绑定,不需要处理业务逻辑。如果一定要在Fragment和Adapter中二选一,Adapter是更合适的位置:
- Fragment的职责是页面初始化、数据加载和整体页面控制,不应该处理单个列表项的视图状态判断;
- Adapter负责每个RecyclerView item的视图绑定和状态控制,判断单个Deals是否过期属于item级别的逻辑,放在Adapter中更合理。
布局修改示例
在你的item布局中添加过期提示TextView,并通过DataBinding绑定显示状态:
<!-- 过期提示文本,仅当Deals过期时显示 --> <TextView android:id="@+id/tv_expired_hint" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="DEALS EXPIRED" android:textColor="@color/red" android:textSize="@dimen/list_fontsize" android:visibility="@{offers.isExpired() ? View.VISIBLE : View.GONE}" /> <!-- 原有日期TextView,过期时隐藏 --> <TextView android:id="@+id/list_offers_startDate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="3dp" android:text="@{offers.StartDate}" android:textColor="@color/colorGrayText" android:textSize="@dimen/list_fontsize" android:visibility="@{offers.isExpired() ? View.GONE : View.VISIBLE}" /> <TextView android:id="@+id/list_offers_endDate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="3dp" android:text="@{offers.ExpiredDate}" android:textColor="@color/colorGrayText" android:textSize="@dimen/form_fontsize" android:visibility="@{offers.isExpired() ? View.GONE : View.VISIBLE}" />
RecyclerView Adapter示例(Kotlin)
import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.yourpackage.databinding.ListOffersItemBinding class DealsAdapter(private val dealsList: List<DealsPageInfo>) : RecyclerView.Adapter<DealsAdapter.DealsViewHolder>() { inner class DealsViewHolder(private val binding: ListOffersItemBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(deal: DealsPageInfo) { binding.offers = deal // 立即执行绑定,避免视图更新延迟 binding.executePendingBindings() } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DealsViewHolder { val inflater = LayoutInflater.from(parent.context) val binding = ListOffersItemBinding.inflate(inflater, parent, false) return DealsViewHolder(binding) } override fun onBindViewHolder(holder: DealsViewHolder, position: Int) { holder.bind(dealsList[position]) } override fun getItemCount() = dealsList.size }
最后补充
如果你的需求是需要过滤掉过期的Deals,那可以在Fragment中加载数据后先过滤,再传给Adapter;但如果是要标记过期状态,还是按照上面的方式处理更合理。
内容的提问来源于stack exchange,提问作者Shawn.Y
相关产品推荐
相关产品推荐

