分为三个部分:
- 自定义星期条和textview
- 自定义组合控件
- 业务处理和点击事件
创建一个WeekView继承View,画出上下两条线以及周数即可。
package com.example.administrator.myapplication;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
public class WeekView extends View {
private int mLineColor = Color.parseColor("#e68c8c");
private int mWeekendColor = Color.parseColor("#FFDF5252");
private int mWeedayColor = Color.parseColor("#e68c8c");
private Paint paint;
public WeekView(Context context) {
super(context);
}
public WeekView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint();}
public WeekView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
paint = new Paint();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
String[] weekString = new String[]{"日","一","二","三","四","五","六"};
//画上下横线
paint.setStyle(Paint.Style.STROKE);
paint.setColor(mLineColor);
paint.setStrokeWidth(4);
canvas.drawLine(0 , 0 ,width,0,paint);
canvas.drawLine(0,height,width,height,paint);
//画周数
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(40);
int columnWidth = width/7;
for (int i = 0 ;i int fontWidth = (int) paint.measureText(text);
int startX = columnWidth*i + (columnWidth-fontWidth)/2;
int startY = (int) (height/2 - (paint.ascent() + paint.descent())/2);
if(text.indexOf("日") > -1|| text.indexOf("六") > -1){
paint.setColor(mWeekendColor);
}else{
paint.setColor(mWeedayColor);
}
canvas.drawText(text, startX, startY, paint);
}
}
}
继承TextView,重写onDraw。
public class DayTextView extends android.support.v7.widget.AppCompatTextView {
//默认为false,即不绘圈
public Boolean isToday = false;
private Paint paint= new Paint();
public DayTextView(Context context) {
super(context);
initControl();
}
public DayTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initControl();
}
public DayTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initControl();
}
private void initControl() {
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.parseColor("#c15757"));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isToday){
//画圈
canvas.translate(getWidth()/2,getHeight()/2);
canvas.drawCircle(0,0,getHeight()/2,paint);
}
}
}
自定义组合控件是将多个控件组合在一起形成一个新的控件。而我们的日历控件整体上分为了三个部分,所以采用这种方式。
首先我们创建一个calendar_view.xml文件。简单分析一下,三个部分自上而下排布,所以可以采用线性布局。第一个部分左右两边各一个按钮,中间是一个textview。第二部分为星期条,这一部分的实现我选择了自定义去画一个。第三部分用GridView实现。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:orientation="vertical"
android:background="@drawable/bgup"
>
<RelativeLayout
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_width="match_parent"
android:layout_height="50dp">
<ImageView
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:id="@+id/iv_pre"
android:src="@drawable/pre"
/>
<ImageView
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:id="@+id/iv_next"
android:src="@drawable/next"
/>
<TextView
android:id="@+id/tv_data"
android:textColor="#e68c8c"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="JUNE 2017"
android:textSize="20sp"
android:layout_centerInParent="true"/>
RelativeLayout>
<com.example.administrator.myapplication.WeekView
android:layout_width="match_parent"
android:layout_height="50dp" />
LinearLayout>
<GridView
android:layout_marginLeft="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/gl_data"
android:numColumns="7"
>
GridView>
LinearLayout>
接下来就可以编写Java代码了。因为我们之前的整体布局是采用的线性布局,所以创建一个NewCalender继承LinearLayout并重写相应的构造方法。
首先要做的就是绑定xml中的控件
private void bindControl(Context context) {
LayoutInflater inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.calendar_view,this);
ivPre = findViewById(R.id.iv_pre);
ivNext = findViewById(R.id.iv_next);
tvData = findViewById(R.id.tv_data);
glData = findViewById(R.id.gl_data);
}
然后就是画出第三部分,日历的内容。
首先获得Calendar的一个实例。
private Calendar curData = Calendar.getInstance();
创建一个方法。
private void rendaerCalendar(){
//设置当前年月
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MMM yyyy");
tvData.setText(simpleDateFormat.format(curData.getTime()));
//创建一个list
ArrayList cells = new ArrayList<>();
//创建作为当前 Calendar 对象副本的新对象,避免影响数据
Calendar calendar = (Calendar) curData.clone();
//设置为当月的第一天
calendar.set(Calendar.DAY_OF_MONTH,1);
/**
获得当月的第一天为本周的第几天,假如六月的一号为星期天,那么就为第七天,简单分析可以发现,
还需要显示上个月的最后六天。也就是说,我们要显示的上个月的时间为当月的一号在本周的第几天减一
**/
int preDays = calendar.get(Calendar.DAY_OF_WEEK) - 1;
/**
我们一个月最多为31天,也就是说一般情况下,五行就可以显示完本月的所有天数,可是,有个地方需要注意,
就是当本月为30天的时候,当月一号为星期日,以及本月为31天的时候,一号为星期六或者星期天,那么就需要6行。
所以这里不能简单的设置日历格数为5*7
**/
int maxCellCount;
if( (calendar.getActualMaximum(Calendar.DAY_OF_MONTH)==30&&preDays>=6) ||
(calendar.getActualMaximum(Calendar.DAY_OF_MONTH)==31&&preDays>=5)) {
maxCellCount = 6*7;
}else {
maxCellCount = 5*7;
}
//设置到我们要显示日期中的第一天
calendar.add(Calendar.DAY_OF_MONTH, - preDays);
//依次添加到LIST中
while(cells.size() < maxCellCount ){
cells.add(calendar.getTime());
calendar.add(Calendar.DAY_OF_MONTH,1);
}
//给GridView设置适配器
glData.setAdapter(new CalendarAdapter(getContext(),cells));
glData.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView> parent, View view, int position, long id) {
if (listener == null){
return false;
}else {
listener.onItemLongPressed((Date) parent.getItemAtPosition(position));
return true;
}
}
});
}
接下要是Gridview的item_date.xml,只需要一个DayTextView即可
<com.example.administrator.myapplication.DayTextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="32dp"
android:layout_height="32dp"
android:id="@+id/tv_date"
android:gravity="center"
>
com.example.administrator.myapplication.DayTextView>
适配器就用ArrayAdapter好啦
private class CalendarAdapter extends ArrayAdapter<Date> {
LayoutInflater layoutInflater;
public CalendarAdapter(@NonNull Context context, ArrayList days) {
super(context, R.layout.item_date,days);
layoutInflater = LayoutInflater.from(context);
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
//获得当前显示的date
Date date = getItem(position);
if(convertView == null){
convertView = layoutInflater.inflate(R.layout.item_date,parent,false);
}
int day = date.getDate();
((TextView)convertView).setText(String.valueOf(day));
//获得当前显示的月份
Date nowM = curData.getTime();
//判断是否为本月日期,本月日期深色显示
if (date.getMonth() == nowM.getMonth()){
((DayTextView)convertView).setTextColor(Color.parseColor("#FFDF5252"));
}else{
((DayDayTextView)convertView).setTextColor(Color.parseColor("#e68c8c"));
}
//判断当前日期是否与系统时间是同一天,若是则设置isToady为true,画圈。
if (DateUtils.isToday(date.getTime())){
((DayTextView)convertView).setTextColor(Color.parseColor("#FFDF5252"));
((DayTextView)convertView).isToday = true;
}
return convertView;
}
}
接下来就是点击事件的绑定。
private void bindControlEvents(final Context context) {
ivNext.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//向前加一个月
curData.add(Calendar.MONTH, 1 );
//重新渲染
rendaerCalendar();
}
});
ivPre.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//向后加一个月
curData.add(Calendar.MONTH, -1 );
//重新渲染
rendaerCalendar();
}
});
}
写一个接口
public interface newCalendarListener {
void onItemLongPressed(Date date);
}
在渲染方法中给gridView添加长按点击事件
glData.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView> parent, View view, int position, long id) {
if (listener == null){
return false;
}else {
listener.onItemLongPressed((Date) parent.getItemAtPosition(position));
return true;
}
}
});
需要监听时,只需实现接口并将接口传入给日历控件即可。
public class MainActivity extends AppCompatActivity implements NewCalendar.newCalendarListener {
private NewCalendar newCalendar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
newCalendar = findViewById(R.id.newCalendar);
newCalendar.listener = this;
}
@Override
public void onItemLongPressed(Date date) {
DateFormat dateFormat = SimpleDateFormat.getDateInstance();
Toast.makeText(this, dateFormat.format(date), Toast.LENGTH_LONG).show();
}
}
public class NewCalendar extends LinearLayout{
private ImageView ivPre;
private ImageView ivNext;
private TextView tvData;
private GridView glData;
private Calendar curData = Calendar.getInstance();
public newCalendarListener listener;
public NewCalendar(Context context) {
super(context);
initControl(context);
}
public NewCalendar(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initControl(context);
}
public NewCalendar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initControl(context);
}
private void initControl(Context context) {
bindControl(context);
bindControlEvents(context);
rendaerCalendar();
}
private void bindControlEvents(final Context context) {
ivNext.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
curData.add(Calendar.MONTH, 1 );
rendaerCalendar();
}
});
ivPre.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
curData.add(Calendar.MONTH, -1 );
rendaerCalendar();
}
});
}
private void rendaerCalendar(){
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MMM yyyy");
tvData.setText(simpleDateFormat.format(curData.getTime()));
ArrayList cells = new ArrayList<>();
Calendar calendar = (Calendar) curData.clone();
calendar.set(Calendar.DAY_OF_MONTH,1);
int preDays = calendar.get(Calendar.DAY_OF_WEEK) - 1;
int maxCellCount;
if( (calendar.getActualMaximum(Calendar.DAY_OF_MONTH)<=30&&preDays>=6) ||
(calendar.getActualMaximum(Calendar.DAY_OF_MONTH)>30&&preDays>=5)) {
maxCellCount = 6*7;
}else {
maxCellCount = 5*7;
}
calendar.add(Calendar.DAY_OF_MONTH, - preDays);
while(cells.size() < maxCellCount ){
cells.add(calendar.getTime());
calendar.add(Calendar.DAY_OF_MONTH,1);
}
glData.setAdapter(new CalendarAdapter(getContext(),cells));
glData.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView> parent, View view, int position, long id) {
if (listener == null){
return false;
}else {
listener.onItemLongPressed((Date) parent.getItemAtPosition(position));
return true;
}
}
});
}
private class CalendarAdapter extends ArrayAdapter {
LayoutInflater layoutInflater;
public CalendarAdapter(@NonNull Context context, ArrayList days) {
super(context, R.layout.item_date,days);
layoutInflater = LayoutInflater.from(context);
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Date date = getItem(position);
if(convertView == null){
convertView = layoutInflater.inflate(R.layout.item_date,parent,false);
}
int day = date.getDate();
((DayTextView)convertView).setText(String.valueOf(day));
Date nowM = curData.getTime();
if (date.getMonth() == nowM.getMonth()){
((DayTextView)convertView).setTextColor(Color.parseColor("#FFDF5252"));
}else{
((DayTextView)convertView).setTextColor(Color.parseColor("#e68c8c"));
}
if (DateUtils.isToday(date.getTime())){
((DayTextView)convertView).setTextColor(Color.parseColor("#FFDF5252"));
((DayTextView)convertView).isToday = true;
}
return convertView;
}
}
private void bindControl(Context context) {
LayoutInflater inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.calendar_view,this);
ivPre = findViewById(R.id.iv_pre);
ivNext = findViewById(R.id.iv_next);
tvData = findViewById(R.id.tv_data);
glData = findViewById(R.id.gl_data);
}
public interface newCalendarListener {
void onItemLongPressed(Date date);
}
}