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);
   }
  });
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。

代码技巧

转载请关注公众号:代码技巧 回复:授权

本文链接地址:https://www.oudahe.com/p/54524/