[JAVA] [Android] Create a calendar using GridView

On iOS, there are many ways to create a calendar using UICollectionView, but on Android, there aren't many ways to create a calendar using GridView, so it's a memo.

Completed form

Screenshot_1518517193.png

It is like this.

Let's create them in order.

Define color resources

Just define the colors to use in xml. Especially the calendar and GridView have nothing to do with it ...;

colors.xml


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>
    <color name="grayColor">#777</color>
    <color name="blueColor">#00F</color>
    <color name="redColor">#F00</color>
    <color name="whiteColor">#FFF</color>
    <color name="blackColor">#000</color>
</resources>

Describe the layout of the main activity

activity_main.xml


<?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="match_parent"
    android:orientation="vertical"
    android:background="@color/grayColor"
    tools:context="jp.co.apps.workout.calendarsample.MainActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:background="@color/whiteColor">

        <TextView
            android:id="@+id/titleText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="2018.2"
            android:textSize="20sp"
            android:layout_centerInParent="true"/>

        <Button
            android:id="@+id/prevButton"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:text="Prev"
            android:layout_alignParentLeft="true"
            android:layout_marginVertical="10dp"
            android:layout_marginLeft="10dp"
            android:background="@color/colorAccent"/>

        <Button
            android:id="@+id/nextButton"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:text="Next"
            android:layout_alignParentRight="true"
            android:layout_marginVertical="10dp"
            android:layout_marginRight="10dp"
            android:background="@color/colorAccent"/>
    </RelativeLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginVertical="1dp">

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:layout_marginHorizontal="1dp"
            android:textAlignment="center"
            android:text="Day"
            android:background="@color/whiteColor"
            android:textColor="@color/redColor"/>

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginRight="1dp"
            android:layout_weight="1"
            android:background="@color/whiteColor"
            android:text="Month"
            android:textColor="@color/blackColor"
            android:textAlignment="center"/>

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:layout_marginRight="1dp"
            android:textAlignment="center"
            android:text="fire"
            android:textColor="@color/blackColor"
            android:background="@color/whiteColor"/>

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:layout_marginRight="1dp"
            android:textAlignment="center"
            android:text="water"
            android:textColor="@color/blackColor"
            android:background="@color/whiteColor"/>

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:layout_marginRight="1dp"
            android:textAlignment="center"
            android:text="wood"
            android:textColor="@color/blackColor"
            android:background="@color/whiteColor"/>

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:layout_marginRight="1dp"
            android:textAlignment="center"
            android:text="Money"
            android:textColor="@color/blackColor"
            android:background="@color/whiteColor"/>

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:layout_marginRight="1dp"
            android:textAlignment="center"
            android:text="soil"
            android:textColor="@color/blueColor"
            android:background="@color/whiteColor"/>

    </LinearLayout>

    <GridView
        android:id="@+id/calendarGridView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="15"
        android:horizontalSpacing="1dp"
        android:layout_marginLeft="1dp"
        android:numColumns="7"
        android:stretchMode="columnWidth"
        android:verticalSpacing="1dp"></GridView>

</LinearLayout>

It's difficult to draw a border, so I'll color the background and draw a ruled line with a margin.

Create a grid view cell layout file

Since only the date is displayed this time, it may not be necessary to create a custom cell, but I will create it for future expansion.

calendar_cell.xml


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

    <TextView
        android:id="@+id/dateText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAlignment="center"/>
    
    <!--Expandable here-->

</RelativeLayout>

Create DateManager class

Calendars often manipulate dates, so create a dedicated class.

DateManager.java


package jp.co.apps.workout.calendarsample;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;


public class DateManager {
    Calendar mCalendar;

    public DateManager(){
        mCalendar = Calendar.getInstance();
    }

    //Get the elements of the month
    public List<Date> getDays(){
        //Keep current state
        Date startDate = mCalendar.getTime();

        //Calculate the total number of squares to be displayed in GridView
        int count = getWeeks() * 7 ;
        
        //Calculate the number of days for the previous month displayed on the calendar for the current month
        mCalendar.set(Calendar.DATE, 1);
        int dayOfWeek = mCalendar.get(Calendar.DAY_OF_WEEK) - 1;
        mCalendar.add(Calendar.DATE, -dayOfWeek);

        List<Date> days = new ArrayList<>();
        
        for (int i = 0; i < count; i ++){
            days.add(mCalendar.getTime());
            mCalendar.add(Calendar.DATE, 1);
        }
        
        //Restore state
        mCalendar.setTime(startDate);
        
        return days;
    }

    //Check if it is this month
    public boolean isCurrentMonth(Date date){
        SimpleDateFormat format = new SimpleDateFormat("yyyy.MM", Locale.US);
        String currentMonth = format.format(mCalendar.getTime());
        if (currentMonth.equals(format.format(date))){
            return true;
        }else {
            return false;
        }
    }

    //Get the number of weeks
    public int getWeeks(){
        return mCalendar.getActualMaximum(Calendar.WEEK_OF_MONTH);
    }

    //Get the day of the week
    public int getDayOfWeek(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.DAY_OF_WEEK);
    }

    //To the next month
    public void nextMonth(){
        mCalendar.add(Calendar.MONTH, 1);
    }

    //To the previous month
    public void prevMonth(){
        mCalendar.add(Calendar.MONTH, -1);
    }
}

Create an Adapter class to set in GridView

This time, I also defined a method to change the month in the adapter class, and I'm wearing it a little sideways. Please pardon. .. ..

CalendarAdapter.java


package jp.co.apps.workout.calendarsample;

import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

public class CalendarAdapter extends BaseAdapter {
    private List<Date> dateArray = new ArrayList();
    private Context mContext;
    private DateManager mDateManager;
    private LayoutInflater mLayoutInflater;

    //After expanding the custom cell, define Wiget here
    private static class ViewHolder {
        public TextView dateText;
    }

    public CalendarAdapter(Context context){
        mContext = context;
        mLayoutInflater = LayoutInflater.from(mContext);
        mDateManager = new DateManager();
        dateArray = mDateManager.getDays();
    }

    @Override
    public int getCount() {
        return dateArray.size();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            convertView = mLayoutInflater.inflate(R.layout.calendar_cell, null);
            holder = new ViewHolder();
            holder.dateText = convertView.findViewById(R.id.dateText);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder)convertView.getTag();
        }

        //Specify cell size
        float dp = mContext.getResources().getDisplayMetrics().density;
        AbsListView.LayoutParams params = new AbsListView.LayoutParams(parent.getWidth()/7 - (int)dp, (parent.getHeight() - (int)dp * mDateManager.getWeeks() ) / mDateManager.getWeeks());
        convertView.setLayoutParams(params);

        //Display only the date
        SimpleDateFormat dateFormat = new SimpleDateFormat("d", Locale.US);
        holder.dateText.setText(dateFormat.format(dateArray.get(position)));

        //Gray out cells other than this month
        if (mDateManager.isCurrentMonth(dateArray.get(position))){
            convertView.setBackgroundColor(Color.WHITE);
        }else {
            convertView.setBackgroundColor(Color.LTGRAY);
        }

        //Sunday to red, Saturday to blue
        int colorId;
        switch (mDateManager.getDayOfWeek(dateArray.get(position))){
            case 1:
                colorId = Color.RED;
                break;
            case 7:
                colorId = Color.BLUE;
                break;

            default:
                colorId = Color.BLACK;
                break;
        }
        holder.dateText.setTextColor(colorId);
        
        return convertView;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }
    
    @Override
    public Object getItem(int position) {
        return null;
    }

    //Get display month
    public String getTitle(){
        SimpleDateFormat format = new SimpleDateFormat("yyyy.MM", Locale.US);
        return format.format(mDateManager.mCalendar.getTime());
    }

    //Next month display
    public void nextMonth(){
        mDateManager.nextMonth();
        dateArray = mDateManager.getDays();
        this.notifyDataSetChanged();
    }

    //Previous month display
    public void prevMonth(){
        mDateManager.prevMonth();
        dateArray = mDateManager.getDays();
        this.notifyDataSetChanged();
    }
}

Describe the main activity

MainActivity.java


package jp.co.apps.workout.calendarsample;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.GridView;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private TextView titleText;
    private Button prevButton, nextButton;
    private CalendarAdapter mCalendarAdapter;
    private GridView calendarGridView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        titleText = findViewById(R.id.titleText);
        prevButton = findViewById(R.id.prevButton);
        prevButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCalendarAdapter.prevMonth();
                titleText.setText(mCalendarAdapter.getTitle());
            }
        });
        nextButton = findViewById(R.id.nextButton);
        nextButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCalendarAdapter.nextMonth();
                titleText.setText(mCalendarAdapter.getTitle());
            }
        });
        calendarGridView = findViewById(R.id.calendarGridView);
        mCalendarAdapter = new CalendarAdapter(this);
        calendarGridView.setAdapter(mCalendarAdapter);
        titleText.setText(mCalendarAdapter.getTitle());
    }

}

This works all the way. If you want to operate when you select a calendar, set setOnItemClickListener in GridView and entangle around getItem and getItemId of CalendarAdapter.

Actually, it would be better to create a DateManager with MainActivity and pass it to the Adapter ... but this time. .. ..

Recommended Posts

[Android] Create a calendar using GridView
[Android] Create a sliding menu without using NavigationView
Creating a calendar using Ruby
Create a fortune using Ruby
Create a Jetty project using Eclipse
Create a tomcat project using Eclipse
Create a Java project using Eclipse
Create a filtering function using acts-as-taggable-on
[Kotlin / Android] Create a custom view
Create a web environment quickly using Docker
Create a RESTful API service using Grape
Create a prefectural select bar using active_hash
Create a login function using Swift's Optional
Create a Privoxy + Tor environment instantly using Docker
[Android] Inherit ImageView to create a new class
Let's create a REST API using WildFly Swarm.
[Rails] How to create a graph using lazy_high_charts
Create a Spring Boot application using IntelliJ IDEA
Create a portfolio app using Java and Spring Boot
Create a Java development environment using jenv on Mac
I tried using a database connection in Android development
[Java] Create a filter
A memorandum when trying to create a GUI using JavaFX
Create a login authentication screen using the session function
Creating a 2021 weekly calendar (refill for personal organizer) using Ruby
How to create a form to select a date from the calendar
[Beginner] android app that rolls a ball using a sensor [Java]
Create a tomcat project using Eclipse Pleiades All in One
Create a MOB using the Minecraft Java Mythicmobs plugin | Preparation 1
Create a java method [Memo] [java11]
[Java] Create a temporary file
Create a VS Code Plugin.
Create a playground with Xcode 12
Make a rhombus using Java
How to create a method
Create a name input function
Create a clear time ranking in Firebase's Realtime Database (Android app)
How to create a jar file or war file using the jar command
I tried to create a simple map app in Android Studio
Create a memo app with Tomcat + JSP + Servlet + MySQL using Eclipse
[Rails 6] How to create a dynamic form input screen using cocoon
Create a static file that expands variables using the ERB class
[Implementation procedure] Create a user authentication function using sorcery in Rails
[Android] Create a square view while maintaining the aspect ratio: SquareLayout
Easy way to create a mapping class when using the API
Oracle Cloud [OCI]: Create a CentOS Stream instance using Compute's cloud-init
Create a calendar from the Calendar class by specifying the year and month