心血来潮想开发个小玩意练练手,于是就有了上面标题的创意,自己一直想用下郭霖大神的开源数据库项目LitePal,再加之想要每天进行推送,于是跟日历结合起来有了下面的构想,项目简单点,对于我这个菜鸟而言就不会半途而废

,希望能够顺利写完。打算用三个篇幅来写,分别是

1。自定义日历控件部分(一),

2。极光推送集成以及富媒体消息推送部分(二),

3。LitePal数据库集成部分(三)。

初步的构想大概类似这样的功能

android studio实现显示农历 安卓studio编写一个日历_android


下面就想从自定义日历开始。自定义日历的教程参考慕课网《自定义实现日历件》http://www.imooc.com/learn/775

先看下我做出来的效果

android studio实现显示农历 安卓studio编写一个日历_LitePal练习_02


好了,开始撸代码,日历的实现不是纯自定义的View , 而是借助系统的控件组合而成的,贴出XML文件大家就一目了然了,这是日历组合布局文件canlendar_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <!--头部布局-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="32dp"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:paddingLeft="5dp"
        android:paddingRight="5dp">

        <ImageView
            android:id="@+id/pre_month_btn"
            android:layout_width="32dp"
            android:layout_height="32dp"
            android:src="@android:drawable/ic_media_previous" />

        <TextView
            android:id="@+id/cal_title_tv"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="May 2017"
            android:textSize="20sp" />

        <ImageView
            android:id="@+id/next_month_btn"
            android:layout_width="32dp"
            android:layout_height="32dp"
            android:src="@android:drawable/ic_media_next" />

    </LinearLayout>

    <LinearLayout
        android:id="@+id/week_title_layout"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:orientation="horizontal">

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="星期日"
            android:textSize="16sp" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="星期一"
            android:textSize="16sp" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="星期二"
            android:textSize="16sp" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="星期三"
            android:textSize="16sp" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="星期四"
            android:textSize="16sp" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="星期五"
            android:textSize="16sp" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="星期六"
            android:textSize="16sp" />

    </LinearLayout>

    <!--日历布局-->
    <GridView
        android:id="@+id/days_grid_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:numColumns="7" />

</LinearLayout>

还是比较简单的,一个头部LinearLayout和一个GridView组合就好了,主要看下实现类的写法,业务逻辑都在实现类中完成的。对应的业务实现类MyCalendarView.java

public class MyCalendarView extends LinearLayout{

    private ImageView mPreBtn , mNextBtn ;
    private TextView mCalendarTitle ;
    private GridView mDaysGridView ;

    public OnCalendarItemClickListener listener ;
    public void setOnCalendarItemClickListener(OnCalendarItemClickListener listener) {
        this.listener = listener;
    }

    private Calendar cruDate = Calendar.getInstance() ; // 全局日历

    public MyCalendarView(Context context) {
        this(context,null);
    }

    public MyCalendarView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
        bindView(context);
    }

    public MyCalendarView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        bindView(context);
    }

    private void bindView(Context context){
        View v = LayoutInflater.from(context).inflate(R.layout.canlendar_view , this , true);
        mPreBtn = (ImageView) v.findViewById(R.id.pre_month_btn);
        mNextBtn = (ImageView) v.findViewById(R.id.next_month_btn);
        mCalendarTitle = (TextView) v.findViewById(R.id.cal_title_tv);
        mDaysGridView = (GridView) v.findViewById(R.id.days_grid_view);
        renderCalendar();
        // 前一月
        mPreBtn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                cruDate.add(Calendar.MONTH , -1);
                renderCalendar();
            }
        });
        // 后一月
        mNextBtn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                cruDate.add(Calendar.MONTH , 1);
                renderCalendar();
            }
        });
    }

    /**
     * 核心的逻辑业务处理部分
     */
    private void renderCalendar(){
        // 设置标题栏
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月");
        mCalendarTitle.setText(sdf.format(cruDate.getTime()));

        // 准备要显示的数据
        ArrayList<Date> cells = new ArrayList<>();
        Calendar calendar = (Calendar) cruDate.clone();
        calendar.set(Calendar.DAY_OF_MONTH,1);
        int preDays = calendar.get(Calendar.DAY_OF_WEEK ) - 1 ;
        calendar.add(Calendar.DAY_OF_MONTH,-preDays);

        int maxDays = 6 * 7 ; // 一个月最多需要6行显示完全,每行7天
        while (cells.size() < maxDays){ // 填充数据
            cells.add(calendar.getTime());
            calendar.add(Calendar.DAY_OF_MONTH,1);
        }
        // 为GridView设置适配器
        mDaysGridView.setAdapter(new CalArrayAdapter(getContext(),cells));
        // 设置单个日期点击的接口
        mDaysGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if(listener == null){
                    return;
                }else{
                    listener.onCalendarItemClickListener((Date) parent.getItemAtPosition(position));
                }
            }
        });
    }

    /**
     * 日历显示数据的GridView的适配器
     */
    private class CalArrayAdapter extends ArrayAdapter<Date>{

        LayoutInflater inflater ;

        public CalArrayAdapter(Context context,  ArrayList<Date> days) {
            super(context, R.layout.calendar_day_text , days);
            inflater = LayoutInflater.from(context);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            Date date = getItem(position);
            if(convertView == null){
                convertView = inflater.inflate(R.layout.calendar_day_text, parent, false);
            }
            int day = date.getDate();
            ((TodayTextView)convertView).setText(String.valueOf(day));

            Date now = new Date();
            // 当月
            if(date.getMonth() == now.getMonth()){
                ((TodayTextView)convertView).setTextColor(Color.BLACK);
            }else{
                ((TodayTextView)convertView).setTextColor(Color.GRAY);
            }
            // 当天
            if(date.getYear() == now.getYear()
                    && date.getMonth() == now.getMonth()
                    && date.getDate() == now.getDate()){
                ((TodayTextView)convertView).setTextColor(Color.RED); // 当天标红
                ((TodayTextView)convertView).isToday = true;
            }
            return convertView;
        }
    }

    /**
     * 自定义日历单个日期点击的接口
     */
    public interface OnCalendarItemClickListener{
        // 传入选中的日期
        void onCalendarItemClickListener(Date day);
    }

}

多的我就不再啰嗦了,代码中的注释还是比较多的,如果有什么不清楚的,完全可以拷贝然后试着运行下,相信你能更加理解我说的意思了,上述代码中有一个自定义的

TodayTextView.java

public class TodayTextView extends TextView {

    private Paint mPaint = new Paint();
    public boolean isToday = false ; // 确认是否是当天

    public TodayTextView(Context context) {
        super(context);
    }

    public TodayTextView(Context context, AttributeSet attrs) {
        this(context, attrs , 0); 
    }

    public TodayTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // 设置画笔
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(3);
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.RED);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.translate(getWidth()/2 , getHeight()/2); // 移动到中心位置
        if(isToday){
            canvas.drawCircle(0 , 0 , getWidth()/2 - 3 ,mPaint); // 画圈
        }
    }
}

这个自定义的View主要用来显示区分当天,这里做的主要工作就是把当前天用红色圆圈圈出来,当然你也可以做更多的事情,比如把某个选中的View换个颜色显示。

还有多一个布局文件,就是上面Adapter中渲染的一个Layout布局文件calendar_day_text.xml


<?xml version="1.0" encoding="utf-8"?>
<com.cjt.customcalendar.view.TodayTextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/cal_day_txt_tv"
    android:layout_width="36dp"
    android:layout_height="36dp"
    android:gravity="center"
    android:textSize="20sp">

</com.cjt.customcalendar.view.TodayTextView>



简单的引用了自定义的布局文件。MianActivity 和对应的activity_main.xml任然灰常简单


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.cjt.customcalendar.MainActivity">

    <com.cjt.customcalendar.view.MyCalendarView
        android:id="@+id/MyCalendarView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>




public class MainActivity extends AppCompatActivity 
        implements MyCalendarView.OnCalendarItemClickListener {

    private MyCalendarView mCalendarView ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mCalendarView = (MyCalendarView) this.findViewById(R.id.MyCalendarView);
        mCalendarView.setOnCalendarItemClickListener(this);
    }

    @Override
    public void onCalendarItemClickListener(Date day) {
        Toast.makeText(this,"Date---"+day.toString(),Toast.LENGTH_SHORT).show();
    }
}


注意:MainActivity实现了自定义View中的那个接口,所以才能弹出效果图中的Toast提示,当然,你也可以直接使用GridView.setOnclickListener来实现。

第一部分的效果:完成了自定义日历控件,能够响应日历的点击事件,为后面的数据查询做准备,第一部分就到此为止。