Java tutorial
/* * Copyright (C) 2016 huanghaibin_dev <huanghaibin_dev@163.com> * WebSite https://github.com/MiracleTimes-Dev * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gitstudy.rili.liarbry; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.support.v4.view.MotionEventCompat; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.animation.LinearInterpolator; import android.widget.AbsListView; import android.widget.LinearLayout; import com.gitstudy.R; /** * */ public class CalendarLayout extends LinearLayout { /** * ? */ private int mActivePointerId; private static final int ACTIVE_POINTER = 1; private static final int INVALID_POINTER = -1; /** * */ private static final int CALENDAR_SHOW_MODE_BOTH_MONTH_WEEK_VIEW = 0; /** * */ private static final int CALENDAR_SHOW_MODE_ONLY_WEEK_VIEW = 1; /** * */ private static final int CALENDAR_SHOW_MODE_ONLY_MONTH_VIEW = 2; /** * */ private static final int STATUS_EXPAND = 0; /** * */ private static final int STATUS_SHRINK = 1; /** * ? */ private int mDefaultStatus; /** * ? */ WeekBar mWeekBar; /** * ViewPager */ MonthViewPager mMonthView; /** * */ WeekViewPager mWeekPager; /** * */ YearViewSelectLayout mYearView; /** * ContentView */ ViewGroup mContentView; /** * */ private static final int GESTURE_MODE_DEFAULT = 0; // /** // * // */ // private static final int GESTURE_MODE_ONLY_CALENDAR = 1; /** * ? */ private static final int GESTURE_MODE_DISABLED = 2; /** * ? */ private int mGestureMode; private int mCalendarShowMode; private int mTouchSlop; private int mContentViewTranslateY; //ContentView ??? , private int mViewPagerTranslateY = 0;// ViewPager???mMonthView? private float downY; private float mLastY; private boolean isAnimating = false; /** * id */ private int mContentViewId; /** * */ private VelocityTracker mVelocityTracker; private int mMaximumVelocity; private int mItemHeight; private CalendarViewDelegate mDelegate; public CalendarLayout(Context context, AttributeSet attrs) { super(context, attrs); setOrientation(LinearLayout.VERTICAL); TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CalendarLayout); mContentViewId = array.getResourceId(R.styleable.CalendarLayout_calendar_content_view_id, 0); mDefaultStatus = array.getInt(R.styleable.CalendarLayout_default_status, STATUS_EXPAND); mCalendarShowMode = array.getInt(R.styleable.CalendarLayout_calendar_show_mode, CALENDAR_SHOW_MODE_BOTH_MONTH_WEEK_VIEW); mGestureMode = array.getInt(R.styleable.CalendarLayout_gesture_mode, GESTURE_MODE_DEFAULT); array.recycle(); mVelocityTracker = VelocityTracker.obtain(); final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = configuration.getScaledTouchSlop(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); } /** * ? * * @param delegate delegate */ final void setup(CalendarViewDelegate delegate) { this.mDelegate = delegate; mItemHeight = mDelegate.getCalendarItemHeight(); initCalendarPosition(delegate.mSelectedCalendar.isAvailable() ? delegate.mSelectedCalendar : delegate.createCurrentDate()); updateContentViewTranslateY(); } /** * ??? * * @param cur ? */ private void initCalendarPosition(Calendar cur) { int diff = CalendarUtil.getMonthViewStartDiff(cur, mDelegate.getWeekStart()); int size = diff + cur.getDay() - 1; updateSelectPosition(size); } /** * ?? * * @param selectPosition position */ final void updateSelectPosition(int selectPosition) { int line = (selectPosition + 7) / 7; mViewPagerTranslateY = (line - 1) * mItemHeight; } /** * ? * * @param week week */ final void updateSelectWeek(int week) { mViewPagerTranslateY = (week - 1) * mItemHeight; } /** * ContentView?? */ void updateContentViewTranslateY() { Calendar calendar = mDelegate.mIndexCalendar; if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_ALL_MONTH) { mContentViewTranslateY = 5 * mItemHeight; } else { mContentViewTranslateY = CalendarUtil.getMonthViewHeight(calendar.getYear(), calendar.getMonth(), mItemHeight, mDelegate.getWeekStart()) - mItemHeight; } //???contentView if (mWeekPager.getVisibility() == VISIBLE) { if (mContentView == null) return; mContentView.setTranslationY(-mContentViewTranslateY); } } /** * */ final void updateCalendarItemHeight() { mItemHeight = mDelegate.getCalendarItemHeight(); if (mContentView == null) return; Calendar calendar = mDelegate.mIndexCalendar; updateSelectWeek(CalendarUtil.getWeekFromDayInMonth(calendar, mDelegate.getWeekStart())); if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_ALL_MONTH) { mContentViewTranslateY = 5 * mItemHeight; } else { mContentViewTranslateY = CalendarUtil.getMonthViewHeight(calendar.getYear(), calendar.getMonth(), mItemHeight, mDelegate.getWeekStart()) - mItemHeight; } translationViewPager(); if (mWeekPager.getVisibility() == VISIBLE) { mContentView.setTranslationY(-mContentViewTranslateY); } } @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent event) { if (mDelegate.isShowYearSelectedLayout) { return false; } if (mContentView == null) { return false; } int action = event.getAction(); float y = event.getY(); mVelocityTracker.addMovement(event); switch (action) { case MotionEvent.ACTION_DOWN: int index = MotionEventCompat.getActionIndex(event); mActivePointerId = MotionEventCompat.getPointerId(event, index); mLastY = downY = y; return true; case MotionEventCompat.ACTION_POINTER_DOWN: { final int indexx = MotionEventCompat.getActionIndex(event); mActivePointerId = MotionEventCompat.getPointerId(event, indexx); if (mActivePointerId == 0) { //?? dy = y- mLastY == 0?? mLastY = MotionEventCompat.getY(event, mActivePointerId); } break; } case MotionEvent.ACTION_MOVE: if (mGestureMode == GESTURE_MODE_DISABLED || mCalendarShowMode == CALENDAR_SHOW_MODE_ONLY_MONTH_VIEW || mCalendarShowMode == CALENDAR_SHOW_MODE_ONLY_WEEK_VIEW) {//???? return false; } getPointerIndex(event, mActivePointerId); if (mActivePointerId == INVALID_POINTER) { //?mLastY?y????? dy== 0?? mLastY = y; mActivePointerId = ACTIVE_POINTER; } float dy = y - mLastY; //?contentView? if (dy < 0 && mContentView.getTranslationY() == -mContentViewTranslateY) { //mContentView.onTouchEvent(event); showWeek(); mLastY = y; return false; } hideWeek(); //?contentView? if (dy > 0 && mContentView.getTranslationY() + dy >= 0) { mContentView.setTranslationY(0); translationViewPager(); mLastY = y; return super.onTouchEvent(event); } //?contentView??contentView? if (dy < 0 && mContentView.getTranslationY() + dy <= -mContentViewTranslateY) { mContentView.setTranslationY(-mContentViewTranslateY); translationViewPager(); mLastY = y; return super.onTouchEvent(event); } //? mContentView.setTranslationY(mContentView.getTranslationY() + dy); translationViewPager(); mLastY = y; break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_POINTER_UP: int pointerIndex = getPointerIndex(event, mActivePointerId); if (mActivePointerId == INVALID_POINTER) break; mLastY = MotionEventCompat.getY(event, pointerIndex); break; case MotionEvent.ACTION_UP: final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); float mYVelocity = velocityTracker.getYVelocity(); if (mContentView.getTranslationY() == 0 || mContentView.getTranslationY() == mContentViewTranslateY) { break; } if (Math.abs(mYVelocity) >= 800) { if (mYVelocity < 0) { shrink(); } else { expand(); } return super.onTouchEvent(event); } if (event.getY() - downY > 0) { expand(); } else { shrink(); } break; } return super.onTouchEvent(event); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (isAnimating) { return true; } if (mGestureMode == GESTURE_MODE_DISABLED) { return false; } if (mYearView == null || mContentView == null || mContentView.getVisibility() != VISIBLE) { return super.onInterceptTouchEvent(ev); } if (mCalendarShowMode == CALENDAR_SHOW_MODE_ONLY_MONTH_VIEW || mCalendarShowMode == CALENDAR_SHOW_MODE_ONLY_WEEK_VIEW) { return false; } if (mYearView.getVisibility() == VISIBLE || mDelegate.isShowYearSelectedLayout) { return super.onInterceptTouchEvent(ev); } final int action = ev.getAction(); float y = ev.getY(); switch (action) { case MotionEvent.ACTION_DOWN: int index = MotionEventCompat.getActionIndex(ev); mActivePointerId = MotionEventCompat.getPointerId(ev, index); mLastY = downY = y; break; case MotionEvent.ACTION_MOVE: float dy = y - mLastY; /* ?ViewPager?? */ if (dy < 0 && mContentView.getTranslationY() == -mContentViewTranslateY) { return false; } /* * ? 2 ?? yViewPager * 1?RecyclerView ViewmContentView * 2?? */ if (dy > 0 && mContentView.getTranslationY() == -mContentViewTranslateY && y >= CalendarUtil.dipToPx(getContext(), 98)) { if (!isScrollTop()) { return false; } } if (dy > 0 && mContentView.getTranslationY() == 0 && y >= CalendarUtil.dipToPx(getContext(), 98)) { return false; } if (Math.abs(dy) > mTouchSlop) {//mTouchSlopContentViewViewPagerCANCEL if ((dy > 0 && mContentView.getTranslationY() <= 0) || (dy < 0 && mContentView.getTranslationY() >= -mContentViewTranslateY)) { mLastY = y; return true; } } break; } return super.onInterceptTouchEvent(ev); } private int getPointerIndex(MotionEvent ev, int id) { int activePointerIndex = MotionEventCompat.findPointerIndex(ev, id); if (activePointerIndex == -1) { mActivePointerId = INVALID_POINTER; } return activePointerIndex; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mContentView != null && mMonthView != null) { int year = mDelegate.mIndexCalendar.getYear(); int month = mDelegate.mIndexCalendar.getMonth(); int monthHeight = CalendarUtil.getMonthViewHeight(year, month, mDelegate.getCalendarItemHeight(), mDelegate.getWeekStart()) + CalendarUtil.dipToPx(getContext(), 41); int height = getHeight(); if (monthHeight >= height && mMonthView.getHeight() > 0) { height = monthHeight; heightMeasureSpec = MeasureSpec.makeMeasureSpec( monthHeight + CalendarUtil.dipToPx(getContext(), 41) + mDelegate.getWeekBarHeight(), MeasureSpec.EXACTLY); } else if (monthHeight < height && mMonthView.getHeight() > 0) { heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); } int h = height - mItemHeight - (mDelegate != null ? mDelegate.getWeekBarHeight() : CalendarUtil.dipToPx(getContext(), 40)) - CalendarUtil.dipToPx(getContext(), 1); super.onMeasure(widthMeasureSpec, heightMeasureSpec); int heightSpec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY); mContentView.measure(widthMeasureSpec, heightSpec); mContentView.layout(mContentView.getLeft(), mContentView.getTop(), mContentView.getRight(), mContentView.getBottom()); } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } @Override protected void onFinishInflate() { super.onFinishInflate(); mMonthView = (MonthViewPager) findViewById(R.id.vp_month); mWeekPager = (WeekViewPager) findViewById(R.id.vp_week); mContentView = (ViewGroup) findViewById(mContentViewId); mYearView = (YearViewSelectLayout) findViewById(R.id.selectLayout); if (mContentView != null) { mContentView.setOverScrollMode(View.OVER_SCROLL_NEVER); } } /** * ViewPager */ private void translationViewPager() { float percent = mContentView.getTranslationY() * 1.0f / mContentViewTranslateY; mMonthView.setTranslationY(mViewPagerTranslateY * percent); } /** * ? * * @return isExpand */ public final boolean isExpand() { return mContentView == null || mMonthView.getVisibility() == VISIBLE; } /** * * * @return ?? */ public boolean expand() { if (isAnimating || mCalendarShowMode == CALENDAR_SHOW_MODE_ONLY_WEEK_VIEW || mContentView == null) return false; if (mMonthView.getVisibility() != VISIBLE) { mWeekPager.setVisibility(GONE); onShowMonthView(); mMonthView.setVisibility(VISIBLE); } ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mContentView, "translationY", mContentView.getTranslationY(), 0f); objectAnimator.setDuration(240); objectAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float currentValue = (Float) animation.getAnimatedValue(); float percent = currentValue * 1.0f / mContentViewTranslateY; mMonthView.setTranslationY(mViewPagerTranslateY * percent); isAnimating = true; } }); objectAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); isAnimating = false; hideWeek(); } }); objectAnimator.start(); return true; } /** * * * @return ? */ public boolean shrink() { if (isAnimating || mContentView == null) { return false; } ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mContentView, "translationY", mContentView.getTranslationY(), -mContentViewTranslateY); objectAnimator.setDuration(240); objectAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float currentValue = (Float) animation.getAnimatedValue(); float percent = currentValue * 1.0f / mContentViewTranslateY; mMonthView.setTranslationY(mViewPagerTranslateY * percent); isAnimating = true; } }); objectAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); isAnimating = false; showWeek(); } }); objectAnimator.start(); return true; } /** * ?? */ final void initStatus() { if (mContentView == null) { return; } if ((mDefaultStatus == STATUS_SHRINK || mCalendarShowMode == CALENDAR_SHOW_MODE_ONLY_WEEK_VIEW) && mCalendarShowMode != CALENDAR_SHOW_MODE_ONLY_MONTH_VIEW) { post(new Runnable() { @Override public void run() { ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mContentView, "translationY", mContentView.getTranslationY(), -mContentViewTranslateY); objectAnimator.setDuration(0); objectAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float currentValue = (Float) animation.getAnimatedValue(); float percent = currentValue * 1.0f / mContentViewTranslateY; mMonthView.setTranslationY(mViewPagerTranslateY * percent); isAnimating = true; } }); objectAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); isAnimating = false; showWeek(); } }); objectAnimator.start(); } }); } else { if (mDelegate.mViewChangeListener == null) { return; } post(new Runnable() { @Override public void run() { mDelegate.mViewChangeListener.onViewChange(true); } }); } } /** * ?? */ private void hideWeek() { onShowMonthView(); mWeekPager.setVisibility(GONE); mMonthView.setVisibility(VISIBLE); } /** * */ private void showWeek() { onShowWeekView(); mWeekPager.getAdapter().notifyDataSetChanged(); mWeekPager.setVisibility(VISIBLE); mMonthView.setVisibility(INVISIBLE); } /** * */ private void onShowWeekView() { if (mWeekPager.getVisibility() == VISIBLE) { return; } if (mDelegate.mViewChangeListener != null) { mDelegate.mViewChangeListener.onViewChange(false); } } /** * */ private void onShowMonthView() { if (mMonthView.getVisibility() == VISIBLE) { return; } if (mDelegate.mViewChangeListener != null) { mDelegate.mViewChangeListener.onViewChange(true); } } /** * ContentView? ??? * * @return ? */ protected boolean isScrollTop() { if (mContentView instanceof CalendarScrollView) { return ((CalendarScrollView) mContentView).isScrollToTop(); } if (mContentView instanceof RecyclerView) return ((RecyclerView) mContentView).computeVerticalScrollOffset() == 0; if (mContentView instanceof AbsListView) { boolean result = false; AbsListView listView = (AbsListView) mContentView; if (listView.getFirstVisiblePosition() == 0) { final View topChildView = listView.getChildAt(0); result = topChildView.getTop() == 0; } return result; } return mContentView.getScrollY() == 0; } /** * ?? */ @SuppressLint("NewApi") final void hideContentView() { if (mContentView == null) return; mContentView.animate().translationY(getHeight() - mMonthView.getHeight()).setDuration(220) .setInterpolator(new LinearInterpolator()).setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); mContentView.setVisibility(INVISIBLE); mContentView.clearAnimation(); } }); } /** * */ @SuppressLint("NewApi") final void showContentView() { if (mContentView == null) return; mContentView.setTranslationY(getHeight() - mMonthView.getHeight()); mContentView.setVisibility(VISIBLE); mContentView.animate().translationY(0).setDuration(180).setInterpolator(new LinearInterpolator()) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); } }); } @SuppressWarnings("unused") private int getCalendarViewHeight() { return mMonthView.getVisibility() == VISIBLE ? mDelegate.getWeekBarHeight() + mMonthView.getHeight() : mDelegate.getWeekBarHeight() + mDelegate.getCalendarItemHeight(); } /** * ??ContentView?? */ public interface CalendarScrollView { /** * ? * * @return ? */ boolean isScrollToTop(); } }