あかんわ

覚えたことをブログに書くようにすれば多少はやる気が出るかと思ったんです

IntelliJ IDEAでScala on Android using sbt Part.5[カレンダーアプリ]前編

Scala on Androidの練習に、簡単なカレンダーアプリを書きました。

前編では、アプリを構成する3つの画面(カレンダーを表示する画面・予定表を表示する画面・予定を編集する画面)のコードを記載しています。
後編には、予定の保存先であるSQLiteデータベースの操作に使用するContentProviderのコードを記載しています。

IDEIntelliJ IDEA CE 2016.2で、ビルドツールはsbt 0.13.12を使っています。
動作確認は、API level 23のAndroidエミュレータで実施しました。

目次

Scala on Androidでカレンダーアプリ

月間カレンダーを表示して、日にちごとの予定を登録できるだけのアプリです。

ソースコードGitHubに置いてますので、動かしてみる場合はこちらの記事を参考にして下さい。

プロジェクトディレクトリの構成
~/CaledarApp
    |- build.sbt                               // sbtのビルド定義ファイル
    |- /project                                // sbt関連の設定ファイルを配置するディレクトリ
    |- /src                                    // アプリのモジュールを構成するディレクトリ
    |    |- /main                              // アプリのメインソースセットを配置するディレクトリ
    |    |    |- AndroidManifest.xml           // アプリに関する情報を記述するマニフェストファイル
    |    |    |- /libs                         // 外部ライブラリを配置するディレクトリ
    |    |    |- /res                          // リソースファイルを配置するディレクトリ
    |    |    |    |- /drawable-mdpi           // 中解像度の画像を配置するディレクトリ
    |    |    |    |- /drawable-hdpi           // 高解像度の画像を配置するディレクトリ
    |    |    |    |- /drawable-xhdpi          // 超高解像度の画像を配置するディレクトリ
    |    |    |    |    |- ic_launcher.png     // アプリのアイコン画像
    |    |    |    |- /drawable-xxhdpi         // 超超高解像度の画像を配置するディレクトリ
    |    |    |    |- /drawable-xxxhdpi        // 超超超高解像度の画像を配置するディレクトリ
    |    |    |    |- /layout                  // レイアウトの定義ファイルを配置するディレクトリ
    |    |    |    |    |- activity_main.xml             // 通常画面のレイアウト定義ファイル
    |    |    |    |    |- activity_scheudle.xml         // 予定表画面のレイアウト定義ファイル
    |    |    |    |    |- listview_scheudlelistitem.xml // 予定表画面のListView内のレイアウト定義ファイル
    |    |    |    |    |- activity_editscheudle.xml     // 予定編集画面のレイアウト定義ファイル
    |    |    |    |- /values                  // 配色や文字の定義ファイルを配置するディレクトリ
    |    |    |                                
    |    |    |- /scala                        // Scalaのコードを配置するディレクトリ
    |    |         |- /com.b0npu.calendarapp             // アプリのパッケージディレクトリ
    |    |              |- CalendarActivity.scala        // アプリのメインファイル
    |    |              |- ScheduleActivity.scala        // 予定表を表示するクラス
    |    |              |- EditScheduleActivity.scala    // 予定の編集と保存のためのクラス
    |    |              |- ScheduleContentProvider.scala // 予定表のSQLiteデータベースを操作するクラス
    |    |              |- ScheduleDB.scala              // 予定表のデータベース情報を管理するオブジェクト
    |    |                                
    |    |- /test                              // テストコードを配置するディレクトリ
    |                             
    |- /target                                 // ビルドで生成された成果物の出力先ディレクトリ
主なソースコード

CalendarActivity.scalaで月間カレンダーを表示し、選択した日の予定表をScheduleActivity.scalaで表示します。
ScheduleActivity.scalaaddScheduleButtonを押すと、EditScheduleActivity.scalaで予定の登録ができます。
登録された予定はSQLiteデータベースに保存され、編集や削除といった変更に対応します。

SQLiteデータベースへの予定の保存は、ContentProviderクラスを継承したScheduleContentProvider.scalaを経由し、ScheduleActivity.scalaへの予定表の表示もScheduleContentProvider.scalaを利用しますので、AndroidManifest.xmlproviderの要素を追加し、ScheduleContentProviderのContent URIを設定しています。

また、ScheduleActivity.scalaでの予定表の表示にSQLiteデータベースの変更を即時に反映させたかったので、ContentProviderからのデータの読込みはCursorLoaderを使用し、非同期的に実施しています。

アプリ名は、res/values/strings.xmlsapp_nameと、build.sbtname :=で定義しています。

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
        xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.b0npu.calendarapp">

    <application
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme">

        <activity android:name=".CalendarActivity">

            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <activity android:name=".ScheduleActivity"
                  android:label="@string/app_name">
        </activity>

        <activity android:name=".EditScheduleActivity"
                  android:label="@string/app_name">
        </activity>

        <provider android:name=".ScheduleContentProvider"
                  android:authorities="com.b0npu.calendarapp.ScheduleContentProvider">
        </provider>

    </application>

</manifest>
CaledarActivity.scala
package com.b0npu.calendarapp

import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.CalendarView
import android.widget.CalendarView.OnDateChangeListener

/**
  * アプリ起動時の画面を表示するクラス
  *
  * CalendarViewを表示する
  */
class CalendarActivity extends AppCompatActivity with TypedFindView {

  /**
    * アプリの画面を生成
    *
    * アプリを起動するとonCreateが呼ばれてActivityが初期化される
    * レイアウトに配置したCalendarViewを表示し選択した日の予定表を表示する
    */
  override def onCreate(savedInstanceState: Bundle): Unit = {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    /* CalendarViewから選択した日の予定表を開く */
    val calendarView: CalendarView = findView(TR.calendarView)
    calendarView.setOnDateChangeListener(new OnDateChangeListener {

      override def onSelectedDayChange(calendarView: CalendarView, year: Int, month: Int, dayOfMonth: Int): Unit = {
        /* 選択された日付をyy/mm/ddの形に整える */
        val selectedDate: String = s"$year/${month + 1}/$dayOfMonth"

        /* インテントにScheduleActivityクラスと選択された日を指定して予定表の画面を開く */
        val scheduleIntent = new Intent(CalendarActivity.this, classOf[ScheduleActivity])
        scheduleIntent.putExtra("select_date", selectedDate)
        startActivity(scheduleIntent)
      }
    })
  }
}
ScheduleActivity.scala
package com.b0npu.calendarapp

import android.content.{ContentResolver, DialogInterface, Intent}
import android.database.Cursor
import android.os.Bundle
import android.support.v4.app.FragmentActivity
import android.support.v4.app.LoaderManager.LoaderCallbacks
import android.support.v4.widget.SimpleCursorAdapter
import android.support.v4.content.{CursorLoader, Loader}
import android.support.v7.app.AlertDialog
import android.view.View
import android.widget.AdapterView.OnItemLongClickListener
import android.widget._

/**
  * 予定表の画面を表示するクラス
  *
  * アプリの画面を生成するonCreateメソッドでCalendarActivityからIntentを受取り
  * CalendarViewで選択された日の予定表を表示する
  * 予定の追加・更新・削除を予定表に即時反映させるためにCursorLoaderを使うので
  * LoaderCallbacksインターフェースを実装する
  */
class ScheduleActivity extends FragmentActivity with TypedFindView with LoaderCallbacks[Cursor] {

  /**
    * フィールドの定義
    *
    * SQLiteデータベースから取得した予定のデータベースIDを格納する配列を定義する
    * 取得した予定の内容をCursorからListViewに渡すために使うCursorAdapterを格納する変数も定義する
    * (自クラスで使うだけのフィールドはprivateにして明示的に非公開にしてます)
    */
  private var scheduleItemIdArray: Array[String] = Array.empty
  private var scheduleContentCursorAdapter: SimpleCursorAdapter = _

  /**
    * アプリの画面を生成
    *
    * アプリを起動するとonCreateが呼ばれてActivityが初期化される
    * 選択された日付をIntentから取得しviewScheduleListメソッドで予定をListViewに表示する
    * addScheduleButtonを押すと予定の編集・登録画面を開く
    */
  override def onCreate(savedInstanceState: Bundle): Unit = {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_schedule)

    /* Intentから選択された日付を取得する */
    val calendarIntent: Intent = getIntent
    val selectedDate = calendarIntent.getStringExtra("select_date")

    /* レイアウトに設置したscheduleDateTextViewに選択された日付を表示する */
    val selectedDateTextView: TextView = findView(TR.selectedDateTextView)
    selectedDateTextView.setText(selectedDate)

    /* 選択された日付の予定をListViewに表示する */
    viewScheduleList(selectedDate)

    /* addScheduleButtonを押して予定を追加する */
    val addScheduleButton: Button = findView(TR.addScheduleButton)
    addScheduleButton.setOnClickListener(new View.OnClickListener {

      override def onClick(view: View): Unit = {
        /* インテントにEditScheduleActivityクラスと予定表の日付を指定して予定の編集・登録画面を開く */
        val editScheduleIntent = new Intent(ScheduleActivity.this, classOf[EditScheduleActivity])
        editScheduleIntent.putExtra("schedule_date", selectedDate)
        startActivity(editScheduleIntent)
      }
    })
  }

  /**
    * viewScheduleListメソッドの定義
    *
    * SQLiteデータベースからselectedDateの日付の予定をCursorLoaderで取得しListViewに表示する
    * ListViewに表示された予定を選択すると予定の編集画面を開く
    * ListViewに表示された予定を長押しすると削除する
    */
  private def viewScheduleList(selectedDate: String): Unit = {

    /* SQLiteデータベースへの問い合わせに使うので選択された日付をCursorLoaderに渡す */
    val scheduleDateBundle = new Bundle
    scheduleDateBundle.putString("selectedDate", selectedDate)
    getSupportLoaderManager.initLoader(0, scheduleDateBundle, ScheduleActivity.this)

    /* CursorLoaderで取得する予定表の項目と項目の表示先のViewを指定してCursorAdapterを作成する */
    val fromScheduleDBColumn = Array(ScheduleDB.TIME, ScheduleDB.CONTENT)
    val toTextViewInnerListView = Array(R.id.scheduleTimeInnerListView, R.id.scheduleContentInnerListView)
    scheduleContentCursorAdapter = new SimpleCursorAdapter(ScheduleActivity.this, R.layout.listview_schedulelistitem, null, fromScheduleDBColumn, toTextViewInnerListView, 0)

    /* scheduleListViewにCursorAdapterを渡して予定を表示する */
    val scheduleListView = findView(TR.scheduleListView)
    scheduleListView.setAdapter(scheduleContentCursorAdapter)

    /* scheduleListViewに表示した予定表から予定を選択して編集する */
    scheduleListView.setOnItemClickListener(new AdapterView.OnItemClickListener {
      override def onItemClick(parent: AdapterView[_], view: View, position: Int, id: Long): Unit = {

        /* 選択された予定のデータベースIDを取得する */
        val selectedScheduleItemId = scheduleItemIdArray(position)
        /* インテントにEditScheduleActivityクラスと予定のデータベースIDを指定して予定を編集する画面を開く */
        val editScheduleIntent = new Intent(ScheduleActivity.this, classOf[EditScheduleActivity])
        editScheduleIntent.putExtra("schedule_id", selectedScheduleItemId)
        startActivity(editScheduleIntent)
      }
    })

    /* scheduleListViewに表示した予定表から長押しした予定を削除する */
    scheduleListView.setOnItemLongClickListener(new OnItemLongClickListener {
      override def onItemLongClick(parent: AdapterView[_], view: View, position: Int, id: Long): Boolean = {

        /* 長押した予定のデータベースIDを取得してSQLステートメントを作成する */
        val selectedScheduleItemId = scheduleItemIdArray(position)
        val scheduleItemSelection = s"${ScheduleDB.ID} = $selectedScheduleItemId"

        /* アラートダイアログを表示して予定を削除するか確認する */
        new AlertDialog.Builder(ScheduleActivity.this)
          .setMessage("この予定を削除しますか?")
          .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener {
            /* ダイアログのOKボタンが押されたら予定を削除する */
            override def onClick(dialogInterface: DialogInterface, i: Int): Unit = {
              /* 予定をSQLiteデータベースから削除する */
              val scheduleContentResolver: ContentResolver = getContentResolver
              scheduleContentResolver.delete(ScheduleDB.CONTENT_URI, scheduleItemSelection, null)
            }
          })
          .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener {
            /* Cancelボタンが押されたら予定を削除せずにダイアログを閉じる */
            override def onClick(dialogInterface: DialogInterface, i: Int): Unit = {
            }
          })
          .create
          .show
        /* LongClickの完了を通知する */
        true
      }
    })
  }

  /**
    * onCreateLoaderメソッドをオーバーライド
    *
    * このメソッドはCursorLoaderを生成するメソッドで
    * 生成されたCursorLoaderは指定された条件によるデータベースへの問い合わせを
    * 非同期的に実施し検索結果をCursorに格納する
    */
  override def onCreateLoader(id: Int, bundle: Bundle): Loader[Cursor] = {

    /* 選択された日付をBundleから取得してSQLステートメントを作成する */
    val selectedDate = bundle.getString("selectedDate")
    val dateSelection = s"${ScheduleDB.DATE} LIKE ?"
    val dateSelectionArgs = Array(s"$selectedDate%")
    val sortOrderByTime = ScheduleDB.TIME

    /* SQLiteデータベースから選択された日付の予定を予定時間の順にCursorに格納するCursorLoaderを生成する */
    new CursorLoader(ScheduleActivity.this, ScheduleDB.CONTENT_URI, null, dateSelection, dateSelectionArgs, sortOrderByTime)
  }

  /**
    * onLoadFinishedメソッドをオーバーライド
    *
    * このメソッドはCursorLoaderが読み込みを終了する際に呼ばれるメソッドで
    * 以前にCursorAdapterに渡したCursorと新しく検索結果を格納したCursorを取り替える
    * ListViewに表示された予定の編集や削除でデータベースIDを使用するので
    * 新しくCursorに格納された予定表の検索結果から予定のデータベースIDを取得し
    * Cursorに格納された順にデータベースIDを配列に格納する
    */
  override def onLoadFinished(loader: Loader[Cursor], cursor: Cursor): Unit = {

    /* 以前のCursorと新しいCursorを取り替える */
    scheduleContentCursorAdapter.swapCursor(cursor)
    /* Cursorを取り替えたらデータベースIDの配列を作り直す */
    scheduleItemIdArray = Array.empty

    /* Cursorに格納された予定表の検索結果の先頭から順に予定のデータベースIDを取得し配列に格納する */
    if (cursor.moveToFirst) {
      do {
        val scheduleItemId = cursor.getString(cursor.getColumnIndex(ScheduleDB.ID))
        scheduleItemIdArray :+= scheduleItemId
      } while (cursor.moveToNext)
    }
  }

  /**
    * onLoaderResetメソッドをオーバーライド
    *
    * このメソッドはCursorLoaderがリセットされる際に呼ばれるメソッドで
    * 以前にCursorAdapterに渡したCursorを取り除き利用できなくする
    */
  override def onLoaderReset(loader: Loader[Cursor]): Unit = {
    scheduleContentCursorAdapter.swapCursor(null)
  }
}
EditScheduleActivity.scala
package com.b0npu.calendarapp

import android.R.style
import android.app.TimePickerDialog
import android.app.TimePickerDialog.OnTimeSetListener
import android.content.{ContentResolver, ContentValues, Intent}
import android.database.Cursor
import android.icu.util.Calendar
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.View
import android.view.View.OnClickListener
import android.widget._

/**
  * 予定の編集と登録をする画面を表示するクラス
  *
  * アプリの画面を生成するonCreateメソッドでScheduleActivityからIntentを受取り
  * Intentに選択された日付が付加されている場合は新しい予定の登録を行ない
  * Intentに予定のデータベースIDが付加されている場合は予定の編集を行う
  */
class EditScheduleActivity extends AppCompatActivity with TypedFindView {

  /**
    * フィールドの定義
    *
    * 複数のメソッドで扱うwidgetのidを格納するための変数を定義する
    * (自クラスで使うだけのフィールドはprivateにして明示的に非公開にしてます)
    */
  private var scheduleDateView: TextView = _
  private var scheduleTimeView: TextView = _
  private var scheduleEditText: EditText = _

  /**
    * アプリの画面を生成
    *
    * アプリを起動するとonCreateが呼ばれてActivityが初期化される
    * 選択された日付と予定のデータベースIDをIntentから取得し
    * データベースIDがIntentに付加されていた場合はviewScheduleItemメソッドで
    * SQLiteデータベースに保存されている予定を表示する
    * saveButtonを押した際にscheduleEditTextに予定が入力されていれば
    * saveScheduleToDBでSQLiteデータベースに予定を保存する
    */
  override def onCreate(savedInstanceState: Bundle): Unit = {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_editschedule)

    /* レイアウトに設置したwidgetのidを変数に格納する */
    scheduleDateView = findView(TR.scheduleDateView)
    scheduleTimeView = findView(TR.scheduleTimeView)
    scheduleEditText = findView(TR.scheduleEditText)
    val saveButton: Button = findView(TR.saveButton)
    val cancelButton: Button = findView(TR.cancelButton)

    /* Intentから予定表のデータベースIDと選択された日付を取得する */
    val scheduleIntent: Intent = getIntent
    val scheduleItemId = scheduleIntent.getStringExtra("schedule_id")
    val scheduleDate = scheduleIntent.getStringExtra("schedule_date")

    if (scheduleItemId == null) {
      /* scheduleItemIdが無ければ新しい予定なのでscheduleDateViewに選択された日付を表示する */
      scheduleDateView.setText(scheduleDate)
    } else {
      /* scheduleItemIdが有れば予定の編集なのでデータベースに保存された予定を表示する */
      viewScheduleItem(scheduleItemId)
    }

    /* scheduleTimeViewに選択した時間を表示する */
    selectScheduleTime

    /* saveButtonが押されたら予定をデータベースに保存する */
    saveButton.setOnClickListener(new OnClickListener {
      override def onClick(view: View): Unit = {
        saveScheduleToDB(scheduleItemId)
      }
    })

    /* cancelButtonが押されたらEditScheduleActivityを閉じる */
    cancelButton.setOnClickListener(new OnClickListener {
      override def onClick(view: View): Unit = {
        finish
      }
    })
  }

  /**
    * viewScheduleItemメソッドの定義
    *
    * SQLiteデータベースからデータベースIDがscheduleItemIdと一致する予定の
    * 日時と時間と内容を取得してレイアウトに配置したViewに表示する
    */
  private def viewScheduleItem(scheduleItemId: String): Unit = {

    /* scheduleItemIdからSQLステートメントを作成しデータベースに問い合わせた検索結果をCursorに格納する */
    val scheduleItemSelection = s"${ScheduleDB.ID} = $scheduleItemId"
    val scheduleContentResolver: ContentResolver = getContentResolver
    val scheduleContentCursor: Cursor = scheduleContentResolver.query(ScheduleDB.CONTENT_URI, null, scheduleItemSelection, null, null)

    /* Cursorに格納された予定の日時と時間と内容を習得する */
    scheduleContentCursor.moveToFirst
    val scheduleDate = scheduleContentCursor.getString(scheduleContentCursor.getColumnIndex(ScheduleDB.DATE))
    val scheduleTime = scheduleContentCursor.getString(scheduleContentCursor.getColumnIndex(ScheduleDB.TIME))
    val scheduleContent = scheduleContentCursor.getString(scheduleContentCursor.getColumnIndex(ScheduleDB.CONTENT))

    /* レイアウトに配置したViewに取得した予定の日付と時間と内容を表示する */
    scheduleDateView.setText(scheduleDate)
    scheduleTimeView.setText(scheduleTime)
    scheduleEditText.setText(scheduleContent)
  }

  /**
    * selectScheduleTimeメソッドの定義
    *
    * TimePickerで選択した時間をscheduleTimeViewに表示する
    */
  private def selectScheduleTime: Unit = {

    /* scheduleTimeViewを選択してTimePickerを表示する */
    scheduleTimeView.setOnClickListener(new OnClickListener {
      override def onClick(view: View): Unit = {

        /* Calendarユーティリティから現在時刻を取得してTimePickerに現在時刻を表示する */
        val calendarUtility = Calendar.getInstance
        val nowHour = calendarUtility.get(Calendar.HOUR_OF_DAY)
        val nowMinute = calendarUtility.get(Calendar.MINUTE)
        new TimePickerDialog(EditScheduleActivity.this, style.Theme_Holo_Light_Dialog, new OnTimeSetListener {
          override def onTimeSet(timePicker: TimePicker, selectHour: Int, selectMinute: Int): Unit = {
            /* TimePickerで選択した時間をHH:MMの形に整えて表示する */
            scheduleTimeView.setText(f"$selectHour%02d : $selectMinute%02d")
          }
        }, nowHour, nowMinute, true).show
      }
    })
  }

  /**
    * saveScheduleToDBメソッドの定義
    *
    * scheduleItemIdがある場合はSQLiteデータベースにある予定をupdateし
    * scheduleItemIdが無い場合はSQLiteデータベースに予定をinsertする
    */
  private def saveScheduleToDB(scheduleItemId: String): Unit = {

    /* 予定編集画面に表示された日付と時間と予定の内容を習得する */
    val scheduleDate = scheduleDateView.getText.toString
    val selectedTime = scheduleTimeView.getText.toString
    val writtenSchedule = scheduleEditText.getText.toString

    /* ContentValuesにSQLiteデータベースに保存する値を格納する */
    val scheduleContentValues = new ContentValues
    scheduleContentValues.put(ScheduleDB.DATE, scheduleDate)
    scheduleContentValues.put(ScheduleDB.TIME, selectedTime)
    scheduleContentValues.put(ScheduleDB.CONTENT, writtenSchedule)

    /* ContentProviderを使いSQLiteデータベースに予定を保存する */
    val scheduleContentResolver: ContentResolver = getContentResolver
    if (writtenSchedule.isEmpty) {
      /* scheduleEditTextに記入された予定が無ければ保存せずに通知する */
      Toast.makeText(
        EditScheduleActivity.this,
        "予定が入っていません\n予定を入力してから保存して下さい",
        Toast.LENGTH_LONG
      ).show

    } else if (scheduleItemId == null) {
      /* scheduleItemIdが無ければデータベースに新しく予定を追加して予定編集画面を閉じる */
      scheduleContentResolver.insert(ScheduleDB.CONTENT_URI, scheduleContentValues)
      finish

    } else {
      /* scheduleItemIdが有れば編集なのでデータベースを更新して予定編集画面を閉じる */
      val scheduleItemSelection = s"${ScheduleDB.ID} = $scheduleItemId"
      scheduleContentResolver.update(ScheduleDB.CONTENT_URI, scheduleContentValues, scheduleItemSelection, null)
      finish
    }
  }
}
activity_main.xml
  • Text
<?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/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingBottom="@dimen/activity_vertical_margin"
        tools:context="com.b0npu.calendarapp.CalendarActivity">

    <CalendarView android:id="@+id/calendarView"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:background="#89C4F4"/>

</RelativeLayout>
  • Design
    f:id:b0npu:20161201195859p:plain
activity_schedule.xml
  • Text
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <!-- 予定の追加ボタンと日付を上部に横並びに配置する -->
    <RelativeLayout android:layout_width="match_parent"
                    android:layout_height="wrap_content">

        <TextView android:text="Date"
                  android:id="@+id/selectedDateTextView"
                  android:textSize="32sp"
                  android:layout_height="wrap_content"
                  android:layout_width="match_parent"
                  android:layout_alignParentLeft="true"/>

        <Button android:text="+"
                android:id="@+id/addScheduleButton"
                android:textSize="32sp"
                android:textColor="@color/colorAccent"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:background="@null"/>

    </RelativeLayout>

    <!-- 予定を表示するListViewの上に区切り線を入れる -->
    <View android:layout_width="match_parent"
          android:layout_height="1dp"
          android:background="#e4e4e4"/>

    <!-- 予定をリストで表示する -->
    <ListView android:id="@+id/scheduleListView"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"/>

</LinearLayout>
  • Design
    f:id:b0npu:20161203144418p:plain
listview_schedulelistitem.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">

    <!-- ListViewの中に予定時間を表示するためのTextView -->
    <TextView android:id="@+id/scheduleTimeInnerListView"
              android:padding="8dp"
              android:layout_alignParentLeft="true"
              android:layout_height="wrap_content"
              android:layout_width="wrap_content"/>

    <!-- ListViewの中に予定の内容を表示するためのTextView -->
    <TextView android:id="@+id/scheduleContentInnerListView"
              android:textSize="24sp"
              android:layout_toRightOf="@+id/scheduleTimeInnerListView"
              android:layout_height="wrap_content"
              android:layout_width="wrap_content"/>

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

    <!-- 予定の項目を最大の広さにして日付と時間と予定のViewを縦に並べる -->
    <TextView android:hint="Date"
              android:id="@+id/scheduleDateView"
              android:textSize="32sp"
              android:layout_height="wrap_content"
              android:layout_width="wrap_content"/>

    <TextView android:hint="Time"
              android:id="@+id/scheduleTimeView"
              android:clickable="true"
              android:textSize="32sp"
              android:layout_height="wrap_content"
              android:layout_width="wrap_content"
              android:padding="@dimen/activity_vertical_margin"/>

    <EditText android:id="@+id/scheduleEditText"
              android:hint="予定"
              android:layout_weight="1"
              android:layout_height="0dp"
              android:layout_width="match_parent"
              android:inputType="textMultiLine"
              android:gravity="top"
              android:textSize="24sp"
              android:textColor="@color/textColor"/>

    <!-- キャンセルボタンと保存ボタンを下部に横並びに配置する -->
    <RelativeLayout android:id="@+id/relativeLayout"
                    android:paddingBottom="10dp"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

        <Button android:text="Cancel"
                android:id="@+id/cancelButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"/>

        <Button android:text="Save"
                android:id="@+id/saveButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"/>

    </RelativeLayout>

</LinearLayout>
  • Design
    f:id:b0npu:20161203144434p:plain
res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Calendar</string>
</resources>
build.sbt
・
・
・
name := "CalendarApp"
・
・
・
Run/Debugの実行結果

ここまでのコードで、カレンダーから予定の編集画面まで移動ができます。 f:id:b0npu:20161202063058g:plain

参考記事

ContentProviderやCursorLoaderの利用は、こちらの記事を参考にさせていただきました。

開発環境

関連記事