Android 画一个萌萌哒日历吧o(* ̄▽ ̄*)ブ

效果图

这里写图片描述

前言

分为三个部分:
- 自定义星期条和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 <weekString.length;i++){
            String text = weekString[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);
        }

    }
}

DayTextView

继承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);
        }
    }
}

自定义组合控件

自定义组合控件是将多个控件组合在一起形成一个新的控件。而我们的日历控件整体上分为了三个部分,所以采用这种方式。

日历组合控件的xml

首先我们创建一个calendar_view.xml文件。简单分析一下,三个部分自上而下排布,所以可以采用线性布局。第一个部分左右两边各一个按钮,中间是一个textview。第二部分为星期条,这一部分的实现我选择了自定义去画一个。第三部分用GridView实现。

<?xml version="1.0" encoding="utf-8"?>
<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代码

接下来就可以编写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<Date> 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即可

<?xml version="1.0" encoding="utf-8"?>
<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<Date> 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();
    }
}

NewCalender完整代码


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<Date> 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<Date> {
        LayoutInflater layoutInflater;
        public CalendarAdapter(@NonNull Context context, ArrayList<Date> 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);
   }
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值