From 8a6b631969064397c486ab0a55f8499205818c16 Mon Sep 17 00:00:00 2001 From: harvey Date: Wed, 7 Jul 2021 10:40:21 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=97=A5=E5=8E=86?= =?UTF-8?q?=E7=9A=84=E6=8E=A7=E5=88=B6=E7=B1=BB=E5=92=8C=E8=A7=86=E5=9B=BE?= =?UTF-8?q?=E7=B1=BB=E5=B9=B6=E5=9C=A8MainActivity=E4=B8=AD=E8=B0=83?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../idealist/calendarview/CalendarAttr.java | 1 - .../idealist/calendarview/CalendarPager.java | 152 ++++++++ .../calendarview/CalendarPagerBehavior.java | 182 +++++++++ .../idealist/calendarview/CalendarUtils.java | 363 ++++++++++++++++++ .../idealist/calendarview/CalendarView.java | 272 +++++++++++++ .../calendarview/CalendarViewAdapter.java | 188 +++++++++ .../interf/OnSelectDateListener.java | 7 + src/app/build.gradle | 1 + .../showme/myapplication/MainActivity.java | 101 +++++ src/app/src/main/res/layout/activity_main.xml | 146 ++++++- src/app/src/main/res/values/styles.xml | 17 + 11 files changed, 1418 insertions(+), 12 deletions(-) create mode 100644 src/CalendarView/src/main/java/com/idealist/calendarview/CalendarPager.java create mode 100644 src/CalendarView/src/main/java/com/idealist/calendarview/CalendarPagerBehavior.java create mode 100644 src/CalendarView/src/main/java/com/idealist/calendarview/CalendarUtils.java create mode 100644 src/CalendarView/src/main/java/com/idealist/calendarview/CalendarView.java create mode 100644 src/CalendarView/src/main/java/com/idealist/calendarview/CalendarViewAdapter.java create mode 100644 src/CalendarView/src/main/java/com/idealist/calendarview/interf/OnSelectDateListener.java create mode 100644 src/app/src/main/res/values/styles.xml diff --git a/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarAttr.java b/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarAttr.java index d38df74..f8f9e04 100644 --- a/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarAttr.java +++ b/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarAttr.java @@ -1,6 +1,5 @@ package com.idealist.calendarview; - public class CalendarAttr { static int RecTop; diff --git a/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarPager.java b/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarPager.java new file mode 100644 index 0000000..7e8d41a --- /dev/null +++ b/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarPager.java @@ -0,0 +1,152 @@ +package com.idealist.calendarview; + + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; + +@CoordinatorLayout.DefaultBehavior(CalendarPagerBehavior.class) +public class CalendarPager extends ViewPager { + + private CalendarAttr attr; + + private OnPageChangeListener mOnPageChangeListener; + + public static int CURRENT_DAY_INDEX = 1000; + + private int mCurrPosition = CURRENT_DAY_INDEX; + + private int pageScrollState = ViewPager.SCROLL_STATE_IDLE; + + public CalendarPager(Context context) { + super(context, null); + } + + public CalendarPager(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + initPager(); + } + + private void initPager() { + ViewPager.OnPageChangeListener pageChangeListener = new ViewPager.OnPageChangeListener() { + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + } + + @Override + public void onPageSelected(int position) { + mCurrPosition = position; + if (mOnPageChangeListener != null) { + CalendarViewAdapter adapter = (CalendarViewAdapter) getAdapter(); + assert adapter != null; + adapter.updateViewByScrollHorizontally(position); + mOnPageChangeListener.onPageSelected(position); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + pageScrollState = state; + } + }; + addOnPageChangeListener(pageChangeListener); + } + + public void setAttr(CalendarAttr attr) { + this.attr = attr; + } + + @Override + public void setAdapter(@Nullable PagerAdapter adapter) { + super.setAdapter(adapter); + setCurrentItem(1000); + mCurrPosition = 1000; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (attr != null && attr.getCalendarType() == State.VIEW_FULL) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + return; + } + int height = 0xffffff; + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + child.measure(widthMeasureSpec, + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + int h = child.getMeasuredHeight(); + height = Math.min(h, height); + } + heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + public void setmOnPageChangeListener(OnPageChangeListener mOnPageChangeListener) { + this.mOnPageChangeListener = mOnPageChangeListener; + } + + public void selectOtherMonth(int offset) { + CalendarViewAdapter adapter = (CalendarViewAdapter) getAdapter(); + adapter.saveSelectedDate(); + adapter.updateDayInViewState(mCurrPosition + offset); + setCurrentItem(mCurrPosition + offset); + } + + public int getViewHeight() { + return attr.getViewHeight(); + } + + public int getItemHeight() { + return attr.getItemHeight(); + } + + public int getScrollLevel() { + return attr.getScrollLevel(); + } + + public void setScrollLevel(int scrollLevel) { + attr.setScrollLevel(scrollLevel); + } + + public int getPageScrollState() { + return pageScrollState; + } + + public void setPageScrollState(int pageScrollState) { + this.pageScrollState = pageScrollState; + } + + public CalendarAttr getAttr() { + return attr; + } + + public int getmCurrPosition() { + return mCurrPosition; + } + + /** + * 月视图到周视图可向上滑动距离距离 + *a + * @return 距离 + */ + public int getTopMovableDistance() { + CalendarViewAdapter adapter = (CalendarViewAdapter) getAdapter(); + assert adapter != null; + return attr.getItemHeight() * (CalendarUtils.getWeekOfMonth(adapter.getSelectDay()) - 1); + } + + public interface OnPageChangeListener { + void onPageScrolled(int position, float positionOffset, int positionOffsetPixels); + + void onPageSelected(int position); + + void onPageScrollStateChanged(int state); + } +} diff --git a/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarPagerBehavior.java b/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarPagerBehavior.java new file mode 100644 index 0000000..7c3525c --- /dev/null +++ b/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarPagerBehavior.java @@ -0,0 +1,182 @@ +package com.idealist.calendarview; + +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import androidx.recyclerview.widget.RecyclerView; + +import static android.content.ContentValues.TAG; + +public class CalendarPagerBehavior extends CoordinatorLayout.Behavior { + private int mTop = 0; + private int touchSlop = 1; + + @Override + public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull CalendarPager child, @NonNull View dependency) { + return dependency instanceof RecyclerView; + } + + @Override + public boolean onLayoutChild(@NonNull CoordinatorLayout parent, @NonNull CalendarPager child, int layoutDirection) { + parent.onLayoutChild(child, layoutDirection); + child.offsetTopAndBottom(mTop); + return true; + } + + private int confirm = 0; + private float downX, downY, lastY, lastTop; + private boolean isVerticalScroll; + private boolean directionUpa; + + @Override + public boolean onTouchEvent(@NonNull CoordinatorLayout parent, @NonNull CalendarPager child, @NonNull MotionEvent ev) { + if (downY > lastTop) { + return false; + } + Log.i(TAG, "onTouchEvent: onTouchEvent"); + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + break; + case MotionEvent.ACTION_MOVE: + if (isVerticalScroll) { + directionUpa = ev.getY() < lastY; + if (child.getScrollLevel() == State.LEVEL_TOP) { + CalendarAttr.setRecTop(State.DEFAULT_ITEM_HEIGHT + (int) (ev.getY() - downY)); + CalendarUtils.scroll(parent.getChildAt(1), (int) (lastY - ev.getY()), + State.DEFAULT_ITEM_HEIGHT, State.DEFAULT_ITEM_HEIGHT * 6); + } else if (child.getScrollLevel() == State.LEVEL_MEDIUM) { + CalendarAttr.setRecTop(State.DEFAULT_ITEM_HEIGHT * 6 + (int) (ev.getY() - downY)); + if (directionUpa) { + CalendarUtils.scroll(parent.getChildAt(1), (int) (lastY - ev.getY()), + State.DEFAULT_ITEM_HEIGHT, State.DEFAULT_ITEM_HEIGHT * 6); + } else { + CalendarUtils.scroll(parent.getChildAt(1), (int) (lastY - ev.getY()), + State.DEFAULT_ITEM_HEIGHT * 6, State.DEFAULT_ITEM_HEIGHT_FULL * 6); + } + } else { + CalendarAttr.setRecTop(child.getViewHeight() + (int) (ev.getY() - downY)); + CalendarUtils.scroll(parent.getChildAt(1), (int) (lastY - ev.getY()), + State.DEFAULT_ITEM_HEIGHT * 6, State.DEFAULT_ITEM_HEIGHT_FULL * 6); + } + lastY = ev.getY(); + return true; + } + break; + case MotionEvent.ACTION_UP: + if (isVerticalScroll) { + CalendarViewAdapter adapter = (CalendarViewAdapter) child.getAdapter(); + if (adapter != null) { + if (directionUpa) { + if (adapter.getCalendarType() == State.VIEW_MONTH) { + child.setScrollLevel(State.LEVEL_TOP); + adapter.changeCalendarType(State.VIEW_WEEK); + CalendarUtils.scrollTo(parent, (RecyclerView) parent.getChildAt(1), + State.DEFAULT_ITEM_HEIGHT, 300); + } else if (adapter.getCalendarType() == State.VIEW_FULL) { + child.setScrollLevel(State.LEVEL_MEDIUM); + adapter.changeCalendarType(State.VIEW_MONTH); + CalendarUtils.scrollTo(parent, (RecyclerView) parent.getChildAt(1), + State.DEFAULT_ITEM_HEIGHT * 6, 300); + } + } else { + if (adapter.getCalendarType() == State.VIEW_WEEK) { + child.setScrollLevel(State.LEVEL_MEDIUM); + adapter.changeCalendarType(State.VIEW_MONTH); + CalendarUtils.scrollTo(parent, (RecyclerView) parent.getChildAt(1), + State.DEFAULT_ITEM_HEIGHT * 6, 300); + } else if (adapter.getCalendarType() == State.VIEW_MONTH) { + child.setScrollLevel(State.LEVEL_BOTTOM); + adapter.changeCalendarType(State.VIEW_FULL); + CalendarUtils.scrollTo(parent, (RecyclerView) parent.getChildAt(1), + State.DEFAULT_ITEM_HEIGHT_FULL * 6, 300); + } + } + } + isVerticalScroll = false; + return true; + } + break; + } + isVerticalScroll = false; + return false; + } + + @Override + public boolean onInterceptTouchEvent(@NonNull CoordinatorLayout parent, @NonNull CalendarPager child, @NonNull MotionEvent ev) { + Log.i(TAG, "onInterceptTouchEvent: Try to intercept!" + isVerticalScroll); + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + downX = ev.getX(); + downY = ev.getY(); + lastTop = CalendarAttr.getRecTop(); + lastY = downY; + break; + case MotionEvent.ACTION_MOVE: + if (downY > lastTop) { + return false; + } + if (Math.abs(ev.getY() - downY) > 25 && Math.abs(ev.getX() - downX) <= 25 && !isVerticalScroll) { + isVerticalScroll = true; + return true; + } + break; + case MotionEvent.ACTION_UP: + if (isVerticalScroll) { + isVerticalScroll = false; + return true; + } + break; + } + return isVerticalScroll; + } + + private int dependencyViewTop = -1; + + @Override + public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull CalendarPager child, @NonNull View dependency) { + Log.i(TAG, "onDependentViewChanged: !"); + CalendarViewAdapter adapter = (CalendarViewAdapter) child.getAdapter(); + + if (dependencyViewTop != -1) { + int dy = dependency.getTop() - dependencyViewTop; + int top = child.getTop(); + + if (dy > touchSlop) { + assert adapter != null; + adapter.changeCalendarType(State.VIEW_MONTH); + } else if (dy < -touchSlop) { + assert adapter != null; + adapter.changeCalendarType(State.VIEW_WEEK); + } + + if (dy > -top) + dy = -top; + + if (dy < -top - child.getTopMovableDistance()) { + dy = -top - child.getTopMovableDistance(); + } + + child.offsetTopAndBottom(dy); +// adapter.changeCalendarType(State.VIEW_WEEK); + } + + if (dependencyViewTop > child.getItemHeight() - 24 + && dependencyViewTop < child.getItemHeight() + 24 + && mTop > -touchSlop - child.getTopMovableDistance() + && mTop < touchSlop - child.getTopMovableDistance()) { + child.setScrollLevel(State.LEVEL_TOP); + adapter.changeCalendarType(State.VIEW_WEEK); + } + if (dependencyViewTop > child.getViewHeight() - 24 + && dependencyViewTop < child.getViewHeight() + 24 + && mTop < touchSlop + && mTop > -touchSlop) { + child.setScrollLevel(State.LEVEL_MEDIUM); + adapter.changeCalendarType(State.VIEW_MONTH); + } + return true; + } +} diff --git a/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarUtils.java b/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarUtils.java new file mode 100644 index 0000000..cc11d54 --- /dev/null +++ b/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarUtils.java @@ -0,0 +1,363 @@ +package com.idealist.calendarview; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.os.SystemClock; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.widget.Scroller; +import android.database.sqlite.SQLiteDatabase; +import android.database.Cursor; + +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.RecyclerView; + +//import com.diary.database.utils.ScheduleSQLUtils; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + +import static android.content.ContentValues.TAG; + +public class CalendarUtils { + + + /** + * 判断某年是否是闰年 + * + * @param year year + * @return 是否是闰年 + */ + public static boolean isLeapYear(int year) { + return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); + } + + /** + * 获取某个月份的天数 + * + * @param year 年 + * @param month 月 + * @return 对应月份天数 + */ + public static int getDayNumOfMonth(int year, int month) { + int res = 31; + switch (month) { + case 4: + case 6: + case 9: + case 11: + res = 30; + break; + case 2: + if (isLeapYear(year)) + res = 29; + else + res = 28; + break; + default: + break; + } + return res; + } + + /** + * 获得某年某月某天为星期几 + * 0 - 6 日 - 六 + * + * @param year 年 + * @param month 月 + * @return 星期 + */ + private static int getDayOfWeek(int year, int month, int day) { + Calendar date = Calendar.getInstance(); + date.set(year, month - 1, day, 12, 0); + return date.get(Calendar.DAY_OF_WEEK) - 1; + } + + /** + * 为月视图生成 Items + * + * @param year year + * @param month month + * @return List + */ + public static List generateItemForMonthView(int year, int month) { + List dates = new ArrayList<>(); + + CalendarDay currDay = getCurrDay(); + int daysNumOfMonth = getDayNumOfMonth(year, month); + int preDiff = getDayOfWeek(year, month, 1); + + int preYear = year; + int nextYear = year; + int preMonth = month - 1; + int nextMonth = month + 1; + + if (month == 1) { + preYear = year - 1; + preMonth = 12; + } else if (month == 12) { + nextYear = year + 1; + nextMonth = 1; + } + + int dayIndex = 1; + for (int i = 1; i <= 42; ++i) { + CalendarDay date = new CalendarDay(); + if (i <= preDiff) { + date.setYear(preYear); + date.setMonth(preMonth); + date.setDay(getDayNumOfMonth(preYear, preMonth) - preDiff + i); + date.setMonthState(State.DAY_PAST_MONTH); + } else if (i > preDiff + daysNumOfMonth) { + date.setYear(nextYear); + date.setMonth(nextMonth); + date.setDay(dayIndex++); + date.setMonthState(State.DAY_NEXT_MONTH); + } else { + date.setYear(year); + date.setMonth(month); + date.setDay(i - preDiff); + date.setMonthState(State.DAY_CURR_MONTH); + date.setCurrDay(date.equals(currDay)); + } + dates.add(date); + if (date.equals(CalendarViewAdapter.loadSelectedDate())) { + date.setSelectState(State.DAY_SELECT); + } +// if (ScheduleSQLUtils.isScheduleMarked(date.toString())) { +// date.setMarkSchedule(true); +// } + } + return dates; + } + + /** + * 为周视图生成 Items + * + * @param year int 年 + * @param month int 月 + * @param day int 天 + * @return List Items + */ + public static List generateItemForWeekView(int year, int month, int day) { + List dates = new ArrayList<>(); + CalendarDay currDay = getCurrDay(); + int dayOfWeekOfCurr = getDayOfWeek(year, month, day); + int dayNumOfCurrMonth = getDayNumOfMonth(year, month); + + int setYear = year; + int setMonth = month; + if (day - dayOfWeekOfCurr <= 0) { + setMonth = month - 1; + if (month == 1) { + setYear = year - 1; + setMonth = 12; + } + } else if (day + (6 - dayOfWeekOfCurr) > dayNumOfCurrMonth) { + setMonth = month + 1; + if (month == 12) { + setYear = year + 1; + setMonth = 1; + } + } + + for (int i = 0; i < 7; ++i) { + CalendarDay date = new CalendarDay(); + + if (day - dayOfWeekOfCurr + i <= 0) { + date.setYear(setYear); + date.setMonth(setMonth); + date.setDay(day - dayOfWeekOfCurr + i + getDayNumOfMonth(setYear, setMonth)); + date.setMonthState(State.DAY_PAST_MONTH); + } else if (day - dayOfWeekOfCurr + i > dayNumOfCurrMonth) { + date.setYear(setYear); + date.setMonth(setMonth); + date.setDay(day - dayOfWeekOfCurr + i - dayNumOfCurrMonth); + date.setMonthState(State.DAY_NEXT_MONTH); + } else { + date.setYear(year); + date.setMonth(month); + date.setDay(day - dayOfWeekOfCurr + i); + date.setMonthState(State.DAY_CURR_MONTH); + date.setCurrDay(date.equals(currDay)); + } + dates.add(date); + if (date.equals(CalendarViewAdapter.loadSelectedDate())) { + date.setSelectState(State.DAY_SELECT); + } +// if (ScheduleSQLUtils.isScheduleMarked(date.toString())) { +// date.setMarkSchedule(true); +// } + } + return dates; + } + + + /** + * 获得当前日期 + * + * @return 日期 + */ + public static CalendarDay getCurrDay() { + CalendarDay day = new CalendarDay(); + Calendar date = Calendar.getInstance(); + day.setDay(date.get(Calendar.DATE)); + day.setMonth(date.get(Calendar.MONTH) + 1); + day.setYear(date.get(Calendar.YEAR)); + day.setCurrDay(true); +// Log.e("util", day.toString()); + return day; + } + + /** + * 获得对应日期周视图前后的周视图日历项 + * + * @param day CalendarDay 当前日历项 + * @param offset int 前后周视图选项 -1为前一周 1为后一周 + * @return CalendarDay 日历项 + */ + public static CalendarDay getNearWeekDay(CalendarDay day, int offset) { + Calendar date = Calendar.getInstance(); + CalendarDay resDay = new CalendarDay(); + date.set(day.getYear(), day.getMonth() - 1, day.getDay(), 12, 0); + date.add(Calendar.DATE, offset * 7); + resDay.setYear(date.get(Calendar.YEAR)); + resDay.setMonth(date.get(Calendar.MONTH) + 1); + resDay.setDay(date.get(Calendar.DATE)); + return resDay; + } + + /** + * 返回对应日历项月的上或下一月日历项 + * + * @param day CalendarDay + * @param offset 前后月视图选项 -1为前一月 1为后一月 + * @return CalendarDay + */ + public static CalendarDay getNearMonthDay(CalendarDay day, int offset) { + if (offset == 0) + return day; + Calendar date = Calendar.getInstance(); + CalendarDay resDay = new CalendarDay(); + date.set(day.getYear(), day.getMonth() - 1, day.getDay(), 12, 0); + date.add(Calendar.MONTH, offset); + resDay.setYear(date.get(Calendar.YEAR)); + resDay.setMonth(date.get(Calendar.MONTH) + 1); + resDay.setDay(date.get(Calendar.DATE)); + return resDay; + } + + public static int getIndexOfCurrDay(int CalendarType) { + List items; + CalendarDay day = getCurrDay(); + if (CalendarType == State.VIEW_MONTH || CalendarType == State.VIEW_FULL) { + items = generateItemForMonthView(day.getYear(), day.getMonth()); + } else { + items = generateItemForWeekView(day.getYear(), day.getMonth(), day.getDay()); + } + return items.indexOf(day); + } + + /** + * 获得 day 所在周是其所在月的第几周 + * + * @param day CalendarDay + * @return int + */ + public static int getWeekOfMonth(CalendarDay day) { + Calendar date = Calendar.getInstance(); + date.set(day.getYear(), day.getMonth(), day.getDay(), 12, 0); + return date.get(Calendar.WEEK_OF_MONTH); + } + + + /** + * 删除方法, 这里只会删除某个文件夹下的文件,如果传入的directory是个文件,将不做处理 + * + * @param child 需要移动的View + * @param dy 实际偏移量 + * @param minOffset 最小偏移量 + * @param maxOffset 最大偏移量 + * @return void + */ + public static int scroll(View child, int dy, int minOffset, int maxOffset) { + Log.i(TAG, "scroll: scrolling in viewTools"); + final int initOffset = child.getTop(); + int offset = calcOffset(initOffset - dy, minOffset, maxOffset) - initOffset; + child.offsetTopAndBottom(offset); + return -offset; + } + + /** + * 计算偏移距离 + * + * @param offset 偏移值 + * @param min 最小偏移值 + * @param max 最大偏移值 + * @return int offset + */ + private static int calcOffset(int offset, int min, int max) { + if (offset > max) { + return max; + } else return Math.max(offset, min); + } + + /** + * 通过scrollTo方法完成协调布局的滑动,其中主要使用了ViewCompat.postOnAnimation + * + * @param parent 协调布局parent + * @param child 协调布局协调滑动的child + * @param y 滑动目标位置y轴数值 + * @param duration 滑动执行时间 + */ + public static void scrollTo(final CoordinatorLayout parent, final RecyclerView child, final int y, int duration) { + final Scroller scroller = new Scroller(parent.getContext()); + int top = CalendarAttr.getRecTop(); + scroller.startScroll(0, top, 0, y - top, duration); //设置scroller的滚动偏移量 + ViewCompat.postOnAnimation(child, new Runnable() { + @Override + public void run() { + //返回值为boolean,true说明滚动尚未完成,false说明滚动已经完成。 + // 这是一个很重要的方法,通常放在View.computeScroll()中,用来判断是否滚动是否结束。 + if (scroller.computeScrollOffset()) { + int delta = scroller.getCurrY() - child.getTop(); + child.offsetTopAndBottom(delta); + CalendarAttr.setRecTop(child.getTop()); + parent.dispatchDependentViewsChanged(child); + ViewCompat.postOnAnimation(child, this); + } + } + }); + } + + /** + * 得到TouchSlop + * + * @param context 上下文 + * @return int touchSlop的具体值 + */ + public static int getTouchSlop(Context context) { + return ViewConfiguration.get(context).getScaledTouchSlop(); + } + + public static void forceStopRecyclerViewScroll(RecyclerView mRecyclerView) { + mRecyclerView.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_CANCEL, 0, 0, 0)); + } + + @SuppressLint("SimpleDateFormat") + public static String getTimeForZone() { + Calendar date = Calendar.getInstance(); + SimpleDateFormat sdf = new SimpleDateFormat(); + sdf.applyPattern("MM-dd HH:mm:ss a"); + Date time = date.getTime(); + return sdf.format(time); + } +} diff --git a/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarView.java b/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarView.java new file mode 100644 index 0000000..9ec68ca --- /dev/null +++ b/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarView.java @@ -0,0 +1,272 @@ +package com.idealist.calendarview; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; + +import com.idealist.calendarview.interf.OnSelectDateListener; + +import java.util.ArrayList; +import java.util.List; + +public class CalendarView extends View implements View.OnLongClickListener { + + private CalendarViewAdapter adapter; + + private CalendarAttr attr; + + private int mItemHeight; + + private int mItemWidth; + + private int mViewHeight; + + private int mBaseLine; + + private boolean isClick; + + private boolean isScroll; + + private int mTextSize; + + private int mCurrItem; + + private int mRowCount; + + MotionEvent event; + + /** + * 点击坐标 + */ + private float mX, mY; + + private List items = new ArrayList<>(); + + private CalendarDay seedDay; + + private OnSelectDateListener onSelectDateListener; + + private OnCalendarClickListener clickListener; + + public CalendarView(Context context, + CalendarAttr attr, + CalendarViewAdapter adapter, + OnSelectDateListener onSelectDateListener, + OnCalendarClickListener clickListener) { + super(context); + this.attr = attr; + this.adapter = adapter; + this.clickListener = clickListener; + this.onSelectDateListener = onSelectDateListener; + this.setOnLongClickListener(this); + initView(); + } + + /** + * 初始化 View + */ + private void initView() { + mRowCount = attr.getRowCount(); + mViewHeight = attr.getViewHeight(); + mItemHeight = attr.getItemHeight(); + mTextSize = attr.getTextSize(); + mBaseLine = (int) (mTextSize + mItemHeight) / 2; + mCurrItem = CalendarUtils.getIndexOfCurrDay(State.VIEW_MONTH); + } + + + /** + * 为对应视图生成对应日历项, 以SeedDay为对应 + */ + final int generateItem() { + if (attr.getCalendarType() != State.VIEW_WEEK) + items = CalendarUtils.generateItemForMonthView(seedDay.getYear(), seedDay.getMonth()); + else + items = CalendarUtils.generateItemForWeekView(seedDay.getYear(), seedDay.getMonth(), seedDay.getDay()); + return items.indexOf(seedDay); + } + + /** + * 当视图变化时变化 UI + */ + public void changeViewType() { + if (attr.getCalendarType() == State.VIEW_FULL) { + attr.setItemHeight(State.DEFAULT_ITEM_HEIGHT_FULL); + } else { + attr.setItemHeight(State.DEFAULT_ITEM_HEIGHT); + } + mItemHeight = attr.getItemHeight(); + mViewHeight = attr.getViewHeight(); + setViewHeight(ViewGroup.LayoutParams.WRAP_CONTENT); + mCurrItem = generateItem(); + invalidate(); + } + + public void updateView() { + mCurrItem = generateItem(); + invalidate(); + } + + + public CalendarDay getSeedDay() { + return seedDay; + } + + public void setSeedDay(CalendarDay seedDay) { + this.seedDay = seedDay; + } + + + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (attr.getCalendarType() != State.VIEW_FULL) { + setMeasuredDimension(widthMeasureSpec, mViewHeight); + } + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouchEvent(MotionEvent event) { + float touchX = event.getX(); + float touchY = event.getY(); + int action = event.getAction(); + float disX, disY; + switch (action) { + case MotionEvent.ACTION_DOWN: + mX = touchX; + mY = touchY; + isClick = true; + break; + case MotionEvent.ACTION_MOVE: + disX = touchX - mX; + disY = touchY - mY; + if (Math.abs(disX) > 2 * mItemWidth || Math.abs(disY) > 2 * mItemHeight) { + isScroll = true; + } + isClick = false; + break; + case MotionEvent.ACTION_UP: + if (isClick) { + items.get(mCurrItem).setSelectState(State.DAY_UN_SELECT); + mCurrItem = getIndexOnView(); + adapter.setSelectDay(items.get(mCurrItem)); + if (items.get(mCurrItem).getMonthState() != State.DAY_CURR_MONTH) { + items.get(mCurrItem).setSelectState(State.DAY_UN_SELECT); + onSelectDateListener.onSelectOtherMonth(items.get(mCurrItem).getMonthState()); + mCurrItem = 0; + } else { + items.get(mCurrItem).setSelectState(State.DAY_SELECT); + } + if (adapter.getSelectDay().isMarkSchedule()) { + clickListener.OnClick(adapter.getSelectDay()); + } + invalidate(); + } + break; + } + return super.onTouchEvent(event); + } + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } + + public void setmCurrItem(int mCurrItem) { + this.mCurrItem = mCurrItem; + } + + private int getIndexOnView() { + int indexX = (int) mX / mItemWidth; + int indexY = (int) mY / mItemHeight; + int position = -1; + if (isClick) { + if (indexX > 6) indexX = 6; + position = 7 * indexY + indexX; + } + if (position > 41) + position = 41; + return position; + } + + @Override + protected void onDraw(Canvas canvas) { + mItemWidth = getWidth() / 7; + mItemHeight = attr.getItemHeight(); + mRowCount = attr.getRowCount(); + mBaseLine = (int) (mTextSize * 1.5); + int sW = mItemWidth / 2; + int sH = mBaseLine; + for (int i = 0; i < mRowCount; ++i) { + for (int j = 0; j < 7; ++j) { + draw(canvas, items.get(7 * i + j), sW + mItemWidth * j, sH + mItemHeight * i); + } + } + } + + private void draw(Canvas canvas, CalendarDay day, int x, int y) { + onDrawText(canvas, day, x, y); + onDrawMark(canvas, day, x, y); + } + + private void onDrawText(Canvas canvas, CalendarDay day, int x, int y) { + Paint paint = new Paint(); + paint.setTextSize(mTextSize); + paint.setColor(Color.GRAY); + paint.setTextAlign(Paint.Align.CENTER); +// Log.e("View", day.toString()); + if (day.isCurrDay()) { + paint.setColor(Color.BLUE); + } else if (day.getSelectState() == State.DAY_SELECT) { + paint.setColor(Color.GREEN); + } else if (day.getMonthState() == State.DAY_CURR_MONTH) { + paint.setColor(Color.BLACK); + } + canvas.drawText(Integer.toString(day.getDay()), x, y, paint); + } + + private void onDrawMark(Canvas canvas, CalendarDay day, int x, int y) { + if (day.isMarkSchedule()) { + Paint paint = new Paint(); + paint.setColor(Color.BLUE); + canvas.drawCircle(x, y+20, 10, paint); + } + } + + + /** + * 设置 View 高度 + * + * @param height 高度 + */ + private void setViewHeight(int height) { + Log.e(null, "setViewHeight"); + ViewGroup.LayoutParams params = (ViewGroup.LayoutParams) this.getLayoutParams(); + params.height = height; + this.setLayoutParams(params); + } + + @Override + public boolean onLongClick(View v) { + if (clickListener != null) { + clickListener.OnLongClick(adapter.getSelectDay()); + } + return false; + } + + public interface OnCalendarClickListener { + void OnClick(CalendarDay day); + void OnLongClick(CalendarDay day); + } +} diff --git a/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarViewAdapter.java b/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarViewAdapter.java new file mode 100644 index 0000000..2629e7e --- /dev/null +++ b/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarViewAdapter.java @@ -0,0 +1,188 @@ +package com.idealist.calendarview; + +import android.content.Context; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.viewpager.widget.PagerAdapter; + +import com.idealist.calendarview.interf.OnSelectDateListener; + +import java.util.ArrayList; +import java.util.List; + +public class CalendarViewAdapter extends PagerAdapter { + + private CalendarAttr attr; + private List mViews = new ArrayList<>(); + private int mCurrPosition = CalendarPager.CURRENT_DAY_INDEX; + + // 保存上一次点击的日期 + private static CalendarDay mRecDate; + private CalendarDay mSelectDay; + private OnCalendarTypeChangeListener typeChangeListener; + + public CalendarViewAdapter(Context context, CalendarAttr attr, + OnSelectDateListener dateListener, + CalendarView.OnCalendarClickListener longClickListener) { + super(); + this.attr = attr; + initViewAdapter(context, dateListener, longClickListener); + } + + + private void initViewAdapter(Context context, OnSelectDateListener dateListener, + CalendarView.OnCalendarClickListener longClickListener) { + mSelectDay = CalendarUtils.getCurrDay(); + CalendarDay day; + for (int i = 0; i < 3; i++) { + CalendarView view = new CalendarView(context, attr, this, dateListener, longClickListener); + day = CalendarUtils.getNearMonthDay(mSelectDay, i - 1); + view.setSeedDay(day); + mViews.add(view); + } + } + + /** + * 通过滑动更新 View + * + * @param position 需要更新的 View 的 position + */ + public void updateViewByScrollHorizontally(int position) { + Log.e("adapter", "position: " + position + " mCurrPosition: " + mCurrPosition); + CalendarDay seedDay = mViews.get(position % 3).getSeedDay(); + int offset = 0; + if (position > mCurrPosition) { + offset = 1; + } else if (position < mCurrPosition) { + offset = -1; + } + CalendarView view = mViews.get((position + offset) % 3); + //TODO 注意视图 + if (attr.getCalendarType() != State.VIEW_WEEK) + view.setSeedDay(CalendarUtils.getNearMonthDay(seedDay, offset)); + else + view.setSeedDay(CalendarUtils.getNearWeekDay(seedDay, offset)); + } + + + public int getCalendarType() { + return attr.getCalendarType(); + } + + /** + * 当视图类型改变时更新 View 的显示模式 + * + * @param updateType 更新的显示模式 + */ + public void changeCalendarType(int updateType) { + attr.setCalendarType(updateType); + CalendarDay seedDay = mViews.get(mCurrPosition % 3).getSeedDay(); + saveSelectedDate(); + for (int i = -1; i < 2; ++i) { + CalendarView view = mViews.get((mCurrPosition + i) % 3); + if (updateType == State.VIEW_WEEK) { + view.setSeedDay(CalendarUtils.getNearWeekDay(mSelectDay, i)); + } else { + view.setSeedDay(CalendarUtils.getNearMonthDay(mSelectDay, i)); + } + view.changeViewType(); + } + } + +// /** +// * 由 pager 通知 adapter 发生点击事件更改,进行处理 +// * @param day 选中 CalendarDay +// */ +// public void notifyDataChanged() { +// +// } + + public void saveSelectedDate() { + mRecDate = mSelectDay; + } + + public static CalendarDay loadSelectedDate() { + return mRecDate; + } + + @Override + public int getCount() { + // 实现无限循环 + return Integer.MAX_VALUE; + } + + + @Override + public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) { + super.setPrimaryItem(container, position % 3, object); + this.mCurrPosition = position; + Log.e("adapterPri", Integer.toString(position)); + } + + @NonNull + @Override + public Object instantiateItem(@NonNull ViewGroup container, int position) { +// if (position < 2) return null; + + CalendarView view = mViews.get(position % 3); + view.generateItem(); + view.invalidate(); + + int mChildNum = container.getChildCount(); + if (mChildNum == mViews.size()) { + container.removeView(view); + } + + if (container.getChildCount() < mViews.size()) { + container.addView(view, 0); + } else { + container.addView(view, position % 3); + } + return view; + } + + public void updateDayInViewState(int position) { + CalendarView view = mViews.get(position % 3); + view.setSeedDay(mSelectDay); + view.updateView(); + } + + public void updateViewScheduleChange() { + CalendarView view = mViews.get(mCurrPosition % 3); + view.updateView(); + } + + public void setTypeChangeListener(OnCalendarTypeChangeListener typeChangeListener) { + this.typeChangeListener = typeChangeListener; + } + + + public CalendarDay getSelectDay() { + return mSelectDay; + } + + public void setSelectDay(CalendarDay mSelectDay) { + this.mSelectDay = mSelectDay; + } + + public List getViews() { + return mViews; + } + + public interface OnCalendarTypeChangeListener { + void onCalendarTypeChanged(int Calendar_Type); + } + + @Override + public boolean isViewFromObject(@NonNull android.view.View view, @NonNull Object object) { + return view == ((View) object); + } + + @Override + public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { + + } +} diff --git a/src/CalendarView/src/main/java/com/idealist/calendarview/interf/OnSelectDateListener.java b/src/CalendarView/src/main/java/com/idealist/calendarview/interf/OnSelectDateListener.java new file mode 100644 index 0000000..79c5829 --- /dev/null +++ b/src/CalendarView/src/main/java/com/idealist/calendarview/interf/OnSelectDateListener.java @@ -0,0 +1,7 @@ +package com.idealist.calendarview.interf; + +import com.idealist.calendarview.CalendarDay; + +public interface OnSelectDateListener { + void onSelectOtherMonth(int offset);//点击其它月份日期 +} diff --git a/src/app/build.gradle b/src/app/build.gradle index 35b7146..8a3b0a4 100644 --- a/src/app/build.gradle +++ b/src/app/build.gradle @@ -33,6 +33,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'com.google.android.material:material:1.2.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + implementation project(path: ':CalendarView') testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' diff --git a/src/app/src/main/java/com/showme/myapplication/MainActivity.java b/src/app/src/main/java/com/showme/myapplication/MainActivity.java index 92bd10a..8df5d92 100644 --- a/src/app/src/main/java/com/showme/myapplication/MainActivity.java +++ b/src/app/src/main/java/com/showme/myapplication/MainActivity.java @@ -2,13 +2,114 @@ package com.showme.myapplication; import androidx.appcompat.app.AppCompatActivity; +import android.annotation.SuppressLint; import android.os.Bundle; +import android.util.DisplayMetrics; +import android.widget.TextView; + +import com.idealist.calendarview.CalendarAttr; +import com.idealist.calendarview.CalendarDay; +import com.idealist.calendarview.CalendarPager; +import com.idealist.calendarview.CalendarUtils; +import com.idealist.calendarview.CalendarView; +import com.idealist.calendarview.CalendarViewAdapter; +import com.idealist.calendarview.State; +import com.idealist.calendarview.interf.OnSelectDateListener; + +import java.util.TimeZone; public class MainActivity extends AppCompatActivity { + private TextView tvMonth; + private TextView tvYear; + + private CalendarDay currentDay; + + private CalendarView currentView; + + private CalendarPager pager; + + private CalendarViewAdapter viewAdapter; + + private CalendarAttr attr; + + private OnSelectDateListener onSelectDateListener; + + private CalendarView.OnCalendarClickListener clickListener; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + + TimeZone.setDefault(TimeZone.getTimeZone("GMT+8")); + DisplayMetrics dm = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getRealMetrics(dm); + int height = dm.heightPixels; + State.setDefaultItemHeightFull((height - 42 - 3*State.DEFAULT_ITEM_HEIGHT) / 6); + + initCalendar(); + + attr = new CalendarAttr(); + attr.setCalendarType(State.VIEW_MONTH); + attr.setScrollLevel(State.LEVEL_MEDIUM); + + initListener(); + viewAdapter = new CalendarViewAdapter(this, attr, onSelectDateListener, + clickListener); + initPager(); } + + private void initCalendar() { + tvMonth = findViewById(R.id.custom_month_view); + tvYear = findViewById(R.id.custom_year_view); + currentDay = CalendarUtils.getCurrDay(); + updateCalendar(); + } + + private void initPager() { + pager = findViewById(R.id.custom_vp); + pager.setAttr(attr); + pager.setAdapter(viewAdapter); + + pager.setmOnPageChangeListener(new CalendarPager.OnPageChangeListener() { + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + } + + @Override + public void onPageSelected(int position) { + currentView = viewAdapter.getViews().get(position % 3); + currentDay = currentView.getSeedDay(); + updateCalendar(); + } + + @Override + public void onPageScrollStateChanged(int state) { + } + }); + } + + private void initListener() { + onSelectDateListener = offset -> pager.selectOtherMonth(offset); + clickListener = new CalendarView.OnCalendarClickListener() { + @Override + public void OnClick(CalendarDay day) { +// updateSchedule(day); + } + + @Override + public void OnLongClick(CalendarDay day) { + + } + }; + } + + @SuppressLint("SetTextI18n") + private void updateCalendar() { + tvMonth.setText(currentDay.getMonth() + ""); + tvYear.setText(currentDay.getYear() + ""); + } + } \ No newline at end of file diff --git a/src/app/src/main/res/layout/activity_main.xml b/src/app/src/main/res/layout/activity_main.xml index 4fc2444..6bc8ba7 100644 --- a/src/app/src/main/res/layout/activity_main.xml +++ b/src/app/src/main/res/layout/activity_main.xml @@ -1,18 +1,142 @@ - + android:layout_height="match_parent"> - + + + + + + + + android:orientation="horizontal"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + \ No newline at end of file diff --git a/src/app/src/main/res/values/styles.xml b/src/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..8a2b97e --- /dev/null +++ b/src/app/src/main/res/values/styles.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file From a9b1069887c2b6637911c42f0345d8ca887154df Mon Sep 17 00:00:00 2001 From: harvey Date: Wed, 7 Jul 2021 12:57:51 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=9C=A8=E6=97=A5=E5=8E=86=E4=B8=AD?= =?UTF-8?q?=E8=B0=83=E7=94=A8DataBase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CalendarView/build.gradle | 1 + .../java/com/idealist/calendarview/CalendarUtils.java | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/CalendarView/build.gradle b/src/CalendarView/build.gradle index 8bc4f08..e92336c 100644 --- a/src/CalendarView/build.gradle +++ b/src/CalendarView/build.gradle @@ -32,6 +32,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.3.0' implementation 'com.google.android.material:material:1.3.0' + implementation project(path: ':DataBase') testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' diff --git a/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarUtils.java b/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarUtils.java index cc11d54..338e02b 100644 --- a/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarUtils.java +++ b/src/CalendarView/src/main/java/com/idealist/calendarview/CalendarUtils.java @@ -15,7 +15,7 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.RecyclerView; -//import com.diary.database.utils.ScheduleSQLUtils; +import com.showme.database.utils.ScheduleSQLUtils; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -193,9 +193,9 @@ public class CalendarUtils { if (date.equals(CalendarViewAdapter.loadSelectedDate())) { date.setSelectState(State.DAY_SELECT); } -// if (ScheduleSQLUtils.isScheduleMarked(date.toString())) { -// date.setMarkSchedule(true); -// } + if (ScheduleSQLUtils.isScheduleMarked(date.toString())) { + date.setMarkSchedule(true); + } } return dates; }