天天看點

Android簡易自定義月曆控件實踐

      最近寫了一個簡易的自定義月曆控件,使用簡單,友善自定義,特此抛磚引玉。在網上看了一些月曆控件,複雜的月曆控件雖然特效好看,但是內建度高,不易修改與擴充。本月曆控件使用gridview編寫,可擴充性好,适合需要簡單用到月曆的項目。效果如下圖:

Android簡易自定義月曆控件實踐

      代碼如下:

import android.content.Context;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.Calendar;
import java.util.Date;

public class CustomCalendar extends LinearLayout implements View.OnClickListener {

    TextView recordLeft;
    TextView recordTitle;
    TextView recordRight;
    GridView recordGridView;

    private Context context;
    private int year;
    private int month;
    private int[][] days = new int[6][7];
    private CalendarAdapter dateAdapter;
    private Calendar calendar;
    private Calendar minCalendar;

    private OnTitleClickListener onTitleClickListener;
    private OnDateSelectedListener onDateSelectedListener;
    private OnMonthSelectedListener onMonthSelectedListener;

    public CustomCalendar(Context context) {
        super(context);
        this.context = context;
        init();
    }

    public CustomCalendar(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        init();
    }

    public CustomCalendar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        init();
    }

    private void init() {
        View view = LayoutInflater.from(context).inflate(R.layout.layout_calendar, this, true);
        recordLeft = (TextView) view.findViewById(R.id.record_left);
        recordRight = (TextView) view.findViewById(R.id.record_right);
        recordTitle = (TextView) view.findViewById(R.id.record_title);
        recordGridView = (GridView) view.findViewById(R.id.record_gridView);
        recordLeft.setOnClickListener(this);
        recordRight.setOnClickListener(this);
        recordTitle.setOnClickListener(this);

        view.setBackgroundColor(ContextCompat.getColor(context, android.R.color.white));
        calendar = Calendar.getInstance();
        year = calendar.get(Calendar.YEAR);
        month = calendar.get(Calendar.MONTH) + 1;
        days = CalendarUtils.getDayOfMonthFormat(year, month);
        dateAdapter = new CalendarAdapter(context, days, year, month);//傳入目前月的年
        String title = year + "年" + month + "月";
        recordTitle.setText(title);
        recordGridView.setAdapter(dateAdapter);
        dateAdapter.changeListHeight(recordGridView, 7);
        recordGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                if (i < 7 && dateAdapter.getItem(i) > 20) {
                } else if (i > 20 && dateAdapter.getItem(i) < 15) {
                } else {
                    calendar.set(year, month - 1, dateAdapter.getItem(i));
                    dateAdapter.setSelectDate(calendar.getTime());
                    if (onDateSelectedListener != null) onDateSelectedListener.onDateSelected(calendar.getTime());
                }
            }
        });
    }

    public void setOnTitleClickListener(OnTitleClickListener l) {
        this.onTitleClickListener = l;
    }

    public void setOnDateSelectedListener(OnDateSelectedListener l) {
        this.onDateSelectedListener = l;
    }

    public void setOnMonthSelectedListener(OnMonthSelectedListener l) {
        this.onMonthSelectedListener = l;
    }

    public void preMonth() {
        if (month == 1) {
            month = 12;
            year--;
        } else {
            month--;
        }
        updateMonth();
    }

    private void updateMonth() {
        days = CalendarUtils.getDayOfMonthFormat(year, month);
        dateAdapter = new CalendarAdapter(context, days, year, month);
        dateAdapter.setSelectDate(calendar.getTime());
        recordGridView.setAdapter(dateAdapter);
        dateAdapter.changeListHeight(recordGridView, 7);
        String title = year + "年" + month + "月";
        recordTitle.setText(title);
        if (minCalendar != null) {
            if (minCalendar.get(Calendar.YEAR) >= year && minCalendar.get(Calendar.MONTH) + 1 >= month) {
                setPreMonthEnable(false);
            } else setPreMonthEnable(true);
        }
        Calendar calendar1 = Calendar.getInstance();
        calendar1.set(Calendar.YEAR, year);
        calendar1.set(Calendar.MONTH, month - 1);
        if (onMonthSelectedListener != null) {
            onMonthSelectedListener.onMonthSelected(calendar1.getTime());
        }
    }

    public void nextMonth() {
        if (month == 12) {
            month = 1;
            year++;
        } else {
            month++;
        }
        updateMonth();
    }

    public void onClick(View view) {
        int i = view.getId();
        if (i == R.id.record_left) {
            preMonth();

        } else if (i == R.id.record_title) {
            if (onTitleClickListener != null) onTitleClickListener.onClick();

        } else if (i == R.id.record_right) {
            nextMonth();

        }
    }

    public void showMonth(int year, int month) {
        this.year = year;
        this.month = month;
        updateMonth();
    }

    public int getYear() {
        return year;
    }

    public int getMonth() {
        return month;
    }

    public void setPreMonthEnable(boolean enable) {
        recordLeft.setEnabled(enable);
        recordLeft.setTextColor(ContextCompat.getColor(context, enable ? android.R.color.holo_blue_light : android.R.color.darker_gray));
    }

    public void setMinCalendar(Calendar calendar) {
        this.minCalendar = calendar;
        if (minCalendar.get(Calendar.YEAR) >= year && minCalendar.get(Calendar.MONTH) + 1 >= month) {
            setPreMonthEnable(false);
        } else setPreMonthEnable(true);
    }

    public void setMarkingDays(int[] markingDays) {
        dateAdapter.setMarkingDays(markingDays);
    }

    public void setSelectedDay(Date date) {
        dateAdapter.setSelectDate(date);
    }

    public interface OnTitleClickListener {
        void onClick();
    }

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

    public interface OnMonthSelectedListener {
        void onMonthSelected(Date date);
    }

}
           

      這是一個将android原生控件組合起來的自定義控件,繼承自LinearLayout,上方是月曆标題和按鈕,下方是一個Gridview。沒有自定義屬性,可以直接在代碼裡更改樣式或者根據需要擴充。點選上一個月或者下一個月可切換,點選月曆中某一天可以選中,同時onDateSelectedListener會執行回調方法,可以設定它以完成一些特定的業務需求。setMinCalendar()方法可以設定一個最小的日期,顯示到最小日期時上一個月将不可點選。setMarkingDays()可以設定複數個被辨別的日期,但要注意的是切換月份時辨別會被清空,需重新設定。setSelectedDay()可以設定月曆的初始選中日期。還有點選月曆标題的回調和切換月份的回調,可以完成相應的業務需求。

      關于月曆的顯示規則可到CalendarAdapter裡進行修改,月曆日期的計算則包含在CalendarUtils裡,整個月曆控件隻包含三個類和幾個資源檔案,層次簡單,便于拓展。我已将完整代碼打包成可以引用成lib的子產品,內建到項目中即可使用。引用示例:

<com.example.simplecalendar.CustomCalendar
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
           

      下載下傳完整代碼請移步至: 點選打開連結