Android向けカスタムカレンダー作成
CalendarViewを使用して、カレンダーを作成しようと思ったのですが、
せっかくなのでカスタムダイアログにて実装してみました。
標準で提供されるダイアログを使用するのもいいですが、
よりアプリケーションをリッチにするための勉強の一貫として挑戦です。
備忘録として記録していきたいと思います。
※カスタムカレンダーを作成するにあたり、以下のサイトを参考にさせて頂きました。
Android Tips #45 Dialog をフルカスタマイズする | Developers.IO
ダイアログ起動時のイメージ
① まずは背景色などを定義する用のXMLを作成していきます。
1)bg_dialog.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <stroke android:width="2dp" android:color="#FFBA6F" /> <gradient android:startColor="#ffffff" android:endColor="#dcdcdc" android:angle="90"/> </shape>
ダイアログの背景
2)bg_grad.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <gradient android:startColor="#FFBA6F" android:endColor="#FF8F52" android:angle="0"/> </shape>
タイトルの背景
3)bt_dilog_close.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="@android:color/white"/> <stroke android:width="3dp" android:color="#FB8F61" /> </shape>
処理終了ボタンの背景
4)bt_dilog_positive,xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <solid android:color="#FFBA6F" /> </shape>
OKボタンの背景
② 次にダイアログのレイアウトファイルを作成します。
1)calendar_dialog.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" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_marginTop="6dp" android:layout_marginLeft="6dp" android:layout_marginRight="6dp" android:background="@drawable/bg_dialog" android:gravity="center_horizontal" android:orientation="vertical" android:weightSum="1"> <!-- タイトル --> <TextView android:id="@+id/calendarTitle" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/bg_grad" android:padding="10dp" android:textSize="17sp" android:textColor="@android:color/white" android:fontFamily="SERIF" android:textStyle="italic" /> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="3dp" android:layout_marginLeft="2dp" android:layout_marginRight="2dp" android:background="#fecfb1"> </LinearLayout> <TableLayout android:layout_width="match_parent" android:layout_height="match_parent" android:stretchColumns="0"> <TableRow android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:text="2015" android:id="@+id/yearTextView" android:gravity="center" android:textStyle="italic" android:layout_gravity="center" android:fontFamily="SERIF" /> </TableRow> </TableLayout> <TableLayout android:layout_width="match_parent" android:layout_height="match_parent" android:stretchColumns="1"> <TableRow android:layout_width="match_parent" android:layout_height="match_parent"> <ImageButton android:layout_width="26.25dp" android:layout_height="15dp" android:id="@+id/backMonthImageViewButton" android:background="@drawable/move_back" android:layout_marginLeft="10dp" android:layout_gravity="center_vertical" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:id="@+id/monthTextView" android:gravity="center_vertical|center_horizontal" android:textStyle="italic" android:fontFamily="SERIF" android:textSize="17sp" /> <ImageButton android:layout_width="26.25dp" android:layout_height="15dp" android:id="@+id/moveMonthImageButton" android:background="@drawable/move_on" android:layout_marginRight="10dp" android:layout_gravity="center_vertical" /> </TableRow> </TableLayout> <TableLayout android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/calendarDateLayout"> <TableRow xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="3dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="SUN" android:id="@+id/sunTextView" android:layout_weight=".2" android:layout_marginLeft="5dp" android:gravity="center" android:textStyle="italic" android:fontFamily="SERIF" android:textSize="11sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="MON" android:id="@+id/monTextView" android:layout_weight=".2" android:gravity="center" android:textStyle="italic" android:fontFamily="SERIF" android:textSize="11sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TUE" android:id="@+id/tueTextView" android:layout_weight=".2" android:gravity="center" android:textStyle="italic" android:fontFamily="SERIF" android:textSize="11sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="WED" android:id="@+id/wedTextView" android:layout_weight=".2" android:gravity="center" android:textStyle="italic" android:fontFamily="SERIF" android:textSize="11sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="THU" android:id="@+id/thuTextView" android:layout_weight=".2" android:gravity="center" android:textStyle="italic" android:fontFamily="SERIF" android:textSize="11sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="FRI" android:id="@+id/friTextView" android:layout_weight=".2" android:gravity="center" android:textStyle="italic" android:fontFamily="SERIF" android:textSize="11sp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="SAT" android:id="@+id/satTextView" android:layout_weight=".2" android:layout_marginRight="5dp" android:gravity="center" android:textStyle="italic" android:fontFamily="SERIF" android:textSize="11sp" /> </TableRow> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="1dp" android:layout_marginLeft="5dp" android:layout_marginRight="5dp" android:background="#fecfb1"> </LinearLayout> </TableLayout> <Button android:id="@+id/ok_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/bt_dialog_positive" android:text="OK" android:textColor="@android:color/white" /> </LinearLayout> <Button android:id="@+id/end_button" android:layout_width="40dp" android:layout_height="40dp" android:layout_alignParentRight="true" android:layout_alignParentTop="true" android:background="@drawable/bt_dialog_close" android:text="×" android:textColor="#FFBA6F"/> </RelativeLayout>
日付以外のレイアウトを定義
③日付部分の表示用のレイアウトファイルを作成します。
1)calendardaterow.xml
<?xml version="1.0" encoding="utf-8"?> <TableRow xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="0dp"> <TextView android:layout_width="wrap_content" android:layout_height="45dp" android:layout_weight=".2" android:layout_marginLeft="5dp" android:gravity="center" android:textStyle="italic" android:fontFamily="SERIF" android:textSize="13sp" /> <TextView android:layout_width="wrap_content" android:layout_height="45dp" android:layout_weight=".2" android:gravity="center" android:textStyle="italic" android:fontFamily="SERIF" android:textSize="13sp" /> <TextView android:layout_width="wrap_content" android:layout_height="45dp" android:layout_weight=".2" android:gravity="center" android:textStyle="italic" android:fontFamily="SERIF" android:textSize="13sp" /> <TextView android:layout_width="wrap_content" android:layout_height="45dp" android:layout_weight=".2" android:gravity="center" android:textStyle="italic" android:fontFamily="SERIF" android:textSize="13sp" /> <TextView android:layout_width="wrap_content" android:layout_height="45dp" android:layout_weight=".2" android:gravity="center" android:textStyle="italic" android:fontFamily="SERIF" android:textSize="13sp" /> <TextView android:layout_width="wrap_content" android:layout_height="45dp" android:layout_weight=".2" android:gravity="center" android:textStyle="italic" android:fontFamily="SERIF" android:textSize="13sp" /> <TextView android:layout_width="wrap_content" android:layout_height="45dp" android:layout_weight=".2" android:layout_marginRight="5dp" android:gravity="center" android:textStyle="italic" android:fontFamily="SERIF" android:textSize="13sp" /> </TableRow>
④ 最後にダイアログフラグメントクラスを作成していきます。
public class CalendarDialogFlagment extends DialogFragment { // 管理しているカレンダー情報 Calendar mCalendar; // イベント通知用 OnClickOKButtonListener mListener; // イベント生成 public interface OnClickOKButtonListener { public void onClickOKButton(int year, int month, int date); } // インスタンス生成 public static CalendarDialogFlagment newInstance(String title, int year, int month, int date) { CalendarDialogFlagment calendarDialogFlagment = new CalendarDialogFlagment(); /* 引数を設定 */ Bundle args = new Bundle(); args.putString("Title", title); args.putInt("Year", year); args.putInt("Month", month); args.putInt("Date", date); calendarDialogFlagment.setArguments(args); return calendarDialogFlagment; } // OKボタン押下時のイベント登録 public void setLisner(OnClickOKButtonListener listner) { mListener = listner; } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { /* 初期表示する年月日およびダイアログに表示するタイトルの取得 */ Bundle args = getArguments(); int year = args.getInt("Year"); int month = args.getInt("Month"); int date = args.getInt("Date"); String title = args.getString("Title"); // 初期設定されている日付をメンバ変数に設定 mCalendar = Calendar.getInstance(); mCalendar.set(year, month - 1, date); final Dialog dialog = new Dialog(getActivity()); // タイトル非表示 dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); // フルスクリーン dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN); // レイアウトファイルの設定 dialog.setContentView(R.layout.calendar_dialog); // 背景を透明にする dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); /* ダイアログの設定 */ ((TextView)dialog.findViewById(R.id.calendarTitle)).setText(title); /* 年の設定 */ ((TextView)dialog.findViewById(R.id.yearTextView)).setText(String.valueOf(year)); /* 月の設定 */ ((TextView)dialog.findViewById(R.id.monthTextView)).setText(getMonth(month)); /* 表示するViewGroup */ ViewGroup viewGroup = (ViewGroup) dialog.findViewById(R.id.calendarDateLayout); // 日付行の追加 for (int i = 0; i < 6; i++) { getActivity().getLayoutInflater().inflate(R.layout.calendardaterow, viewGroup); } // 初期設定されている日付設定 Calendar calendar = Calendar.getInstance(); calendar.set(year, month - 1, 1); /* setCalendar */ this.setCalendar(calendar, dialog); /* イベント生成 */ for (int i = 0; i < 6; i++) { TableRow dateTableRow = (TableRow) viewGroup.getChildAt(2 + i); for (int j = 0; j < 7; j++) { ((TextView) (dateTableRow.getChildAt(j))).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // 選択されたポジション取得 if (!((TextView) v).getText().equals("")) { /* 表示するViewGroup */ // 色の初期化 ViewGroup viewGroup = (ViewGroup) dialog.findViewById(R.id.calendarDateLayout); for (int i = 0; i < 5; i++) { TableRow dateTableRow = (TableRow) viewGroup.getChildAt(2 + i); for (int j = 0; j < 7; j++) { ((TextView) (dateTableRow.getChildAt(j))).setBackgroundColor(0x00000000); } } /* 年の取得 */ String year = (String) ((TextView) dialog.findViewById(R.id.yearTextView)).getText(); /* 月の設定 */ String month = (String) ((TextView) dialog.findViewById(R.id.monthTextView)).getText(); /* 日の取得 */ String date = (String) ((TextView) v).getText(); //メンバ変数へ選択されている日付情報を取得(OKボタン押下時に親クラスに戻すため設定) mCalendar.set(Integer.parseInt(year), getMonth(month) - 1, Integer.parseInt(date)); ((TextView) v).setBackgroundColor(0xfffeccad); } } }); } } //前の月に移動(move_back) dialog.findViewById(R.id.backMonthImageViewButton).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { /* 年の設定 */ String year = (String)((TextView)dialog.findViewById(R.id.yearTextView)).getText(); /* 月の設定 */ String month = (String)((TextView)dialog.findViewById(R.id.monthTextView)).getText(); // 初期設定されている日付取得 Calendar calendar = Calendar.getInstance(); calendar.set(Integer.parseInt(year), getMonth(month) - 1, 1); calendar.add(Calendar.MONTH, -1); ((TextView)dialog.findViewById(R.id.yearTextView)).setText(String.valueOf(calendar.get(Calendar.YEAR))); ((TextView)dialog.findViewById(R.id.monthTextView)).setText(getMonth(calendar.get(Calendar.MONTH) + 1)); /* 初期化 */ initializeCalendar(dialog); /* Calendar設定 */ setCalendar(calendar, dialog); } }); //次の月に移動(move_on) dialog.findViewById(R.id.moveMonthImageButton).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { /* 年の設定 */ String year = (String)((TextView)dialog.findViewById(R.id.yearTextView)).getText(); /* 月の設定 */ String month = (String)((TextView)dialog.findViewById(R.id.monthTextView)).getText(); // 初期設定されている日付取得 Calendar calendar = Calendar.getInstance(); calendar.set(Integer.parseInt(year), getMonth(month), 1); ((TextView)dialog.findViewById(R.id.yearTextView)).setText(String.valueOf(calendar.get(Calendar.YEAR))); ((TextView)dialog.findViewById(R.id.monthTextView)).setText(getMonth(calendar.get(Calendar.MONTH) + 1)); /* 初期化 */ initializeCalendar(dialog); /* Calendar設定 */ setCalendar(calendar, dialog); } }); // OK ボタンのリスナ dialog.findViewById(R.id.ok_button).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mListener.onClickOKButton(mCalendar.get(Calendar.YEAR), mCalendar.get(Calendar.MONTH), mCalendar.get(Calendar.DATE)); dismiss(); } }); // Close ボタンのリスナ dialog.findViewById(R.id.end_button).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { dismiss(); } }); // Dialogを返す return dialog; } /* 表示クリア */ private void initializeCalendar(Dialog dialog) { /* 表示するViewGroup */ ViewGroup viewGroup = (ViewGroup) dialog.findViewById(R.id.calendarDateLayout); for (int i = 0; i < 6; i++) { TableRow dateTableRow = (TableRow) viewGroup.getChildAt(2 + i); for (int j = 0; j < 7; j++) { ((TextView) (dateTableRow.getChildAt(j))).setBackgroundColor(0x00000000); ((TextView) (dateTableRow.getChildAt(j))).setText(""); } } } /* Calendar設定 */ private void setCalendar(Calendar calendar, Dialog dialog) { /* 表示するViewGroup */ ViewGroup viewGroup = (ViewGroup) dialog.findViewById(R.id.calendarDateLayout); // 日付設定 int cahngeDateIndex = 7 - calendar.get(Calendar.DAY_OF_WEEK); /* 日付設定 */ TableRow dateTableRow; TextView dateTextView; int index = 0; int dayCount = calendar.getActualMaximum(Calendar.DATE); for(int i = 0; i < dayCount; i++) { int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); if (i - cahngeDateIndex <= 0) { dateTableRow = (TableRow) viewGroup.getChildAt(2); dateTextView = (TextView) (dateTableRow.getChildAt(dayOfWeek - 1)); } else { index = (i + 6 - cahngeDateIndex) / 7; dateTableRow = (TableRow) viewGroup.getChildAt(2 + index); dateTextView = (TextView) (dateTableRow.getChildAt(dayOfWeek - 1)); } // 日付設定 dateTextView.setText(String.valueOf(i + 1)); //選択状態にする if(mCalendar.get(Calendar.YEAR) == calendar.get(Calendar.YEAR) && mCalendar.get(Calendar.MONTH) == calendar.get(Calendar.MONTH) && mCalendar.get(Calendar.DATE) == calendar.get(Calendar.DATE)) { dateTextView.setBackgroundColor(0xfffeccad); } // 次の日に移動 calendar.add(Calendar.DATE, 1); } } /* 対応する月を表す文字列を取得 */ private String getMonth(int month) { String strMonth = ""; switch(month) { case 1: strMonth = "January"; break; case 2: strMonth = "February"; break; case 3: strMonth = "March"; break; case 4: strMonth = "April"; break; case 5: strMonth = "May"; break; case 6: strMonth = "June"; break; case 7: strMonth = "July"; break; case 8: strMonth = "August"; break; case 9: strMonth = "September"; break; case 10: strMonth = "October"; break; case 11: strMonth = "November"; break; case 12: strMonth = "December"; break; } return strMonth; } private int getMonth(String month) { int integerMonth = 0; switch(month) { case "January": integerMonth = 1; break; case "February": integerMonth = 2; break; case "March": integerMonth = 3; break; case "April": integerMonth = 4; break; case "May": integerMonth = 5; break; case "June": integerMonth = 6; break; case "July": integerMonth = 7; break; case "August": integerMonth = 8; break; case "September": integerMonth = 9; break; case "October": integerMonth = 10; break; case "November": integerMonth = 11; break; case "December": integerMonth = 12; break; } return integerMonth; } }
以上でカレンダーダイアログは完成です。
親画面より呼び出せはダイアログが表示されます。
カスタマイズダイアログを作成できるようになると
処理の幅が広がるように思います。
バグ等ありましたら指摘して頂ければと思います。