先来看看日历的效果,请自行忽略我截屏时的淡绿色圆圈:
在手指上滑或者下滑后,日历将在月视图和周视图之间切换。 主要的原理就是利用margin值,让想隐藏的部分移动到父布局的上方。同时变换父布局的高度。
二话不说贴代码:
public class ZoomCalendarView extends RelativeLayout {
float mPosX, mPosY, mCurPosX, mCurPosY;
private int m_marginCountY = 0;
private Context m_context;
private PagerAdapter m_monthAdapter;
private PagerAdapter m_weekAdapter;
private ViewPager m_monthPager;
private ViewPager m_weekPager;
private LinearLayout m_calendarLayout;
private LinearLayout m_weekLayout;
private CardView m_calendarCard;
private RelativeLayout m_cardTitle;
private int m_orignalHeight = 0;
private int m_orignalWeekHeight = 0;
private int m_orignalTitleHeight = 0;
// 最小滑动距离
private int m_minMoveSize;
private int m_minEffectiveSize;
private int m_minOrignalHeight = 0;
private boolean isChangeEffect = false;
AppCompatButton m_yearButton;
AppCompatButton m_monthButton;
public ZoomCalendarView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
m_context = context;
LayoutInflater.from(m_context).inflate(R.layout.zoom_calendar_layout, this);
m_monthPager = (ViewPager) findViewById(R.id.month_viewpager);
m_weekPager = (ViewPager) findViewById(R.id.week_viewpager);
m_monthAdapter = new MonthPagerAdapter(m_context);
m_weekAdapter = new WeekPageAdapter(m_context);
m_monthPager.setAdapter(m_monthAdapter);
m_weekPager.setAdapter(m_weekAdapter);
m_monthPager.setCurrentItem(ValueUtil.DEFAULT_MONTH_ITEM);
m_weekPager.setCurrentItem(ValueUtil.DEFAULT_WEEK_ITEM);
m_calendarLayout = (LinearLayout) findViewById(R.id.calendar_linearlayout);
m_weekLayout = (LinearLayout) findViewById(R.id.weekday_linearlayout);
m_minMoveSize = getResources().getDimensionPixelSize(R.dimen.min_move_size);
m_minEffectiveSize = 3 * m_minMoveSize;
m_calendarCard = (CardView) findViewById(R.id.calendar_card);
m_cardTitle = (RelativeLayout) findViewById(R.id.calendar_card_title);
m_yearButton = (AppCompatButton) findViewById(R.id.year_button);
m_monthButton = (AppCompatButton) findViewById(R.id.month_button);
setMonthYear(Calendar.getInstance());
m_monthPager.setCurrentItem(ValueUtil.DEFAULT_MONTH_ITEM);
m_monthPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MONTH,position - ValueUtil.DEFAULT_MONTH_ITEM);
setMonthYear(calendar);
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
m_weekPager.setCurrentItem(ValueUtil.DEFAULT_WEEK_ITEM);
m_weekPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.WEEK_OF_YEAR,position - ValueUtil.DEFAULT_WEEK_ITEM);
setMonthYear(calendar);
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
private void setMonthYear(Calendar calendar){
m_yearButton.setText(String.valueOf(calendar.get(Calendar.YEAR)));
m_monthButton.setText(String.valueOf(calendar.get(Calendar.MONTH ) + 1));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (m_orignalHeight == 0)
m_orignalHeight = m_calendarCard.getHeight();
if (m_orignalWeekHeight == 0)
m_orignalWeekHeight = m_weekLayout.getHeight();
if (m_orignalTitleHeight == 0)
m_orignalTitleHeight = m_cardTitle.getHeight();
if(m_minOrignalHeight == 0)
m_minOrignalHeight = MomentsUtils.dip2px(m_context, 50) + m_orignalWeekHeight + MomentsUtils.dip2px(m_context, 11) + m_orignalTitleHeight;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mPosX = ev.getX();
mPosY = ev.getY();
Log.d("lzy", " ACTION_DOWN----- " + (int) (mPosY));
break;
case MotionEvent.ACTION_MOVE:
mCurPosX = ev.getX();
mCurPosY = ev.getY();
m_marginCountY = (int) (mPosY - mCurPosY);
if (Math.abs(mCurPosX - mPosX) < Math.abs(mCurPosY - mPosY)) {
if (m_marginCountY > m_minMoveSize)
smoothShrink(m_marginCountY);
else if (m_marginCountY < -m_minMoveSize) {
Log.d("lzy", "smoothExpand ==== ");
smoothExpand(Math.abs(m_marginCountY));
}
}
break;
case MotionEvent.ACTION_UP:
mCurPosX = ev.getX();
mCurPosY = ev.getY();
if (Math.abs(mCurPosX - mPosX) >= Math.abs(mCurPosY - mPosY))
break;
if (Math.abs(m_marginCountY) <= m_minMoveSize) {
m_marginCountY = 0;
} else {
if (Math.abs(m_marginCountY) > m_minEffectiveSize) {
isChangeEffect = true;
if (m_marginCountY > 0) {
smoothShrinkAuto();
} else {
smoothExpandAuto();
}
} else {
isChangeEffect = false;
if (m_marginCountY < 0) {
smoothShrinkAuto();
} else {
smoothExpandAuto();
}
}
return true;
}
Log.d("lzy", "ACTION_UP-------");
break;
}
return super.dispatchTouchEvent(ev);
}
private void smoothShrinkAuto() {
if(isChangeEffect)
setMonthYear(CalendarUtil.getChooseCalendar());
new Thread() {
@Override
public void run() {
if (isChangeEffect) {
while (m_marginCountY < m_orignalHeight) {
post(new Runnable() {
@Override
public void run() {
synchronized (m_context) {
m_marginCountY += m_minMoveSize;
smoothShrink(m_marginCountY);
}
}
});
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isChangeEffect = false;
} else{
m_marginCountY= m_orignalHeight - m_calendarCard.getHeight();
while (m_marginCountY < m_orignalHeight) {
post(new Runnable() {
@Override
public void run() {
synchronized (m_context) {
m_marginCountY += m_minMoveSize;
smoothShrink(m_marginCountY);
}
}
});
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}.start();
}
private void smoothShrink(int scrollY) {
if (m_monthPager.getVisibility() == VISIBLE) {
int chooseHeight = 0;
int rowHeight = 0;
for (int i = 0; i < m_monthPager.getChildCount(); i++) {
MonthCalendar index = (MonthCalendar) m_monthPager.getChildAt(i);
rowHeight = index.getRowHeight();
if (index.getChooseHeight() != -1) {
chooseHeight = index.getChooseHeight();
break;
}
}
if (scrollY < chooseHeight) {
m_monthPager.scrollTo(m_monthPager.getScrollX(), scrollY);
Log.d("lzy", "monthCalendar.getChooseHeight() == " + chooseHeight);
} else {
m_monthPager.scrollTo(m_monthPager.getScrollX(), chooseHeight);
}
Log.d("lzy", "(m_orignalHeight - scrollY) == " + (m_orignalHeight - scrollY) + "monthCalendar.getRowHeight() == " + rowHeight);
if ((m_orignalHeight - scrollY) > rowHeight + m_orignalWeekHeight + m_orignalTitleHeight) {
resizeCardHeigt(m_orignalHeight - scrollY);
} else {
m_monthPager.setVisibility(GONE);
m_weekPager.setVisibility(VISIBLE);
int apartWeek = CalendarUtil.getWeekFromDayToDay(Calendar.getInstance(), CalendarUtil.getChooseCalendar());
Log.d("lzy", "apartweek ==== " + apartWeek);
m_weekPager.setCurrentItem(ValueUtil.DEFAULT_WEEK_ITEM + apartWeek, false);
for (int i = 0; i < m_weekPager.getChildCount(); i++) {
WeekCalendar weekCalendar = (WeekCalendar) m_weekPager.getChildAt(i);
weekCalendar.reflushSelectDay();
}
resizeCardHeigt(m_minOrignalHeight);
}
Log.d("lzy", " ACTION_MOVE----- " + (int) (mCurPosY - mPosY));
}
}
private void smoothExpand(int scrollY) {
scrollY = Math.abs(scrollY);
int apartMonth = CalendarUtil.getMonthFromDayToDay(Calendar.getInstance(), CalendarUtil.getChooseCalendar());
m_monthPager.setCurrentItem(ValueUtil.DEFAULT_MONTH_ITEM + apartMonth, false);
m_monthPager.setVisibility(VISIBLE);
m_weekPager.setVisibility(GONE);
int chooseHeight = 0;
int monthHeigt = 0;
for (int i = 0; i < m_monthPager.getChildCount(); i++) {
MonthCalendar monthCalendar = (MonthCalendar) m_monthPager.getChildAt(i);
if (monthCalendar.getChooseHeight() != -1) {
chooseHeight = monthCalendar.getChooseHeight();
monthHeigt = monthCalendar.getHeight();
break;
}
}
Log.d("lzy", "scrollY + m_minOrignalHeight ==== " + scrollY + m_minOrignalHeight);
Log.d("lzy", "m_orignalHeight + MomentsUtils.dip2px(m_context,11) === " + m_orignalHeight + MomentsUtils.dip2px(m_context, 11));
if (monthHeigt - scrollY < chooseHeight)
m_monthPager.scrollTo(m_monthPager.getScrollX(), monthHeigt - scrollY);
if (scrollY + m_minOrignalHeight < m_orignalHeight) {
resizeCardHeigt(m_minOrignalHeight + scrollY);
} else {
for (int i = 0; i < m_monthPager.getChildCount(); i++) {
MonthCalendar monthCalendar = (MonthCalendar) m_monthPager.getChildAt(i);
monthCalendar.reflushSelectDay();
}
resizeCardHeigt(m_orignalHeight);
m_monthPager.scrollTo(m_monthPager.getScrollX(), 0);
}
}
private void smoothExpandAuto() {
Log.d("lzy", "smoothExpandAuto start ======");
if(isChangeEffect)
setMonthYear(CalendarUtil.getChooseCalendar());
new Thread() {
@Override
public void run() {
if (isChangeEffect) {
while (Math.abs(m_marginCountY) < m_orignalHeight - m_minOrignalHeight) {
post(new Runnable() {
@Override
public void run() {
synchronized (m_context) {
m_marginCountY -= m_minMoveSize;
smoothExpand(Math.abs(m_marginCountY));
}
}
});
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isChangeEffect = false;
} else {
m_marginCountY = m_calendarCard.getHeight() - m_minOrignalHeight;
while (Math.abs(m_marginCountY) < m_orignalHeight - m_minOrignalHeight) {
int i = 0;
post(new Runnable() {
@Override
public void run() {
synchronized (m_context) {
m_marginCountY += m_minMoveSize;
smoothExpand( Math.abs(m_marginCountY));
}
}
});
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}.start();
Log.d("lzy", "smoothExpandAuto end ======");
}
private void resizeCardHeigt(int height) {
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) m_calendarCard.getLayoutParams();
params.width = m_calendarCard.getWidth();
params.height = height;
Log.d("lzy", "params.height === " + params.height);
m_calendarCard.setLayoutParams(params);
}
}
当滑动的距离大于有效距离时,缩小或放大日历。否则·日历变为原来的大小。
这里介绍如何收缩:
以smoothexpandauto为例,当滑动距离大于最小滑动距离,那么启动一个线程,调用ViewPager 的scrollTo到选择的日期的位置,同时以相同的速度扩大父布局,当滚动到 选择的日期的位置时,不再scrollto, 直接放大父布局。这里可以优化成先加速后减速,更符合material design。
代码链接:
点击打开链接
代码参考了github上的某个项目,具体找不到 ,如有需要立即删除。