在Android中需要时间日历选择功能的APP不少,但是实现日历功能都比较麻烦,在这里使用一款第三方开源库快速实现日历选择功能
先在gradle中加入下面这句,刷新工程
compile 'com.prolificinteractive:material-calendarview:1.4.2' //日历所在的开源库
compile 'com.jakewharton:butterknife:8.4.0' //注解使用的第三方库
compile 'com.android.support:design:25.0.0'
compile 'org.greenrobot:eventbus:3.0.0'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
封装一个BaseActivity
public abstract class BaseActivity extends AppCompatActivity {
//定义一个Activity的集合,方便管理和销毁
private List<Activity> mActivitys=new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
mActivitys.add(this);
ButterKnife.bind(this);
}
//布局让继承他的Activity去实现
public abstract int getLayoutId();
@Override
protected void onDestroy() {
super.onDestroy();
mActivitys.remove(this);
}
//退出所有Activity
public void finishAll(){
for (Activity activity:mActivitys){
if (activity!=null){
activity.finish();
}
}
}
//设置标题栏
protected void setToolbar(Toolbar toolbar, String title){
toolbar.setTitle(title);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
finishAfterTransition();
}else {
finish();
}
}
});
}
}
有日历时间的主界面
主界面中继承BaseActivity并且实现他的抽象方法
public class MainActivity extends BaseActivity implements View.OnClickListener {
private static final String TAG = "MainActivity";
RelativeLayout rlPrevious;
TextView tvTime;
RelativeLayout rlNext;
LinearLayout linTimeBar;
FloatingActionButton floatButton;
FloatingActionButton floatButton2;
private Calendar calendar;
private int currentDay;
private int currentMonth;
private int currentYear;
@Override
public int getLayoutId() {
Log.d(TAG, "------------getLayoutId: ------------------");
return R.layout.activity_main;
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rlNext= (RelativeLayout) findViewById(R.id.rl_next);
rlPrevious = (RelativeLayout) findViewById(R.id.rl_previous);
tvTime= (TextView) findViewById(R.id.tv_time);
linTimeBar= (LinearLayout) findViewById(R.id.lin_time_bar);
floatButton= (FloatingActionButton) findViewById(R.id.floatButton);
floatButton2= (FloatingActionButton) findViewById(R.id.floatButton2);
Log.d(TAG, "------------onCreate: ------------------");
rlPrevious.setOnClickListener(this);
rlNext.setOnClickListener(this);
tvTime.setOnClickListener(this);
floatButton.setOnClickListener(this);
floatButton2.setOnClickListener(this);
initTime();
EventBus.getDefault().register(this);
}
@Override
protected void onStart() {
super.onStart();
/* rlPrevious.setOnClickListener(this);
rlNext.setOnClickListener(this);
tvTime.setOnClickListener(this);
floatButton.setOnClickListener(this);*/
Log.d(TAG, "------------onStart: ------------------");
}
//获取当前时间日历
private void initTime() {
calendar = Calendar.getInstance();
currentYear = calendar.get(Calendar.YEAR);
currentMonth = calendar.get(Calendar.MONTH) + 1;
currentDay = calendar.get(Calendar.DAY_OF_MONTH);
Log.d(TAG, "initTime: "+currentDay+"-----"+currentMonth+"------"+currentYear);
setTVTime();
}
//监听日历时间的变化,并且接收传过来的数据
@Subscribe
public void onEventMainThread(CalendarDay date) {
currentYear=date.getYear();
currentMonth=date.getMonth()+1;
currentDay=date.getDay();
Log.d(TAG, "-------------------onEventMainThread: -------------------"+currentDay);
setTVTime();
}
//设置标题时间
private void setTVTime() {
tvTime.setText(currentYear+"年"+currentMonth+"月"+currentDay+"日");
}
@Override
public void onClick(View v) {
Log.d(TAG, "-------onClick:------ ");
switch(v.getId()){
case R.id.rl_next:
Toast.makeText(this, "下一天", Toast.LENGTH_SHORT).show();
Log.d(TAG,"下一天");
setNext();
setTVTime();
break;
case R.id.rl_previous:
Toast.makeText(this, "前一天", Toast.LENGTH_SHORT).show();
setPreVious();
setTVTime();
Log.d(TAG,"前一天");
break;
case R.id.floatButton:
Toast.makeText(this, "悬浮按钮", Toast.LENGTH_SHORT).show();
Intent intent=new Intent(this,CalendarActivity.class);
CircularAnimUtil.startActivity(this,intent,v,R.color.colorPrimary,500);
//CircularAnimUtil.startActivityForResult(this,intent, 10,v, R.color.colorPrimary);
Log.d(TAG,"悬浮按钮");
break;
case R.id.tv_time:
Toast.makeText(this, "时间", Toast.LENGTH_SHORT).show();
break;
}
}
private void setPreVious() {
if (currentMonth == 3) {
currentDay--;
if (currentDay < 1) {
if ((currentYear % 4 == 0 && currentYear % 100 != 0) || currentYear % 400 == 0) {
//闰年
currentDay = 29;
currentMonth--;
} else {
//平年
currentDay = 28;
currentMonth--;
}
}
} else if (currentMonth == 1) {
currentDay--;
if (currentDay < 1) {
currentDay = 31;
currentMonth = 12;
currentYear--;
}
} else if (currentMonth == 2 || currentMonth == 4 || currentMonth == 6 || currentMonth == 8 || currentMonth == 9 || currentMonth == 11) {
currentDay--;
if (currentDay < 1) {
currentDay = 31;
currentMonth--;
}
} else {
currentDay--;
if (currentDay < 1) {
currentDay = 30;
currentMonth--;
}
}
}
private void setNext() {
if (currentMonth == 2) {
//二月
if ((currentYear % 4 == 0 && currentYear % 100 != 0) || currentYear % 400 == 0) {
//闰年
currentDay++;
if (currentDay > 29) {
currentDay = 1;
currentMonth++;
}
} else {
//平年
currentDay++;
if (currentDay > 28) {
currentDay = 1;
currentMonth++;
}
}
} else if (currentMonth == 1 || currentMonth == 3 || currentMonth == 5 || currentMonth == 6 || currentMonth == 8 || currentMonth == 10) {
//大月
currentDay++;
if (currentDay > 31) {
currentDay = 1;
currentMonth++;
}
} else if (currentMonth == 12) {
currentDay++;
if (currentDay > 31) {
currentDay = 1;
currentMonth = 1;
currentYear++;
}
} else {
//小月
currentDay++;
if (currentDay > 30) {
currentDay = 1;
currentMonth++;
}
}
}
}
主界面fragment_today_in_history布局如下
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/ll_time_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/holo_blue_light"
android:gravity="center"
android:orientation="horizontal"
android:paddingTop="5dp"
android:paddingBottom="10dp"
app:layout_scrollFlags="scroll|enterAlways">
<RelativeLayout
android:id="@+id/rl_previous"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<ImageView
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_centerInParent="true"
android:src="@drawable/ic_left"
android:tint="@android:color/white" />
</RelativeLayout>
<TextView
android:id="@+id/tv_time"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:gravity="center"
android:text="2016年10月15日"
android:textColor="@android:color/white"
android:textSize="20sp" />
<RelativeLayout
android:id="@+id/rl_next"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<ImageView
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_centerInParent="true"
android:src="@drawable/ic_right"
android:tint="@android:color/white" />
</RelativeLayout>
</LinearLayout>
<android.support.design.widget.FloatingActionButton
android:id="@+id/floatActionBtn"
app:elevation="6dp"
app:fabSize="normal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_margin="30dp"
android:src="@drawable/ic_calender" />
</android.support.design.widget.AppBarLayout>
效果如下:

记得初始化EventBus
EventBus.getDefault().register(this);
点击悬浮按钮跳转到日历选择器
Intent intent=new Intent(getActivity(),CalendarActivity.class);
CircularAnimUtil.startActivityForResult(getActivity(),intent, Constants.CODE_CALENDAR,v, R.color.colorPrimary);
工具类CircularAnimUtil的具体代码,点击悬浮按钮使用动画出现日历选择界面
/**
* 对 ViewAnimationUtils.createCircularReveal() 方法的封装.
* <p/>
* Created on 16/7/20.
* GitHub: https://github.com/XunMengWinter
*
* @author ice
*/
public class CircularAnimUtil {
public static final long PERFECT_MILLS = 618;
public static final int MINI_RADIUS = 0;
/**
* 向四周伸张,直到完成显示。
*/
@SuppressLint("NewApi")
public static void show(View myView, float startRadius, long durationMills) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
myView.setVisibility(View.VISIBLE);
return;
}
int cx = (myView.getLeft() + myView.getRight()) / 2;
int cy = (myView.getTop() + myView.getBottom()) / 2;
int w = myView.getWidth();
int h = myView.getHeight();
// 勾股定理 & 进一法
int finalRadius = (int) Math.sqrt(w * w + h * h) + 1;
Animator anim =
ViewAnimationUtils.createCircularReveal(myView, cx, cy, startRadius, finalRadius);
myView.setVisibility(View.VISIBLE);
anim.setDuration(durationMills);
anim.start();
}
/**
* 由满向中间收缩,直到隐藏。
*/
@SuppressLint("NewApi")
public static void hide(final View myView, float endRadius, long durationMills) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
myView.setVisibility(View.INVISIBLE);
return;
}
int cx = (myView.getLeft() + myView.getRight()) / 2;
int cy = (myView.getTop() + myView.getBottom()) / 2;
int w = myView.getWidth();
int h = myView.getHeight();
// 勾股定理 & 进一法
int initialRadius = (int) Math.sqrt(w * w + h * h) + 1;
Animator anim =
ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, endRadius);
anim.setDuration(durationMills);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
myView.setVisibility(View.INVISIBLE);
}
});
anim.start();
}
/**
* 从指定View开始向四周伸张(伸张颜色或图片为colorOrImageRes), 然后进入另一个Activity,
* 返回至 @thisActivity 后显示收缩动画。
*/
@SuppressLint("NewApi")
public static void startActivityForResult(
final Activity thisActivity, final Intent intent, final Integer requestCode, final Bundle bundle,
final View triggerView, int colorOrImageRes, long durationMills) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
thisActivity.startActivity(intent);
return;
}
int[] location = new int[2];
triggerView.getLocationInWindow(location);
final int cx = location[0] + triggerView.getWidth() / 2;
final int cy = location[1] + triggerView.getHeight() / 2;
final ImageView view = new ImageView(thisActivity);
view.setScaleType(ImageView.ScaleType.CENTER_CROP);
view.setImageResource(colorOrImageRes);
final ViewGroup decorView = (ViewGroup) thisActivity.getWindow().getDecorView();
int w = decorView.getWidth();
int h = decorView.getHeight();
decorView.addView(view, w, h);
// 计算中心点至view边界的最大距离
int maxW = Math.max(cx, w - cx);
int maxH = Math.max(cy, h - cy);
final int finalRadius = (int) Math.sqrt(maxW * maxW + maxH * maxH) + 1;
Animator
anim = ViewAnimationUtils.createCircularReveal(view, cx, cy, 0, finalRadius);
int maxRadius = (int) Math.sqrt(w * w + h * h) + 1;
// 若使用默认时长,则需要根据水波扩散的距离来计算实际时间
if (durationMills == PERFECT_MILLS) {
// 算出实际边距与最大边距的比率
double rate = 1d * finalRadius / maxRadius;
// 水波扩散的距离与扩散时间成正比
durationMills = (long) (PERFECT_MILLS * rate);
}
final long finalDuration = durationMills;
anim.setDuration(finalDuration);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (requestCode == null)
thisActivity.startActivity(intent);
else if (bundle == null)
thisActivity.startActivityForResult(intent, requestCode);
else
thisActivity.startActivityForResult(intent, requestCode, bundle);
// 默认渐隐过渡动画.
thisActivity.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
// 默认显示返回至当前Activity的动画.
triggerView.postDelayed(new Runnable() {
@Override
public void run() {
Animator anim =
ViewAnimationUtils.createCircularReveal(view, cx, cy, finalRadius, 0);
anim.setDuration(finalDuration);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
try {
decorView.removeView(view);
} catch (Exception e) {
e.printStackTrace();
}
}
});
anim.start();
}
}, 1000);
}
});
anim.start();
}
/*下面的方法全是重载,用简化上面方法的构建*/
public static void startActivityForResult(
Activity thisActivity, Intent intent, Integer requestCode, View triggerView, int colorOrImageRes) {
startActivityForResult(thisActivity, intent, requestCode, null, triggerView, colorOrImageRes, PERFECT_MILLS);
}
public static void startActivity(
Activity thisActivity, Intent intent, View triggerView, int colorOrImageRes, long durationMills) {
startActivityForResult(thisActivity, intent, null, null, triggerView, colorOrImageRes, durationMills);
}
public static void startActivity(
Activity thisActivity, Intent intent, View triggerView, int colorOrImageRes) {
startActivity(thisActivity, intent, triggerView, colorOrImageRes, PERFECT_MILLS);
}
public static void startActivity(Activity thisActivity, Class<?> targetClass, View triggerView, int colorOrImageRes) {
startActivity(thisActivity, new Intent(thisActivity, targetClass), triggerView, colorOrImageRes, PERFECT_MILLS);
}
public static void show(View myView) {
show(myView, MINI_RADIUS, PERFECT_MILLS);
}
public static void hide(View myView) {
hide(myView, MINI_RADIUS, PERFECT_MILLS);
}
}
日历界面
/**
* Created by an on 2017/6/10.
*/
class CalendarActivity extends BaseActivity{
private Toolbar toolbar;
private MaterialCalendarView materialCalendarView;
private Button btOk;
private CalendarDay mDatas;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
btOk = (Button) findViewById(R.id.btn_ok);
materialCalendarView = (MaterialCalendarView) findViewById(R.id.calendarView);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setToolbar();
materialCalendarView.setOnDateChangedListener(new OnDateSelectedListener() {
@Override
public void onDateSelected(@NonNull MaterialCalendarView widget, @NonNull CalendarDay date, boolean selected) {
mDatas=date;
}
});
btOk.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mDatas!=null) {
EventBus.getDefault().post(mDatas);
}
finish();
}
});
}
private void setToolbar() {
toolbar.setTitle("选择日期");
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
@Override
public int getLayoutId() {
return R.layout.activity_calendar;
}
}
日历界面布局如下
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/toolbar"/>
<com.prolificinteractive.materialcalendarview.MaterialCalendarView
android:id="@+id/calendarView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:layout_marginTop="20dp"
android:textColor="@android:color/white"
android:text="确定"
android:background="@color/colorPrimary"
android:id="@+id/btn_ok"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<include layout="@layout/toolbar"/>中toolbar的布局如下
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/titile_bg"
android:fitsSystemWindows="true"
app:titleTextColor="@android:color/white" />
效果图如下

选择日期之后,确定就会把所选的日期时间返回到上一级页面,只需要在那个界面做接受数据的处理就行了。
在这里使用EventBus来监听数据的变化
compile 'org.greenrobot:eventbus:3.0.0'
在显示日期的界面中获取返回的时间数据代码如下
@Subscribe
public void onEventMainThread(CalendarDay date) {
currentYear=date.getYear();
currentMonth=date.getMonth()+1;
currentDay=date.getDay();
Log.d(TAG, "-------------------onEventMainThread: -------------------"+currentDay);
setTVTime();
}
//设置时间年月日
public void setTVTime() {
tvTime.setText(currentYear + "年" + currentMonth + "月" + currentDay + "日");
}
此时日历选择功能基本结束了,不清楚的可以看看“历史的今天”这个项目。
网友评论