You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Android Studio中实现图示样式的日历?

在Android Studio中实现指定样式日历的方案

根据你提供的日历样式(带月份切换、日期网格、选中状态及周末高亮),可以通过两种方式实现:第三方库快速搭建,或自定义实现高度定制。

一、使用第三方库(推荐,高效快捷)

以Material Calendar View为例,它适配Material Design风格,能快速实现你要的效果:

  1. 添加依赖
    在Module级别的build.gradle(或build.gradle.kts)中添加依赖:
implementation 'com.prolificinteractive:material-calendarview:1.4.3'
  1. 布局中添加日历控件
    在XML布局文件里配置日历的基础样式:
<com.prolificinteractive.materialcalendarview.MaterialCalendarView
    android:id="@+id/calendarView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    <!-- 显示其他月份的日期 -->
    app:mcv_showOtherDates="all"
    <!-- 选中日期的颜色 -->
    app:mcv_selectionColor="#FF4081"
    <!-- 星期标题的文字样式 -->
    app:mcv_weekDayTextAppearance="@style/WeekDayTextStyle"
    <!-- 日期文字样式 -->
    app:mcv_dateTextAppearance="@style/DateTextStyle"
    <!-- 月份切换箭头颜色 -->
    app:mcv_arrowColor="#3F51B5"/>
  1. 自定义周末高亮效果
    创建DayViewDecorator子类来标记周末日期:
public class WeekendHighlightDecorator implements DayViewDecorator {
    private final Calendar calendar = Calendar.getInstance();
    private final Drawable highlightBg;

    public WeekendHighlightDecorator(Context context) {
        highlightBg = ContextCompat.getDrawable(context, R.drawable.weekend_bg);
    }

    @Override
    public boolean shouldDecorate(CalendarDay day) {
        day.copyTo(calendar);
        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
        return dayOfWeek == Calendar.SATURDAY || dayOfWeek == Calendar.SUNDAY;
    }

    @Override
    public void decorate(DayViewFacade view) {
        view.setBackgroundDrawable(highlightBg);
    }
}

在Activity/Fragment中给日历添加装饰器:

MaterialCalendarView calendarView = findViewById(R.id.calendarView);
// 添加周末高亮装饰
calendarView.addDecorator(new WeekendHighlightDecorator(this));
// 设置日期选中监听
calendarView.setOnDateChangedListener((widget, selectedDate, isSelected) -> {
    // 处理选中日期的逻辑,比如显示选中日期
    String date = selectedDate.getYear() + "-" + (selectedDate.getMonth()+1) + "-" + selectedDate.getDay();
    Toast.makeText(this, "选中日期:" + date, Toast.LENGTH_SHORT).show();
});
  1. 自定义样式补充
    可以在styles.xml中定义星期和日期的文字样式:
<style name="WeekDayTextStyle" parent="TextAppearance.MaterialComponents.Caption">
    <item name="android:textColor">#757575</item>
    <item name="android:textSize">12sp</item>
</style>

<style name="DateTextStyle" parent="TextAppearance.MaterialComponents.Body2">
    <item name="android:textColor">#212121</item>
    <item name="android:textSize">14sp</item>
</style>

二、自定义实现(高度定制)

如果需要更个性化的样式,可自行搭建日历组件:

  1. 搭建布局结构
    顶部放置月份标题和左右切换按钮,下方用RecyclerView(或GridView)展示日期网格:
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:gravity="center"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/btn_prev_month"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:src="@drawable/ic_arrow_left"/>

        <TextView
            android:id="@+id/tv_current_month"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="2024年6月"
            android:textSize="18sp"
            android:gravity="center"/>

        <ImageView
            android:id="@+id/btn_next_month"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:src="@drawable/ic_arrow_right"/>
    </LinearLayout>

    <RecyclerView
        android:id="@+id/rv_calendar_dates"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:numColumns="7"/>
</LinearLayout>
  1. 计算日历数据
    编写工具类计算指定月份的第一天星期数、当月天数,生成包含空白占位(月初前的非当月日期)的日期列表:
public class CalendarUtils {
    public static List<Date> getMonthDates(int year, int month) {
        List<Date> dates = new ArrayList<>();
        Calendar calendar = Calendar.getInstance();
        calendar.set(year, month, 1);

        // 获取当月第一天是星期几(周日为1,周一为2...)
        int firstDayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
        // 添加入月初的空白占位
        for (int i = 1; i < firstDayOfWeek; i++) {
            dates.add(null);
        }

        // 添加当月天数
        int daysInMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        for (int i = 1; i <= daysInMonth; i++) {
            Calendar dateCal = Calendar.getInstance();
            dateCal.set(year, month, i);
            dates.add(dateCal.getTime());
        }
        return dates;
    }
}
  1. 实现日期Adapter
    自定义RecyclerView.Adapter,绑定日期数据,处理选中状态、周末高亮:
public class CalendarDateAdapter extends RecyclerView.Adapter<CalendarDateAdapter.DateViewHolder> {
    private List<Date> mDates;
    private Date mSelectedDate;
    private OnDateSelectedListener mListener;

    public interface OnDateSelectedListener {
        void onDateSelected(Date date);
    }

    public CalendarDateAdapter(List<Date> dates, OnDateSelectedListener listener) {
        mDates = dates;
        mListener = listener;
        mSelectedDate = new Date();
    }

    @Override
    public DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_calendar_date, parent, false);
        return new DateViewHolder(view);
    }

    @Override
    public void onBindViewHolder(DateViewHolder holder, int position) {
        Date date = mDates.get(position);
        if (date == null) {
            holder.tvDate.setText("");
            holder.itemView.setEnabled(false);
            return;
        }

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        holder.tvDate.setText(String.valueOf(day));

        // 标记周末
        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
        if (dayOfWeek == Calendar.SATURDAY || dayOfWeek == Calendar.SUNDAY) {
            holder.tvDate.setTextColor(Color.parseColor("#FF4081"));
        } else {
            holder.tvDate.setTextColor(Color.parseColor("#212121"));
        }

        // 标记选中状态
        boolean isSelected = isSameDate(date, mSelectedDate);
        if (isSelected) {
            holder.itemView.setBackgroundResource(R.drawable.selected_date_bg);
        } else {
            holder.itemView.setBackgroundResource(0);
        }

        holder.itemView.setOnClickListener(v -> {
            mSelectedDate = date;
            notifyDataSetChanged();
            mListener.onDateSelected(date);
        });
    }

    private boolean isSameDate(Date date1, Date date2) {
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);
        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
                cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH) &&
                cal1.get(Calendar.DAY_OF_MONTH) == cal2.get(Calendar.DAY_OF_MONTH);
    }

    @Override
    public int getItemCount() {
        return mDates.size();
    }

    static class DateViewHolder extends RecyclerView.ViewHolder {
        TextView tvDate;

        public DateViewHolder(View itemView) {
            super(itemView);
            tvDate = itemView.findViewById(R.id.tv_date);
        }
    }
}
  1. 实现月份切换逻辑
    在Activity中处理左右按钮点击,切换月份并刷新日历数据:
public class CalendarActivity extends AppCompatActivity {
    private int mCurrentYear;
    private int mCurrentMonth;
    private TextView tvCurrentMonth;
    private RecyclerView rvCalendarDates;
    private CalendarDateAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_calendar);

        initViews();
        initCurrentDate();
        loadCalendarData();
    }

    private void initViews() {
        tvCurrentMonth = findViewById(R.id.tv_current_month);
        rvCalendarDates = findViewById(R.id.rv_calendar_dates);
        rvCalendarDates.setLayoutManager(new GridLayoutManager(this, 7));

        findViewById(R.id.btn_prev_month).setOnClickListener(v -> {
            mCurrentMonth--;
            if (mCurrentMonth < 0) {
                mCurrentMonth = 11;
                mCurrentYear--;
            }
            loadCalendarData();
        });

        findViewById(R.id.btn_next_month).setOnClickListener(v -> {
            mCurrentMonth++;
            if (mCurrentMonth > 11) {
                mCurrentMonth = 0;
                mCurrentYear++;
            }
            loadCalendarData();
        });
    }

    private void initCurrentDate() {
        Calendar calendar = Calendar.getInstance();
        mCurrentYear = calendar.get(Calendar.YEAR);
        mCurrentMonth = calendar.get(Calendar.MONTH);
    }

    private void loadCalendarData() {
        List<Date> dates = CalendarUtils.getMonthDates(mCurrentYear, mCurrentMonth);
        tvCurrentMonth.setText(mCurrentYear + "年" + (mCurrentMonth + 1) + "月");
        mAdapter = new CalendarDateAdapter(dates, date -> {
            // 处理日期选中逻辑
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA);
            Toast.makeText(this, "选中:" + sdf.format(date), Toast.LENGTH_SHORT).show();
        });
        rvCalendarDates.setAdapter(mAdapter);
    }
}

注意事项

  • 自定义实现时需处理闰年2月的天数差异,避免日期计算错误。
  • 适配不同屏幕尺寸,确保日历网格的每个单元格大小一致。
  • 第三方库版本可能更新,使用时注意查看官方文档适配最新版本。

内容的提问来源于stack exchange,提问作者Raj Subh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 10:35:29