つもりダイエット - dominickchen/cmrd GitHub Wiki
痩身ブームの今、様々なダイエット法やダイエット道具が世に溢れているが、
中には健康を害す無茶な方法もある。健康的に美を目指すには長い時間をかけて
心身共に健康にしていくことが大事である。食事制限や運動も大事だけれど、
一番はその頑張りを振り返って気持ちを保ち続けることが重要である。また、続けるのに無理は禁物。
しっかり3食食べたうえで今日のおやつはちょっと我慢。今回の作品は、そんなちょっとした我慢を記録し、蓄積していく。
身近なスマートフォンアプリで無理なくちょっとした心がけで気軽にダイエットをサポートできるものを目指した。
杉町 愛美(クリエイティブ・メディアR&Dゼミ4年/1期生)
企画:2018年6月頃~
プログラミング学習:2018年9月頃~
制作:2018年11月下旬頃~
実験:2018年12月6日~12日
論文執筆:2018年12月初旬頃~
発表:2018年12月20日
本作品「つもりダイエット」は本作品制作前の制作物からヒントを得て企画された。
企画面からヒントを得た「VR植物」(グループ制作)と技術面からヒントを得た「子ども向け料理ゲーム」(個人制作)である。
それぞれ制作期間が2018年5月~7月、2017年12月頃~2018年11月である。
本作品を説明するうえで合わせて2点の制作についても同時並行で述べる。
★が「VR植物」、☆が「子ども向け料理ゲーム」の説明部分となる。
スマートフォンで使用頻度の高いアプリの種類によって様々な植物が育ち、それを3Dホログラムで実体化させるという企画。
3年生の内田さんが主な企画者。4人グループで制作を行った。
写真に撮った素材を使って自由に切る、焼く、煮るなどの調理をし、任意のおもちゃに料理を与えられるというゲームを作ろうと考えていた。
何度か仕様変更の変遷があり、「スマートフォンで遊べるおもちゃ」というのを主軸に制作。
iOSでの実装を想定しており、swiftを同時並行で学習。
就職活動もひと段落し、卒業制作にとりかかり始めた時期。
この時は☆の「子ども向け料理ゲーム」を卒業制作としようと考えていた。
アプリの種類の多さからまずはプロトタイプとして2種類の木のモデリングをすることに。
ホログラムを映す装置もアクリル板を用いて作り(画像参照)、立体として出力することでひとまず完成とした。
残念ながら植物が成長するという機能まで制作できなかった、という反省が残った。
就職活動でしばらく間が空いてしまい、swift学習の復習を開始。
学習に使用していたのは「Swift Playgrounds」
ドミニク先生から貸して頂いたMacの使い方に慣れず、Xcodeを開くのがやっとの時期。
せっかくプログラミングを学ぶのだから、今現在の自分が欲しいものも作ってみようと思い立ち、卒業制作が終わった後自分向けにダイエットアプリを作ろうと画策。数多のアプリを使用しても中々長続きしなかったため、自分が欲しい機能を盛り込んだものをオリジナルで作ろうとした。
自分が続かなかった原因として以下の点がある。
- 食事記録アプリは行動を「許容摂取カロリーがあとどれだけ残っているか」という減算型が多いが、毎食の記録に切迫感がある。
- 食事記録アプリは食べたものに関してしか記録できず、食べるのを我慢したものには触れることがなく、頑張りが可視化されにくい
また、それをどう改善すれば使おうと思えるかを考えたのが以下の案である。
- 食事は毎度記録しなければならないが、間食であれば記録頻度も少なく手軽で済むのではないか
- どれだけ我慢できたかという行為を積み上げる加算型にし、それを可視化することでやる気が出るのではないか
また大きなシステムの原案として★「VR植物」の成長するという機能が作れなかった反省を活かし、自分の行動によって何かが育つ機能を入れようと考えた。
実際に近しいアプリとして他に参考にしたのが「ぜい肉で育つダイペット」と、機能面の簡易性で参考にした「ダイエット・カロリー・体重記録アプリもぐたん」。
ダイペットは減らした贅肉が餌になりペットが大きくなるというところから、努力が加算されていくシステム、もぐたんは食べ物のアイコンから食事したものを選ぶだけ、というところからアイコン型の簡易的な食事管理アプリであるところを構想した。
この時点でもまだ調理体験アプリの方を作る目的で動いていた。
当初iPhoneを所持していたため、iOSでの制作を考えていたが、肝心のMacを持っていないことや、先生のMacを貸与してもらいそれを独占して制作を行うことが難しいと判断したため、対応端末をiPhoneからAndroid端末に変更、加えて言語もswiftからjavaに変更。
怒涛のjava習得期間が始まる。
学習に使用したのはProgateのjavaコース。
ただし、あまり実践向きではなく、本当に0から基礎を学ぶ程度の学習にオススメである。
また、この時に想定していた画面遷移図はこちら
まだこの頃はこの案を卒業制作に使うなんて思いもよらなかった。
当初から考えていた調理体験アプリの方が難航。
9月頃に想定していた遷移図の序盤で詰む。
できたのはホーム画面から画面遷移し、カメラボタンを押すとカメラを起動、写真を撮ってビットマップ化、元の画面にその画像を張り付ける、というところまでである。
その後の画像をユーザーのタッチイベントにたいしてリアルタイムに処理をかけて形を変化(調理)させるというコードが初心者には到底無理だったためテーマ変更。
代わりに6月頃ぼんやり作ろうと思っていたダイエットアプリの製作に着手。
この時既に11月20日過ぎ。
機能としては
・間食を「がまん」した際に我慢したカロリーを記録、総がまんカロリーに加算し記録
・「がまん」したカロリーを代わりに画面内のキャラクター「しぼうちゃん」が摂取、成長
・「がまん」したカロリーを摂取していた場合、どの程度現体重から体重が増えていたかを試算
というものをメイン機能とした。
おもちゃアプリが画面遷移が多く、機能が複雑でとん挫した反省を活かし、画面遷移は2画面のみ、ほぼ機能も足し算のみ、とシンプルにした。
食品選択画面の食品は個人的によく食べたいと思う糖分と脂肪分の多いお菓子やスナック9種類を置き、それぞれに平均的なカロリー数値を与えた。
制作する際に使用したソフトはAndroid Studio
言語はjavaに代わりKotlinを主に使用した。
Kotlinで制作するにあたって参考にした書籍は『はじめてのAndroidプログラミング 第3版』
章ごとに簡単なアプリを制作するので実践的に基礎が学べてオススメである。
以下実際に制作に用いたコード
・メイン画面のレイアウト(xml)
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!-- 背景 -->
<ImageView
android:layout_width="900dp"
android:layout_height="644dp" app:srcCompat="@drawable/back"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:id="@+id/backImage" android:layout_marginTop="8dp"
app:layout_constraintVertical_bias="0.751" app:layout_constraintHorizontal_bias="0.1"
android:scaleType="fitCenter"/>
<!-- がまんボタン -->
<Button
android:layout_width="383dp"
android:layout_height="83dp" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:id="@+id/gaman" android:text="@string/gaman"
style="@style/Widget.AppCompat.Button.Colored" app:layout_constraintHorizontal_bias="0.51"
android:textStyle="bold" android:textAllCaps="true" android:textSize="30sp"/>
<!-- しぼうちゃん画像 -->
<ImageView
app:srcCompat="@drawable/shibou"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:id="@+id/shibou" app:layout_constraintVertical_bias="0.65"
android:layout_width="200px" android:layout_height="200px"/>
<!-- 総がまんカロリー表示 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="16dp"
android:layout_marginStart="4dp"
android:id="@+id/calorie"
android:textStyle="bold"
android:textSize="18sp"
android:typeface="normal"
android:background="@drawable/flame_style"
android:paddingBottom="8dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="8dp" tools:text='"総がまんカロリー\n %1d kcal"'/>
<!-- しぼうちゃんの体重表示 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/weight"
android:textStyle="bold" android:textSize="18sp" android:typeface="normal"
android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent" android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/calorie" android:layout_marginStart="8dp"
android:background="@drawable/flame_style" android:paddingBottom="8dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="8dp" app:layout_constraintHorizontal_bias="1.0" tools:text='"しぼうちゃんの体重\n %1d kg"'/>
<!-- リセットボタン -->
<Button
android:text="リセット"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/reset" android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/calorie" app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp"/>
</android.support.constraint.ConstraintLayout>
これでこのような画面ができる
補足として「しぼうちゃんの体重」を囲っている囲み角丸はコードで別にカスタマイズしており、上記コードだと
android:background=@drawable/flame_style
にあたる。参考までにflame_style(Xml)のコードも記載する。
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- 背景色 -->
<solid android:color="#ffffff" />
<!-- 角の丸み -->
<corners android:radius="6dp" />
<!-- 枠線 width:線の幅、color:線の色 -->
<stroke
android:width="2dp"
android:color="#753524" />
</shape>
・メイン画面機能コード(Kotlin)
//機能インポート部分
package com.creativemediard.tsumoridiet
import android.content.Context
import android.content.Intent
import android.graphics.Matrix
import android.os.Bundle
import android.preference.PreferenceManager
import android.support.constraint.ConstraintSet
import android.support.v7.app.AppCompatActivity
import android.view.View
import kotlinx.android.synthetic.main.activity_main.*
import org.jetbrains.anko.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//レイアウト干渉宣言
val constraintSet = ConstraintSet()
constraintSet.clone(constraintLayout)
//設定受け渡し
val pref = PreferenceManager.getDefaultSharedPreferences(this)
val editor = pref.edit()
//変数/定数定義設定部分
val kcalorie = intent.getIntExtra("CALORIE",0)
saveCalorie(kcalorie)
val allCalorie = pref.getInt("ALL_CALORIE", 0)
var plusWeight = pref.getInt("PLUS", 0)
var allWeight = pref.getInt("ALL_WEIGHT", 0)
var resize = pref.getInt("RESIZE",0)
var shibouSize = pref.getInt("SIZE", 200)
//ボタン遷移設定
gaman.setOnClickListener{ onGamanTapped(it)}
reset.setOnClickListener{(resetTapped(it))}
//70Kcalごとに10g増える設定
if( plusWeight >= 70){
while (plusWeight >= 70 ){
allWeight += 10
resize += 10
plusWeight -= 70
editor.putInt("PLUS", plusWeight)
.putInt("ALL_WEIGHT",allWeight)
.putInt("RESIZE", resize)
.apply()
}
}
//設定保存
saveWeight(allWeight)
//100gごとにしぼうちゃんをリサイズする設定
if (resize >= 100){
shibouSize += 50
constraintSet.constrainHeight(R.id.shibou, shibouSize)
constraintSet.constrainWidth(R.id.shibou,shibouSize)
constraintSet.applyTo(constraintLayout)
resize -= 100
editor.putInt("RESIZE", resize)
.putInt("SIZE", shibouSize)
.apply()
}else{
constraintSet.constrainHeight(R.id.shibou, shibouSize)
constraintSet.constrainWidth(R.id.shibou,shibouSize)
constraintSet.applyTo(constraintLayout)
}
//レイアウトの数値表示部分に変数数値を入れ込む設定
calorie.text = "総がまんカロリー\n %1d kcal".format(allCalorie)
weight.text = "しぼうちゃんの体重\n %1d g".format(allWeight)
}
//がまんボタン設定
fun onGamanTapped(view: View?){
//食選択画面にここで遷移
val intent = Intent(this, Food::class.java)
startActivity(intent)
}
//リセットボタン設定
fun resetTapped(view: View?){
val pref = PreferenceManager.getDefaultSharedPreferences(this)
val editor = pref.edit()
editor.putInt("ALL_CALORIE", 0)
.putInt("PLUS", 0)
.putInt("ALL_WEIGHT", 0)
.putInt("RESIZE",0)
.putInt("SIZE",200)
.apply()
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
//数値保存キー設定
private fun saveCalorie(kcalorie: Int) {
val pref = PreferenceManager.getDefaultSharedPreferences(this)
val allCalorie = pref.getInt("ALL_CALORIE", 0)
val plusWeight = pref.getInt("PLUS", 0)
val shibouSize = pref.getInt("SIZE",200)
val editor = pref.edit()
editor.putInt("ALL_CALORIE", allCalorie + kcalorie)
.putInt("PLUS", plusWeight + kcalorie)
.putInt("SIZE", shibouSize)
.apply()
}
private fun saveWeight (allWeight: Int) {
val pref = PreferenceManager.getDefaultSharedPreferences(this)
val allWeight = pref.getInt("ALL_WEIGHT", 0)
val resize = pref.getInt("RESIZE", 0)
val editor = pref.edit()
editor.putInt("ALL_WEIGHT", allWeight)
.putInt("RESIZE", resize)
.apply()
}
}
Androidアプリ制作のための記事ではないので、詳細は省くが、
- 食べ物選択画面で得たカロリー情報はプリファレンス機能を使い、キーで授受、保存
//設定受け渡し
val pref = PreferenceManager.getDefaultSharedPreferences(this)
val editor = pref.edit()
//変数/定数定義設定部分
val kcalorie = intent.getIntExtra("CALORIE",0)
saveCalorie(kcalorie)
- 得たカロリーをそれぞれの変数に加算、保存
//数値保存キー設定
private fun saveCalorie(kcalorie: Int) {
val pref = PreferenceManager.getDefaultSharedPreferences(this)
val allCalorie = pref.getInt("ALL_CALORIE", 0)
val plusWeight = pref.getInt("PLUS", 0)
val shibouSize = pref.getInt("SIZE",200)
val editor = pref.edit()
editor.putInt("ALL_CALORIE", allCalorie + kcalorie)
.putInt("PLUS", plusWeight + kcalorie)
.putInt("SIZE", shibouSize)
.apply()
}
private fun saveWeight (allWeight: Int) {
val pref = PreferenceManager.getDefaultSharedPreferences(this)
val allWeight = pref.getInt("ALL_WEIGHT", 0)
val resize = pref.getInt("RESIZE", 0)
val editor = pref.edit()
editor.putInt("ALL_WEIGHT", allWeight)
.putInt("RESIZE", resize)
.apply()
}
}
- 変数の数値によってレイアウトを変更するコード
//レイアウト干渉宣言
val constraintSet = ConstraintSet()
constraintSet.clone(constraintLayout)
//100gごとにしぼうちゃんをリサイズする設定
if (resize >= 100){
shibouSize += 50
constraintSet.constrainHeight(R.id.shibou, shibouSize)
constraintSet.constrainWidth(R.id.shibou,shibouSize)
constraintSet.applyTo(constraintLayout)
resize -= 100
editor.putInt("RESIZE", resize)
.putInt("SIZE", shibouSize)
.apply()
}else{
constraintSet.constrainHeight(R.id.shibou, shibouSize)
constraintSet.constrainWidth(R.id.shibou,shibouSize)
constraintSet.applyTo(constraintLayout)
}
//レイアウトの数値表示部分に変数数値を入れ込む設定
calorie.text = "総がまんカロリー\n %1d kcal".format(allCalorie)
weight.text = "しぼうちゃんの体重\n %1d g".format(allWeight)
といった部分がそこそこ苦労した部分である。
特に数値保存機能と変数の数値が変わるごとにレイアウトのキャラクター画像サイズを変更するといったことが参考書には十分に載っていなかったため、ここでかなりの時間を費やした。
がまんボタンを押すと食選択画面(以下画像)に遷移
・食選択画面の設定コード(Kotlin)は以下
//インポート部分
package com.creativemediard.tsumoridiet
import android.content.Intent
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import kotlinx.android.synthetic.main.activity_food.*
class Food : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_food)
//それぞれの食品タップ後の動作設定
ramen.setOnClickListener { onFoodTapped(it) }
pan.setOnClickListener { onFoodTapped(it) }
potechi.setOnClickListener { onFoodTapped(it) }
cake.setOnClickListener { onFoodTapped(it) }
choko.setOnClickListener { onFoodTapped(it) }
karaage.setOnClickListener { onFoodTapped(it) }
juice.setOnClickListener { onFoodTapped(it) }
ice.setOnClickListener { onFoodTapped(it) }
beer.setOnClickListener { onFoodTapped(it) }
//戻るボタン設定
back.setOnClickListener { onBackTapped(it) }
}
//それぞれの食品にカロリー数値を設定、キーに情報伝達設定及び遷移設定
fun onFoodTapped(view: View?){
val intent = Intent(this, MainActivity::class.java)
val calorie = when(view?.id){
R.id.ramen -> {
350
}
R.id.pan -> {
400
}
R.id.karaage -> {
300
}
R.id.cake -> {
300
}
R.id.choko -> {
100
}
R.id.potechi -> {
500
}
R.id.ice -> {
250
}
R.id.juice -> {
200
}
R.id.beer -> {
150
}
else -> {
0
}
}
intent.putExtra("CALORIE", calorie)
intent.putExtra("FOOD", view?.id)
startActivity(intent)
}
//戻るボタン押した際の遷移設定
fun onBackTapped(view: View?){
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
}
キャラクターが成長する(画像リサイズ)機能を除いてこれらの機能を1週間で作成した。
人間やればできるものである。
Android端末を所有する女性二人にプレ(キャラクター成長機能なし)を使用してもらった
【良かった点】
- シンプルで使いやすい
- 食べなかったことで摂取を防げたものが一目瞭然なのが良い
【改善点】
- 食品の選択肢が少ない
- 存在感が薄い(リマインダー無)
- 画面変化が乏しい
完成版(しぼうちゃん成長機能あり)を自身で3日間使用
【良かった点】
- シンプルで使いやすい
- 自分の我慢が可視化されて嬉しい
- 褒められも貶されもしないので無理が特にない
【改善点】
- 食品の選択肢が少ない
- 量の調節ができない
- キャラクターが大きくなっていることにずっと使っていると気づきにくい
- できたから何があるとか、できなかったから何があるということがない点
- なるべくシンプルに操作の手間を少なくした点
- 加算型で自分のがんばりが一目で分かるようにした点
- しぼうちゃんが体重に合わせて大きくなる点
- しぼうちゃんが画面内で生きているような演出(アニメーションなど)
- 体重を〇.〇kg表記にする
- 食品の量を決定する機能
- リマインダー機能
- アニメーションを組み込む際のコードを組み込む位置が良く分からなかった
- プリファレンスにdouble型が渡せず、代替方法が欲分からなかった
- 量決定は遷移方法でいい方法がまだ分かっていない
- リマインド方法は分かっているが勉強不足で組み込み方が分からない
- できなかったことを実装
- より正確に食べた世界線の自分と食べなかった今の自分の比較ができるようにする
- 手軽にダイエットを意識できて負担にならないアプリになるように改善