Edited at

[Android]GridViewを使ってカレンダーを作成する

More than 1 year has passed since last update.

iOSだとUICollectionViewを使ってカレンダーを作る方法がたくさん出てくるのに、AndroidではGridViewを使ってカレンダーを作る方法があまり出てこなかったのでメモです。


完成形

Screenshot_1518517193.png

こんな感じです。

では順番に作成していきます。


カラーリソースを定義

xmlで使う色を定義するだけです。

特にカレンダーも GridViewも関係ないですね・・;


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>


メインアクティビティのレイアウトを記述


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="日"
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="月"
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="火"
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="水"
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="木"
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="金"
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="土"
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>


枠線を描くのがめんどくさい大変なので、背景に色をつけて、マージンで罫線を描いていきます。


グリッドビューのセルのレイアウトファイルを作成

今回は日付のみ表示しているのでカスタムセルを作るまでないかもですが、今後の拡張のためにも自作します。


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"/>

<!--ここに拡張可能-->

</RelativeLayout>



DateManagerクラスを作成

カレンダーは日付を操作することが多いので、専用クラスを作っておきます。


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();
}

//当月の要素を取得
public List<Date> getDays(){
//現在の状態を保持
Date startDate = mCalendar.getTime();

//GridViewに表示するマスの合計を計算
int count = getWeeks() * 7 ;

//当月のカレンダーに表示される前月分の日数を計算
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);
}

//状態を復元
mCalendar.setTime(startDate);

return days;
}

//当月かどうか確認
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;
}
}

//週数を取得
public int getWeeks(){
return mCalendar.getActualMaximum(Calendar.WEEK_OF_MONTH);
}

//曜日を取得
public int getDayOfWeek(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return calendar.get(Calendar.DAY_OF_WEEK);
}

//翌月へ
public void nextMonth(){
mCalendar.add(Calendar.MONTH, 1);
}

//前月へ
public void prevMonth(){
mCalendar.add(Calendar.MONTH, -1);
}
}



GridViewにセットするAdapterクラスを作成

今回はアダプタークラスに月を変更するメソッドとかも定義して、少し横着をしています。

ご容赦ください。。。


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;

//カスタムセルを拡張したらここでWigetを定義
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();
}

//セルのサイズを指定
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);

//日付のみ表示させる
SimpleDateFormat dateFormat = new SimpleDateFormat("d", Locale.US);
holder.dateText.setText(dateFormat.format(dateArray.get(position)));

//当月以外のセルをグレーアウト
if (mDateManager.isCurrentMonth(dateArray.get(position))){
convertView.setBackgroundColor(Color.WHITE);
}else {
convertView.setBackgroundColor(Color.LTGRAY);
}

//日曜日を赤、土曜日を青に
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;
}

//表示月を取得
public String getTitle(){
SimpleDateFormat format = new SimpleDateFormat("yyyy.MM", Locale.US);
return format.format(mDateManager.mCalendar.getTime());
}

//翌月表示
public void nextMonth(){
mDateManager.nextMonth();
dateArray = mDateManager.getDays();
this.notifyDataSetChanged();
}

//前月表示
public void prevMonth(){
mDateManager.prevMonth();
dateArray = mDateManager.getDays();
this.notifyDataSetChanged();
}
}



メインアクティビティを記述


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());
}

}


これで一通り動作します。

カレンダーを選択した時に動作させたい時はGridViewにsetOnItemClickListenerを設定して、CalendarAdapterのgetItemgetItemIdあたりを絡めてゴニョゴニョしてください。

本当はMainActivityでDateManagerを生成してAdapterに受け渡して・・・という方がお行儀が良いのでしょうが、今回はこれで。。。