最近用到的一个日历控件,记录下来造福人类, 效果如图

Android 底部弹窗 日历选择器 android日历展示_日历

布局文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:visibility="visible">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/bg_gray"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/prevMonth"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_weight="1"
            android:src="@drawable/prev_month" />

        <TextView
            android:id="@+id/currentMonth"
            android:layout_width="0dp"
            android:layout_height="35dp"
            android:layout_weight="3"
            android:gravity="center"
            android:text="2016年9月"
            android:textColor="@color/black"
            android:textSize="18sp" />

        <ImageView
            android:id="@+id/nextMonth"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_weight="1"
            android:src="@drawable/next_month" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="20dp"
        android:background="@color/bg_gray">

        <TextView
            style="@style/weekName"
            android:text="周日"
            android:textColor="@color/green" />

        <TextView
            style="@style/weekName"
            android:text="周一" />

        <TextView
            style="@style/weekName"
            android:text="周二" />

        <TextView
            style="@style/weekName"
            android:text="周三" />

        <TextView
            style="@style/weekName"
            android:text="周四" />

        <TextView
            style="@style/weekName"
            android:text="周五" />

        <TextView
            style="@style/weekName"
            android:text="周六"
            android:textColor="@color/green" />
    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/line" />

    <ViewFlipper
        android:id="@+id/flipper"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="@color/line"
        android:padding="1dp" />

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/line" />

</LinearLayout>

日历 PopCalendar.class的代码

public class PopCalendar extends PopupWindow implements View.OnClickListener {
    private View contentView;
    private Context mContext;
    private WindowManager windowManager;
    private GestureDetector gestureDetector = null;
    private CalendarAdapter calV = null;
    private ViewFlipper flipper = null;
    private GridView gvCalendar = null;
    private static int jumpMonth = 0; // 每次滑动,增加或减去一个月,默认为0(即显示当前月)
    private static int jumpYear = 0; // 滑动跨越一年,则增加或者减去一年,默认为0(即当前年)
    private int yearC = 0;
    private int monthC = 0;
    private int dayC = 0;
    private String currentDate = "";
    //当前年月,显示在日历顶端
    private TextView currentMonthTv;
    //上个月,下个月的图标
    private ImageView prevMonthIv;
    private ImageView nextMonthIv;

    public PopCalendar(final Activity context) {
        this.mContext = context;
        this.windowManager = context.getWindowManager();;
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-M-d");
        currentDate = sdf.format(date); // 当期日期
        yearC = Integer.parseInt(currentDate.split("-")[0]);
        monthC = Integer.parseInt(currentDate.split("-")[1]);
        dayC = Integer.parseInt(currentDate.split("-")[2]);
        jumpMonth = 0;
        jumpYear = 0;

        //设置PopWindow的属性
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        contentView = inflater.inflate(R.layout.pop_calendar, null);
        this.setContentView(contentView);
        this.setWidth(WindowManager.LayoutParams.FILL_PARENT);
        this.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
        this.setFocusable(true);
        this.setOutsideTouchable(true);
        this.update();
        ColorDrawable dw = new ColorDrawable(0000000000);
        this.setBackgroundDrawable(dw);

        currentMonthTv = (TextView) contentView.findViewById(R.id.currentMonth);
        prevMonthIv = (ImageView) contentView.findViewById(R.id.prevMonth);
        nextMonthIv = (ImageView) contentView.findViewById(R.id.nextMonth);
        setListener();

        gestureDetector = new GestureDetector(mContext, new MyGestureListener());
        flipper = (ViewFlipper) contentView.findViewById(R.id.flipper);
        flipper.removeAllViews();
        calV = new CalendarAdapter(mContext, mContext.getResources(), jumpMonth, jumpYear, yearC, monthC, dayC);
        addGridView();
        gvCalendar.setAdapter(calV);
        flipper.addView(gvCalendar, 0);
        addTextToTopTextView(currentMonthTv);
    }

    private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            if (e1.getX() - e2.getX() > 120) {
                // 像左滑动
                enterNextMonth();
                return true;
            } else if (e1.getX() - e2.getX() < -120) {
                // 向右滑动
                enterPrevMonth();
                return true;
            }
            return false;
        }
    }

    /**
     * 移动到下一个月
     *
     */
    private void enterNextMonth() {
        addGridView(); // 添加一个gridView
        jumpMonth++; // 下一个月

        calV = new CalendarAdapter(mContext, mContext.getResources(), jumpMonth, jumpYear, yearC, monthC, dayC);
        gvCalendar.setAdapter(calV);
        addTextToTopTextView(currentMonthTv); // 移动到下一月后,将当月显示在头标题中
        flipper.addView(gvCalendar, 1);
        flipper.setInAnimation(AnimationUtils.loadAnimation(mContext, R.anim.push_left_in));
        flipper.setOutAnimation(AnimationUtils.loadAnimation(mContext, R.anim.push_left_out));
        flipper.showNext();
        flipper.removeViewAt(0);
    }

    /**
     * 移动到上一个月
     *
     */
    private void enterPrevMonth() {
        addGridView(); // 添加一个gridView
        jumpMonth--; // 上一个月

        calV = new CalendarAdapter(mContext, mContext.getResources(), jumpMonth, jumpYear, yearC, monthC, dayC);
        gvCalendar.setAdapter(calV);
        addTextToTopTextView(currentMonthTv); // 移动到上一月后,将当月显示在头标题中
        flipper.addView(gvCalendar, 1);

        flipper.setInAnimation(AnimationUtils.loadAnimation(mContext, R.anim.push_right_in));
        flipper.setOutAnimation(AnimationUtils.loadAnimation(mContext, R.anim.push_right_out));
        flipper.showPrevious();
        flipper.removeViewAt(0);
    }

    /**
     * 添加头部的年份 闰哪月等信息
     * @param view
     */
    public void addTextToTopTextView(TextView view) {
        StringBuffer textDate = new StringBuffer();
        textDate.append(calV.getShowYear()).append("年").append(calV.getShowMonth()).append("月").append("\t");
        view.setText(textDate);
    }

    private void addGridView() {
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.MATCH_PARENT);
        // 取得屏幕的宽度和高度
        Display display = windowManager.getDefaultDisplay();
        int Width = display.getWidth();
        int Height = display.getHeight();
        gvCalendar = new GridView(mContext);
        gvCalendar.setNumColumns(7);
        gvCalendar.setColumnWidth(40);
        // gridView.setStretchMode(GridView.STRETCH_COLUMN_WIDTH);
        if (Width == 720 && Height == 1280) {
            gvCalendar.setColumnWidth(40);
        }
        gvCalendar.setGravity(Gravity.CENTER_VERTICAL);
        gvCalendar.setSelector(new ColorDrawable(Color.TRANSPARENT));
        // 去除gridView边框
        gvCalendar.setVerticalSpacing(2);
        gvCalendar.setHorizontalSpacing(2);
        gvCalendar.setOnTouchListener(new View.OnTouchListener() {
            // 将gridView中的触摸事件回传给gestureDetector
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                return PopCalendar.this.gestureDetector.onTouchEvent(event);
            }
        });

        gvCalendar.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
                // TODO Auto-generated method stub
                // 点击任何一个item,得到这个item的日期(排除点击的是周日到周六(点击不响应))
                int startPosition = calV.getStartPosition();
                int endPosition = calV.getEndPosition();
                if (startPosition <= position + 7 && position <= endPosition - 7) {
                    String scheduleDay = calV.getDateByClickItem(position); // 这一天的阳历
                    String scheduleYear = calV.getShowYear();
                    String scheduleMonth = calV.getShowMonth();
                    Toast.makeText(mContext, scheduleYear + "-" + scheduleMonth + "-" + scheduleDay, Toast.LENGTH_SHORT).show();
                }
            }
        });
        gvCalendar.setLayoutParams(params);
    }

    private void setListener() {
        prevMonthIv.setOnClickListener(this);
        nextMonthIv.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        switch (v.getId()) {
            case R.id.nextMonth: // 下一个月
                enterNextMonth();
                break;
            case R.id.prevMonth: // 上一个月
                enterPrevMonth();
                break;
            default:
                break;
        }
    }

    /**
     * 显示popWindow
     */
    public void showPopupWindow(View parent) {
        if (!this.isShowing()) {
            // 以下拉方式显示popupwindow
            this.showAsDropDown(parent);
        } else {
            this.dismiss();
        }
    }
}

日历的内容是一个GridView,可以自定义类似签到效果的图标

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@color/bg_gray" >

    <TextView
        android:id="@+id/tv_text"
        android:layout_width="fill_parent"
        android:layout_height="30dp"
        android:gravity="center" />

    <ImageView
        android:layout_width="15dp"
        android:layout_height="15dp"
        android:visibility="invisible"
        android:layout_alignParentBottom="true"
        android:background="@drawable/pen"
        android:layout_alignParentEnd="true"
        android:id="@+id/iv_pen" />


</RelativeLayout>

日历的adapter

public class CalendarAdapter extends BaseAdapter {
    private boolean isLeapYear = false; // 是否为闰年
    private int daysOfMonth = 0; // 某月的天数
    private int dayOfWeek = 0; // 具体某一天是星期几
    private int lastDaysOfMonth = 0; // 上一个月的总天数
    private Context context;
    private String[] dayNumber = new String[42]; // 一个gridview中的日期存入此数组中
    private SpecialCalendar sc = null;
    private Resources res = null;

    private String currentYear = "";
    private String currentMonth = "";

    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-M-d");
    private int currentFlag = -1; // 用于标记当天

    private String showYear = ""; // 用于在头部显示的年份
    private String showMonth = ""; // 用于在头部显示的月份

    // 系统当前时间
    private String sysDate = "";
    private String sys_year = "";
    private String sys_month = "";
    private String sys_day = "";
    public CalendarAdapter() {
        Date date = new Date();
        sysDate = sdf.format(date); // 当期日期
        sys_year = sysDate.split("-")[0];
        sys_month = sysDate.split("-")[1];
        sys_day = sysDate.split("-")[2];
    }

    public CalendarAdapter(Context context, Resources rs, int jumpMonth, int jumpYear, int year_c, int month_c, int day_c) {
        this();
        this.context = context;
        sc = new SpecialCalendar();
        this.res = rs;

        int stepYear = year_c + jumpYear;
        int stepMonth = month_c + jumpMonth;
        if (stepMonth > 0) {
            // 往下一个月滑动
            if (stepMonth % 12 == 0) {
                stepYear = year_c + stepMonth / 12 - 1;
                stepMonth = 12;
            } else {
                stepYear = year_c + stepMonth / 12;
                stepMonth = stepMonth % 12;
            }
        } else {
            // 往上一个月滑动
            stepYear = year_c - 1 + stepMonth / 12;
            stepMonth = stepMonth % 12 + 12;
            if (stepMonth % 12 == 0) {

            }
        }

        currentYear = String.valueOf(stepYear); // 得到当前的年份
        currentMonth = String.valueOf(stepMonth); // 得到本月
        // (jumpMonth为滑动的次数,每滑动一次就增加一月或减一月)

        getCalendar(Integer.parseInt(currentYear), Integer.parseInt(currentMonth));

    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return dayNumber.length;
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return position;
    }

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if (convertView == null) {
            convertView = LayoutInflater.from(context).inflate(R.layout.calendar_item, null);
        }
        TextView textView = (TextView) convertView.findViewById(R.id.tv_text);
        ImageView ivPen = (ImageView) convertView.findViewById(R.id.iv_pen);
        String d = dayNumber[position];

        SpannableString sp = new SpannableString(d);
        sp.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, d.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        sp.setSpan(new RelativeSizeSpan(1.2f), 0, d.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        textView.setText(sp);
        textView.setTextColor(Color.BLACK);// 字体设黑
        if (position % 7 == 0 || position % 7 == 6) {
            // 当前月信息显示
            textView.setTextColor(res.getColor(R.color.green));// 周末字体设绿色
        }

        if (position >= dayOfWeek && position < daysOfMonth + dayOfWeek
                && (Integer.parseInt(sys_month) >= Integer.parseInt(currentMonth)&&Integer.parseInt(sys_year)==Integer.parseInt(currentYear)
                ||Integer.parseInt(sys_year)> Integer.parseInt(currentYear))) {
            // 当前月信息显示
            int a[] = {2, 6, 29};//每个月不标记的天数
            for (int i = 0; i < a.length; i++) {
                if (position == a[i]+dayOfWeek-1) {
                    textView.setBackgroundColor(res.getColor(R.color.yellow));//为写日记日期填充黄色
                    ivPen.setVisibility(View.INVISIBLE);
                    break;
                } else {
                    ivPen.setVisibility(View.VISIBLE);
                }
            }
        } else if (position < dayOfWeek || position >= daysOfMonth + dayOfWeek) {
            textView.setTextColor(res.getColor(R.color.bg_gray));
        }

        if (Integer.parseInt(sys_year)==Integer.parseInt(currentYear)
                &&Integer.parseInt(sys_month) == Integer.parseInt(currentMonth)&& currentFlag < position) {
            // 设置本月当天之后的背景
            textView.setBackgroundColor(res.getColor(R.color.bg_gray));//全部填充灰色
            ivPen.setVisibility(View.INVISIBLE);
        }

        if (currentFlag == position) {
            //设置当天的背景
            textView.setBackgroundColor(res.getColor(R.color.blue));
            textView.setTextColor(Color.WHITE);
        }
        return convertView;
    }

    // 得到某年的某月的天数且这月的第一天是星期几
    public void getCalendar(int year, int month) {
        isLeapYear = sc.isLeapYear(year); // 是否为闰年
        daysOfMonth = sc.getDaysOfMonth(isLeapYear, month); // 某月的总天数
        dayOfWeek = sc.getWeekdayOfMonth(year, month); // 某月第一天为星期几
        lastDaysOfMonth = sc.getDaysOfMonth(isLeapYear, month - 1); // 上一个月的总天数
        getWeek(year, month);
    }

    // 将一个月中的每一天的值添加入数组dayNuber中
    private void getWeek(int year, int month) {
        int j = 1;
        // 得到当前月的所有日程日期(这些日期需要标记)
        for (int i = 0; i < dayNumber.length; i++) {
            if (i < dayOfWeek) { // 前一个月
                int temp = lastDaysOfMonth - dayOfWeek + 1;
                dayNumber[i] = (temp + i) + "" ;
            } else if (i < daysOfMonth + dayOfWeek) { // 本月
                String day = String.valueOf(i - dayOfWeek + 1); // 得到的日期
                dayNumber[i] = i - dayOfWeek + 1 + "";
                // 对于当前月才去标记当前日期
                if (sys_year.equals(String.valueOf(year)) && sys_month.equals(String.valueOf(month)) && sys_day.equals(day)) {
                    // 标记当前日期
                    currentFlag = i;
                }
                setShowYear(String.valueOf(year));
                setShowMonth(String.valueOf(month));
            } else { // 下一个月
                dayNumber[i] = j + "";
                j++;
            }
        }
    }

    /**
     * 点击每一个item时返回item中的日期
     * @param position
     * @return
     */
    public String getDateByClickItem(int position) {
        return dayNumber[position];
    }

    /**
     * 在点击gridView时,得到这个月中第一天的位置
     * @return
     */
    public int getStartPosition() {
        return dayOfWeek + 7;
    }

    /**
     * 在点击gridView时,得到这个月中最后一天的位置
     * @return
     */
    public int getEndPosition() {
        return (dayOfWeek + daysOfMonth + 7) - 1;
    }

    public String getShowYear() {
        return showYear;
    }

    public void setShowYear(String showYear) {
        this.showYear = showYear;
    }

    public String getShowMonth() {
        return showMonth;
    }

    public void setShowMonth(String showMonth) {
        this.showMonth = showMonth;
    }
}

在MainActivity点击显示日历,可以指定PopWindow在哪一个控件的下方出现

public class MainActivity extends AppCompatActivity {

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

        final Button button = (Button)findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                PopCalendar popCalendar = new PopCalendar(MainActivity.this);
                popCalendar.showPopupWindow(button);
            }
        });
    }
}