基于ViewPager2的网格日历开发问题:需按月展示而非单日
实现ViewPager2按月展示日历的核心方案
1. 重构数据结构:从单日到按月分组
把原有的单日数据结构,改成按月分组的日期集合,每个组对应一个月的完整日历数据(包含上月末尾、当月、下月开头的补位日期,保证日历排版为完整的6行7列)。
示例数据结构:
// 每个子列表对应一个月的所有日期(含补位) private List<List<LocalDate>> mMonthDatesList = new ArrayList<>();
2. 完善daysInMonthArray方法,生成单月完整日历数据
修改该方法,让它返回指定月份的所有日期(包括补位),而非单日。示例实现:
public List<LocalDate> daysInMonthArray(LocalDate date) { List<LocalDate> daysInMonth = new ArrayList<>(); YearMonth yearMonth = YearMonth.from(date); int daysInMonthCount = yearMonth.lengthOfMonth(); // 获取当月第一天的星期值(周一=1,周日=7,可按需调整) LocalDate firstDayOfMonth = yearMonth.atDay(1); int firstDayWeekDay = firstDayOfMonth.getDayOfWeek().getValue(); // 添加上月补位日期 LocalDate prevMonthLastDay = firstDayOfMonth.minusDays(1); for (int i = firstDayWeekDay - 1; i > 0; i--) { daysInMonth.add(prevMonthLastDay.minusDays(i - 1)); } // 添加当月日期 for (int day = 1; day <= daysInMonthCount; day++) { daysInMonth.add(yearMonth.atDay(day)); } // 添加下月补位日期,凑够6行×7列=42天 int remainingSlots = 42 - daysInMonth.size(); LocalDate nextMonthFirstDay = yearMonth.plusMonths(1).atDay(1); for (int i = 0; i < remainingSlots; i++) { daysInMonth.add(nextMonthFirstDay.plusDays(i)); } return daysInMonth; }
3. 修改ViewPager2Adapter,适配按月页面
原适配器绑定单日数据,现在改为绑定单月日期列表,每个页面加载一个月的日历布局(示例用RecyclerView网格布局实现)。
示例适配器修改:
public class ViewPager2Adapter extends RecyclerView.Adapter<ViewPager2Adapter.MonthViewHolder> { private List<List<LocalDate>> mMonthDates; public ViewPager2Adapter(List<List<LocalDate>> monthDates) { this.mMonthDates = monthDates; } @NonNull @Override public MonthViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_month_calendar, parent, false); return new MonthViewHolder(view); } @Override public void onBindViewHolder(@NonNull MonthViewHolder holder, int position) { List<LocalDate> monthDays = mMonthDates.get(position); // 给单月日历网格设置日期适配器 CalendarGridAdapter gridAdapter = new CalendarGridAdapter(monthDays); holder.mCalendarGrid.setAdapter(gridAdapter); // 设置月份标题,如"2024年5月" holder.mMonthTitle.setText(YearMonth.from(monthDays.get(15)).format(DateTimeFormatter.ofPattern("yyyy年MM月"))); } @Override public int getItemCount() { return mMonthDates.size(); } static class MonthViewHolder extends RecyclerView.ViewHolder { RecyclerView mCalendarGrid; TextView mMonthTitle; public MonthViewHolder(@NonNull View itemView) { super(itemView); mCalendarGrid = itemView.findViewById(R.id.recycler_calendar_grid); mMonthTitle = itemView.findViewById(R.id.tv_month_title); // 配置网格布局:7列 GridLayoutManager layoutManager = new GridLayoutManager(itemView.getContext(), 7); mCalendarGrid.setLayoutManager(layoutManager); } } }
对应的单月日历布局item_month_calendar.xml示例:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/tv_month_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="18sp" android:textStyle="bold" android:gravity="center" android:padding="8dp"/> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_calendar_grid" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>
4. 在MainActivity中初始化ViewPager2
生成前后N个月的数据(示例默认加载当前月,前后各12个月),并绑定适配器:
public class MainActivity extends AppCompatActivity { private ViewPager2 mViewPager; private ViewPager2Adapter mAdapter; private List<List<LocalDate>> mMonthDatesList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mViewPager = findViewById(R.id.view_pager_calendar); // 生成前后12个月的日历数据 generateMonthDates(LocalDate.now(), 12, 12); // 设置适配器 mAdapter = new ViewPager2Adapter(mMonthDatesList); mViewPager.setAdapter(mAdapter); // 默认跳转到当前月的位置(中间索引) mViewPager.setCurrentItem(12, false); } // 生成指定日期前后n个月的数据 private void generateMonthDates(LocalDate centerDate, int pastMonths, int futureMonths) { // 添加过去的月份 for (int i = pastMonths; i > 0; i--) { mMonthDatesList.add(daysInMonthArray(centerDate.minusMonths(i))); } // 添加当前月 mMonthDatesList.add(daysInMonthArray(centerDate)); // 添加未来的月份 for (int i = 1; i <= futureMonths; i++) { mMonthDatesList.add(daysInMonthArray(centerDate.plusMonths(i))); } } // 此处放置修改后的daysInMonthArray方法 public List<LocalDate> daysInMonthArray(LocalDate date) { // 实现如上述步骤2 } }
5. 扩展优化逻辑
- 日期点击:在
CalendarGridAdapter中给每个日期Item添加点击事件,可通过YearMonth.from(date).equals(YearMonth.from(当月中心日期))区分当月和补位日期。 - 无限滑动:若需类似Google Calendar的无限滑动效果,可让适配器
getItemCount()返回极大值(如Integer.MAX_VALUE),在onBindViewHolder中通过取模计算实际月份索引,同时监听ViewPager2滚动事件动态加载新月份数据。
内容的提问来源于stack exchange,提问作者Chris Damianidis
相关产品推荐
相关产品推荐

