私がAndroid系スマホユーザーなので、今回はAndroid Studioを使ってアプリを作ってみる。
参考教材:いきなりプログラミング Androidアプリ開発
NFCタグをamazonで買って、タグとアプリを紐づけてからチェキキーホルダーにタグをぶち込むと、
ホルダーにスマホをかざすだけで観劇オタクアプリが開くという、オタクグッズが爆誕する
NFCタグの紐付けは、NFC tools(googleダウンロードページ)というアプリで行った。 ※ちゃんと動きました
舞台好きの中には多ステ(一つの演目で複数回鑑賞すること)するファンもいる。そのようなお客さんにより来場してもらえるように
来場スタンプをつくり、複数回来たお客さんにノベルティをプレゼントしたり、割引をしたりしている劇団もある。
しかし、私のような人間は紙のスタンプカードだと財布に入れたり何やらかんやらしているうちに失くしがちである。
→さすがにスマホアプリなら失くさないし、すぐ取り出せるのでは?
私がAndroid系スマホユーザーなので、今回はAndroid Studioを使ってアプリを作ってみる。
動いてる様子
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".MainActivity"
- android:orientation="vertical"
- android:gravity="center">
- <TextView
- android:id="@+id/messageView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/message"
- android:textSize="18sp" />
- <ImageView
- android:id="@+id/flowerImage"
- android:layout_width="200dp"
- android:layout_height="200dp"
- android:src="@drawable/_10"
- android:layout_marginVertical="40dp"
- android:contentDescription="@string/img_flower" />
- <Button
- android:id="@+id/waterBtn"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/btn_water" />
- <Button
- android:id="@+id/resetBtn"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@style/Widget.Material3.Button.OutlinedButton"
- android:text="@string/btn_reset"
- android:layout_marginTop="20dp"
- android:visibility="invisible"/>
- </LinearLayout>
- <resources>
- <string name="app_name">visit_counter</string>
- <string name="message">来場したらスタッフに\nボタンを押してもらおう!</string>
- <string name="message1">1回目!</string>
- <string name="message2">2回目!</string>
- <string name="message3">3回目!</string>
- <string name="message4">4回目!</string>
- <string name="message5">5回目!</string>
- <string name="message6">6回目!</string>
- <string name="message7">7回目!</string>
- <string name="message8">8回目!</string>
- <string name="message9">9回目!</string>
- <string name="message10">10回目! おめでとう!</string>
- <string name="img_flower">スタンプ</string>
- <string name="btn_water">来場しました!</string>
- <string name="btn_reset">リセット</string>
- </resources>
- package com.example.sample
- import androidx.appcompat.app.AppCompatActivity
- import android.os.Bundle
- import android.view.View
- import android.widget.Button
- import android.widget.ImageView
- import android.widget.TextView
- class MainActivity : AppCompatActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- val messageView: TextView = findViewById(R.id.messageView)
- val flowerImage: ImageView = findViewById(R.id.flowerImage)
- val waterBtn: Button = findViewById(R.id.waterBtn)
- val resetBtn: Button = findViewById(R.id.resetBtn)
- var count = 0
- waterBtn.setOnClickListener {
- count++
- when (count) {
- 1 -> {
- messageView.text = getString(R.string.message1)
- flowerImage.setImageResource(R.drawable._9)
- }
- 2 -> {
- messageView.text = getString(R.string.message2)
- flowerImage.setImageResource(R.drawable._8)
- }
- 3 -> {
- messageView.text = getString(R.string.message3)
- flowerImage.setImageResource(R.drawable._7)
- }
- 4 -> {
- messageView.text = getString(R.string.message4)
- flowerImage.setImageResource(R.drawable._6)
- }
- 5 -> {
- messageView.text = getString(R.string.message5)
- flowerImage.setImageResource(R.drawable._5)
- }
- 6 -> {
- messageView.text = getString(R.string.message6)
- flowerImage.setImageResource(R.drawable._4)
- }
- 7 -> {
- messageView.text = getString(R.string.message7)
- flowerImage.setImageResource(R.drawable._3)
- }
- 8 -> {
- messageView.text = getString(R.string.message8)
- flowerImage.setImageResource(R.drawable._2)
- }
- 9 -> {
- messageView.text = getString(R.string.message9)
- flowerImage.setImageResource(R.drawable._1)
- }
- 10 -> {
- messageView.text = getString(R.string.message10)
- flowerImage.setImageResource(R.drawable.comp)
- resetBtn.visibility = View.VISIBLE
- waterBtn.visibility = View.INVISIBLE
- }
- }
- }
- resetBtn.setOnClickListener {
- count = 0
- flowerImage.setImageResource(R.drawable._10)
- resetBtn.visibility = View.INVISIBLE
- waterBtn.visibility = View.VISIBLE
- }
- }
- }
次に取り組むこと
舞台やイベントによっては上演中の撮影が可能な場合がある。普通にスマホのカメラロールに画像を入れたままでもいいが、
せっかくならコメントとかつけて閲覧できたら後から色々思い出して楽しい気持ちになれるかもしれない?
- <?xml version="1.0" encoding="utf-8"?>
- <androidx.constraintlayout.widget.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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:contentDescription="@string/btn_left"
- tools:context=".MainActivity">
- <ImageView
- android:id="@+id/imageView"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:background="@color/black"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0.0"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="0.0"
- tools:ignore="contentDescription" />
- <ImageButton
- android:id="@+id/BtnLeft"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:background="@color/white60"
- android:contentDescription="@string/btn_left"
- android:src="@drawable/baseline_chevron_left_24"
- app:layout_constraintBottom_toBottomOf="@+id/imageView"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="0.9" />
- <ImageButton
- android:id="@+id/BtnRight"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:background="@color/white60"
- android:contentDescription="@string/btn_right"
- android:src="@drawable/baseline_chevron_right_24"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="0.9"
- tools:ignore="contentDescription" />
- <EditText
- android:id="@+id/editTextTextMultiLine"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="68dp"
- android:ems="10"
- android:gravity="start|top"
- android:hint="@string/ent_text"
- android:importantForAutofill="no"
- android:inputType="textMultiLine"
- android:minHeight="48dp"
- android:textColor="#FFFFFF"
- android:textColorHint="#AAA0A0"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0.497"
- app:layout_constraintStart_toStartOf="parent" />
- </androidx.constraintlayout.widget.ConstraintLayout>
- <resources>
- <string name="app_name">slite</string>
- <string name="btn_left">左へスライド</string>
- <string name="btn_right">右へスライド</string>
- <string name="ent_text">文字を入力してください</string>
- </resources>
- plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
- }
- android {
- namespace = "com.example.slite"
- compileSdk = 34
- defaultConfig {
- applicationId = "com.example.slite"
- minSdk = 19
- targetSdk = 34
- versionCode = 1
- versionName = "1.0"
- testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
- }
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(
- getDefaultProguardFile("proguard-android-optimize.txt"),
- "proguard-rules.pro"
- )
- }
- }
- compileOptions {
- sourceCompatibility = JavaVersion.VERSION_1_8
- targetCompatibility = JavaVersion.VERSION_1_8
- }
- kotlinOptions {
- jvmTarget = "1.8"
- }
- buildFeatures{
- viewBinding= true
- }
- }
- dependencies {
- implementation("androidx.core:core-ktx:1.9.0")
- implementation("androidx.appcompat:appcompat:1.6.1")
- implementation("com.google.android.material:material:1.11.0")
- implementation("androidx.constraintlayout:constraintlayout:2.1.4")
- testImplementation("junit:junit:4.13.2")
- androidTestImplementation("androidx.test.ext:junit:1.1.5")
- androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
- }
- package com.example.slite
- import androidx.appcompat.app.AppCompatActivity
- import android.os.Bundle
- import android.widget.EditText
- import com.example.slite.databinding.ActivityMainBinding
- class MainActivity : AppCompatActivity() {
- private lateinit var binding: ActivityMainBinding
- private var position = 0
- private var imageList = listOf(R.drawable.cap1, R.drawable.cap2, R.drawable.kj)
- // Create an array to hold EditText objects
- private var editTextList = mutableListOf<EditText>()
- private fun movePosition(num: Int) {
- position += num
- if (position >= imageList.size) {
- position = 0
- } else if (position < 0) {
- position = imageList.size - 1
- }
- binding.imageView.setImageResource(imageList[position])
- // Update the text of the EditText corresponding to the current image
- val currentEditText = editTextList[position]
- binding.editTextTextMultiLine.setText(currentEditText.text.toString())
- }
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- binding = ActivityMainBinding.inflate(layoutInflater)
- setContentView(binding.root)
- // Initialize EditText objects and add them to the list
- for (i in imageList.indices) {
- val editText = EditText(this)
- // Customize EditText properties if needed
- editTextList.add(editText)
- }
- // Set the first EditText as the initial state
- binding.editTextTextMultiLine.setText(editTextList[position].text.toString())
- movePosition(0)
- binding.BtnLeft.setOnClickListener {
- movePosition(-1)
- }
- binding.BtnRight.setOnClickListener {
- movePosition(1)
- }
- }
- }
閉じる△
舞台ごとに紹介文、X(旧twitter)の該当ハッシュタグのページが見れて、かつ人にも布教できたら良いのにな、という劇団オタク向けアプリ
pythonでやろうとしたヤツの続きみたいなもの
作品ごとの詳細、Xでのハッシュタグ検索、共有ができる
動いてる様子
apkファイルの作り方→実機(Android端末)へのインストールと動作確認【Androidアプリ開発】(外部サイト)を参考にした
pythonよりAndroid Studioの方が体感としてビルドが楽、アプリもちゃんと私物スマホで起動した
- <resources>
- <string name="app_name">Favorites</string>
- <string name="image_description">推しの画像</string>
- <string name="label_date">上演:%s</string>
- <string name="btn_url">Xをみる</string>
- <string name="btn_share">おすすめする</string>
- <string name="btnBack">戻る</string>
- <string name="explain_1">人口わずか170名の島『津久江島』何もないこの島、一応住所は東京都。
- 住人たちは現代のテクノロジーからかけ離れた生活を送っていた・・・
- そんな『津久江島』に嫌気がさし、本土に強い憧れをもつ「岸野弥生」は島から出ようと決意する。
- しかし、出発当日、島の異変に気づく・・・・
- 人々の憩いの場となっている島唯一の病院「田島産婦人科」には島の異変に気付いた住人達がパニックになり集まっていた・・・・
- 一体津久江島に何が起こったのか・・・・?</string>
- <string name="explain_2">映画監督を目指す拓郎はある出来事をきっかけに部分的に記憶を失ってしまう。
- そこに当時の旧友から授かったバーチャルリアリティゲーム「どぎまぎメモリアル」。
- それは自身の想像力を元に恋愛シミュレーションができるゲーム。
- あんなことやこんなこともできる中で拓郎は違和感を感じる。
- 「俺・・・これ知ってる。」破茶滅茶なゲーム展開に食らいつきながら拓郎は自身の記憶を取り戻していく・・・。</string>
- <string name="explain_3">私立彩色女子学園高等学校芸能科。それぞれの「才」を尊重し、活かし、伸ばすことを第一に考える東京屈指の芸能学校。
- ここでは「エンタメ力」というものが評価基準の全てとされ、上位の者には独自のパイプ で望む未来への道が約束される。
- だが近年の少子化、個人活動の繁栄によって生徒は激減し廃校の危機 学園の復興を願う理事長はある提案をする。
- 「彩色祭で大賞を獲ったクラスには年末のゴールデン特番2時間の枠を独占させる」
- 生徒たちは将来のスターへ約束される道を狙い争い合う</string>
- <string name="explain_4">「香澄は目の前が真っ暗になった」同郷の仲間二人と役者になるために上京したその世界は想像を超える厳しさだったが、夢を叶えたいという一心で乗り越えてきた。
- しかしある出来事をきっかけに香澄の心の中で張り詰めていた糸がプツンと切れてしまう。
- そこに現れたのは”ドリームキャッチャー”という夢叶えさせ屋。少々無茶はあるものの、香澄の願望はどんどん叶えられていく。
- 願っていたことが叶っていく中で香澄はある疑問を抱く。「あれ?結局私にとっての夢ってなんだったっけ――」
- 初めて自分の夢と真剣に向き合おうとする女性の物語。</string>
- <string name="explain_5">年に一度、村を上げて開催される小湊祭りの日、祭り会場から近い老舗の旅館『鴨川屋』は大忙しだった。
- しかし、祭りの日以外は閑古鳥が鳴くこの旅館は、この日を最後に閉館が決まっていた・・幽霊が出ると噂されている『椿の間』鴨川屋の館長は最後に幽霊を成仏させる為に霊
- 媒師を呼ぶが、何も知らない新人の仲居が飛び込みの客を椿の間に泊めてしまう・・・そして、最後の日に最強のクレーマーが現れる・・
- それぞれの想いを胸に業務をこなす中居達、客室内で巻き起こるトラブルを乗り越え『鴨川屋』は無事に最後の日を終える事ができるのか・・・・?</string>
- <string name="explain_6">県立音葉高等学校に転校してきた「神楽駒音」は、早々恋に落ちてしまった。お相手は爽やかな好青年「仁志敏久」。彼の誘いで、駒音は将棋部に入部する事に。
- しかし、生徒会長の魔の手によって将棋部は廃部へと追いやられてしまう......廃部を回避する方法はただ1つ、大会で優勝する事だった。
- 果たして将棋部の運命は?そして神楽駒音が抱えている秘密とは......?</string>
- <string name="explain_7">ホスト業界では汚い金稼ぎが主流となり、眠らない街と言われていたこの街も少しずつ活気が薄れていた。
- そんな歌舞伎町で透はただただバイトをしていた。何かしたいと心では思っていても一歩が出ない中途半端な人生。そこに現れたホスト東堂雅。
- 今のホスト業界を変えるため訴えを続ける雅に透は心を打たれる。「いいか、ホストにとって夢ってのは見るもんじゃねぇ。見せるもんだ」
- 一歩を踏み出しホストを目指す透だったが、そこは今にも潰れる寸前のホストクラブ「ラブ本店」
- ギリギリの状態で営業を続ける中でもやり方を変えないこの店にも透は疑問を抱くが、徐々に大切なことに気付かされていく。</string>
- <string name="explain_8">歌舞伎町ホスト業界。楽して稼げるイメージからホストの人口は急激に上がりはしたものの、現実は厳しかった。
- 収入の差の激しさから汚いやり方に手を染める若手が増え、ホストのイメージはより一層悪くなってしまう。
- だが、そんな時代を一人のホストが変えた。「ホストにとって夢ってのは叶えるもんじゃねえ、魅せるもんだ」
- 真の男(ホスト)たちにより再び息を吹き返したホスト業界だったが、そこに新たなジャンルが参入したことにより事態は一変する。
- その名も「男装ホスト」 「ホストクラブなのに女?いえいえ。体は女性でも見た目や心は男より男らしい男装ホストたちが甘い夜へと誘います。」
- 世はまさに大ホスト時代。</string>
- <string name="url_1">https://twitter.com/search?q=%23%E7%94%A3%E3%83%95%E3%82%A9%E3%83%BC&src=typed_query&f=top</string>
- <string name="url_2">https://twitter.com/search?q=%23%E3%81%A9%E3%81%8E%E3%83%A1%E3%83%A22023&src=typed_query&f=top</string>
- <string name="url_3">https://twitter.com/search?q=%23%E5%BD%A9%E5%A5%B3&src=typed_query&f=top</string>
- <string name="url_4">https://twitter.com/search?q=%23%E5%A4%A2%E3%81%BC%E3%81%8F%E3%82%8D&src=typed_query&f=top</string>
- <string name="url_5">https://twitter.com/search?q=%23%E9%B4%A8%E3%83%95%E3%82%A1%E3%82%A4&src=typed_query&f=top</string>
- <string name="url_6">https://twitter.com/search?q=%23%E9%A3%9B%E8%BB%8A%E3%83%A2%E3%83%B32023&src=typed_query&f=top</string>
- <string name="url_7">https://twitter.com/search?q=%23%E3%83%93%E3%82%BF%E3%83%8A%E3%82%A42023&src=typed_query&f=top</string>
- <string name="url_8">https://twitter.com/search?q=%23%E3%82%B7%E3%83%A5%E3%82%AC%E3%83%8A%E3%82%A42023&src=typed_query&f=top</string>
- </resources>
- <?xml version="1.0" encoding="utf-8"?>
- <androidx.constraintlayout.widget.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:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".DetailActivity">
- <ImageView
- android:id="@+id/detailImage"
- android:layout_width="180dp"
- android:layout_height="180dp"
- android:layout_marginTop="40dp"
- android:contentDescription="@string/image_description"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- tools:srcCompat="@tools:sample/avatars" />
- <TextView
- android:id="@+id/detailName"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="20dp"
- android:textSize="18sp"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/detailImage"
- tools:text="舞台名" />
- <TextView
- android:id="@+id/detailDate"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="20dp"
- android:textSize="16sp"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/detailName"
- tools:text="上演:2000/1/1" />
- <TextView
- android:id="@+id/detailExplain"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="20dp"
- android:paddingHorizontal="20dp"
- android:textSize="14sp"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/detailDate"
- app:lineHeight="24sp"
- tools:text="ここにテキストが入ります。ここにテキストが入ります。https://google.com" />
- <Button
- android:id="@+id/btnShare"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/btn_share"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/detailExplain" />
- <Button
- android:id="@+id/urlView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/btn_url"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toStartOf="@+id/detailImage"
- app:layout_constraintTop_toBottomOf="@+id/detailExplain" />
- <Button
- android:id="@+id/btnBack"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/btnBack"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintStart_toEndOf="@+id/detailImage"
- app:layout_constraintTop_toBottomOf="@+id/detailExplain" />
- </androidx.constraintlayout.widget.ConstraintLayout>
- <?xml version="1.0" encoding="utf-8"?>
- <androidx.constraintlayout.widget.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:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".DetailActivity">
- <ImageView
- android:id="@+id/detailImage"
- android:layout_width="180dp"
- android:layout_height="180dp"
- android:layout_marginTop="40dp"
- android:contentDescription="@string/image_description"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- tools:srcCompat="@tools:sample/avatars" />
- <TextView
- android:id="@+id/detailName"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="20dp"
- android:textSize="18sp"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/detailImage"
- tools:text="舞台名" />
- <TextView
- android:id="@+id/detailDate"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="20dp"
- android:textSize="16sp"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/detailName"
- tools:text="上演:2000/1/1" />
- <TextView
- android:id="@+id/detailExplain"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="20dp"
- android:paddingHorizontal="20dp"
- android:textSize="14sp"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/detailDate"
- app:lineHeight="24sp"
- tools:text="ここにテキストが入ります。ここにテキストが入ります。https://google.com" />
- <Button
- android:id="@+id/btnShare"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/btn_share"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/detailExplain" />
- <Button
- android:id="@+id/urlView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/btn_url"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toStartOf="@+id/detailImage"
- app:layout_constraintTop_toBottomOf="@+id/detailExplain" />
- <Button
- android:id="@+id/btnBack"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/btnBack"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintStart_toEndOf="@+id/detailImage"
- app:layout_constraintTop_toBottomOf="@+id/detailExplain" />
- </androidx.constraintlayout.widget.ConstraintLayout>
- <?xml version="1.0" encoding="utf-8"?>
- <androidx.constraintlayout.widget.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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:padding="6dp">
- <ImageView
- android:id="@+id/image"
- android:layout_width="50dp"
- android:layout_height="50dp"
- android:contentDescription="@string/image_description"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- tools:src="@tools:sample/avatars"/>
- <TextView
- android:id="@+id/name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="6dp"
- android:paddingEnd="6dp"
- android:textSize="18sp"
- app:layout_constraintBottom_toBottomOf="@+id/image"
- app:layout_constraintStart_toEndOf="@+id/image"
- tools:text="推しの名前"
- />
- </androidx.constraintlayout.widget.ConstraintLayout>
- package com.example.favorites
- import android.content.Intent
- import androidx.appcompat.app.AppCompatActivity
- import android.os.Bundle
- import android.widget.ArrayAdapter
- import android.widget.SimpleAdapter
- import android.widget.Toast
- import com.example.favorites.databinding.ActivityMainBinding
- class MainActivity : AppCompatActivity() {
- private lateinit var binding: ActivityMainBinding
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- binding = ActivityMainBinding.inflate(layoutInflater)
- setContentView(binding.root)
- binding.listView.adapter= SimpleAdapter(
- this, listData, R.layout.lint_item, arrayOf("image","name"), intArrayOf(R.id.image,R.id.name)
- )
- binding.listView.setOnItemClickListener{ _, _, position, _->
- startActivity(
- Intent(this@MainActivity, DetailActivity::class.java).apply{
- putExtra("POSITION", position)
- }
- )
- Toast.makeText(this, "${listData[position]["name"]}を選択しました",
- Toast.LENGTH_SHORT).show()
- }
- }
- }
- package com.example.favorites
- import android.content.Intent
- import android.net.Uri // Import Uri
- import androidx.appcompat.app.AppCompatActivity
- import android.os.Bundle
- import com.example.favorites.databinding.ActivityDetailBinding
- class DetailActivity : AppCompatActivity() {
- private lateinit var binding: ActivityDetailBinding
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- binding = ActivityDetailBinding.inflate(layoutInflater)
- setContentView(binding.root)
- val position = intent.getIntExtra("POSITION", 0)
- val data = listData[position]
- val name = data["name"].toString()
- val date = data["date"].toString()
- val explain = resources.getString(data["explain"].toString().toInt())
- binding.detailImage.setImageResource(data["image"].toString().toInt())
- binding.detailName.text = name
- binding.detailDate.text = getString(R.string.label_date, date)
- binding.detailExplain.text = explain
- binding.urlView.setOnClickListener {
- // URL を取得
- val url = resources.getString(data["url"].toString().toInt())
- // URL を開くための Intent を作成
- val openUrlIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
- // Intent を実行
- startActivity(openUrlIntent)
- }
- binding.btnShare.setOnClickListener {
- val subject = "【 ${name}】を布教"
- val message = "舞台タイトル:${name} \n 誕生日:${date} \n\n ${explain} "
- val sendIntent: Intent = Intent().apply {
- action = Intent.ACTION_SEND
- putExtra(Intent.EXTRA_SUBJECT, subject)
- putExtra(Intent.EXTRA_TEXT, message)
- type = "text/plain"
- }
- val shareIntent = Intent.createChooser(sendIntent, null)
- startActivity(shareIntent)
- }
- binding.btnBack.setOnClickListener {
- // Create an Intent to navigate to MainActivity
- val intent = Intent(this@DetailActivity, MainActivity::class.java)
- // Start the MainActivity
- startActivity(intent)
- // Finish the current activity to remove it from the back stack
- finish()
- }
- }
- }
- package com.example.favorites
- val listData = listOf(
- mapOf("image" to R.drawable.person1, "name" to "産声フォーユー", "date" to "2023年2月", "explain" to R.string.explain_1, "url" to R.string.url_1),
- mapOf("image" to R.drawable.person2, "name" to "どきまぎメモリアル", "date" to "2023年3月、4月、9月", "explain" to R.string.explain_2,"url" to R.string.url_2),
- mapOf("image" to R.drawable.person3, "name" to "志立彩色女学院アイドル科", "date" to "2023年5月", "explain" to R.string.explain_3, "url" to R.string.url_3),
- mapOf("image" to R.drawable.person4, "name" to "夢ぼくろファンタジア", "date" to "2023年6月", "explain" to R.string.explain_4,"url" to R.string.url_4),
- mapOf("image" to R.drawable.person5, "name" to "鴨川ファイヤーワークス", "date" to "2023年7月", "explain" to R.string.explain_5, "url" to R.string.url_5),
- mapOf("image" to R.drawable.person6, "name" to "高飛車モンスター", "date" to "2023年8月", "explain" to R.string.explain_6, "url" to R.string.url_6),
- mapOf("image" to R.drawable.person7, "name" to "歌舞伎町ビターナイト", "date" to "2023年10月", "explain" to R.string.explain_7, "url" to R.string.url_7),
- mapOf("image" to R.drawable.person8, "name" to "歌舞伎町シュガーナイト", "date" to "2023年11月", "explain" to R.string.explain_8, "url" to R.string.url_8),
- )
閉じる△3.でやったこと+簡単な日記を保存できて、さらにSNSなどに転送できる仕組みを作る
動いてる様子
apkファイルの作り方→実機(Android端末)へのインストールと動作確認【Androidアプリ開発】(外部サイト)を参考にした
pythonよりAndroid Studioの方が体感としてビルドが楽、アプリもちゃんと私物スマホで起動した
- package com.example.diary
- import android.content.ContentValues
- import android.content.Intent
- import android.database.sqlite.SQLiteDatabase
- import androidx.appcompat.app.AppCompatActivity
- import android.os.Bundle
- import android.widget.Toast
- import com.example.diary.databinding.ActivityEditBinding
- class EditDiary : AppCompatActivity() {
- private lateinit var binding: ActivityEditBinding
- private lateinit var dbHelper: DatabaseHelper
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- binding = ActivityEditBinding.inflate(layoutInflater)
- setContentView(binding.root)
- intent?.extras?.let {
- binding.diaryDate.setText(it.getString("DIARY_DATE"))
- binding.diaryText.setText(it.getString("DIARY_TEXT"))
- }
- dbHelper = DatabaseHelper(this@EditDiary)//データベースの用意
- //データ保存ボタン
- binding.btnSave.setOnClickListener {
- //入力チェック 未入力あればポップアップ
- if (binding.diaryDate.text.isNullOrBlank() || binding.diaryText.text.isNullOrBlank()) {
- Toast.makeText(this, "未入力あり", Toast.LENGTH_SHORT).show()
- return@setOnClickListener
- }
- dbHelper.writableDatabase.use { db ->//データベース開く
- //保存データの用意
- val values = ContentValues().apply {
- put("diary_date", binding.diaryDate.text.toString())
- put("diary_text", binding.diaryText.text.toString())
- }
- //保存&更新(日付が重複したら置き換え
- db.insertWithOnConflict("items", null, values, SQLiteDatabase.CONFLICT_REPLACE)
- //mainactivityを表示
- startActivity(Intent(this@EditDiary, ListDiary::class.java))
- }
- }
- //削除ボタン
- binding.btnDelete.setOnClickListener {
- if (binding.diaryDate.text.isNullOrBlank()) {
- Toast.makeText(this, "日付が設定されていません", Toast.LENGTH_SHORT).show()
- return@setOnClickListener
- }
- dbHelper.writableDatabase.use { db ->
- val params = arrayOf(binding.diaryDate.text.toString())
- db.delete("items", "diary_date = ?", params)
- startActivity(Intent(this@EditDiary, ListDiary::class.java))
- }
- }
- //デートピッカーを開く
- binding.diaryDate.setOnClickListener {
- val datePicker = DatePickerFragment()
- datePicker.show(supportFragmentManager, "datePicker")
- }
- //SNSシェアボタン
- binding.btnSns.setOnClickListener {
- val message = binding.diaryText.text.toString()
- val sendIntent = Intent().apply {
- action = Intent.ACTION_SEND
- putExtra(Intent.EXTRA_TEXT, message)
- type = "text/plain"
- }
- val shareIntent = Intent.createChooser(sendIntent, null)
- if (sendIntent.resolveActivity(packageManager) != null) {
- startActivity(shareIntent)
- } else {
- // シェア用のアプリが見つからない場合のエラーハンドリング
- Toast.makeText(this, "シェア用のアプリが見つかりません", Toast.LENGTH_SHORT).show()
- }
- }
- }
- }
- package com.example.diary
- import android.content.ContentValues
- import android.content.Intent
- import android.database.sqlite.SQLiteDatabase
- import androidx.appcompat.app.AppCompatActivity
- import android.os.Bundle
- import android.widget.Toast
- import com.example.diary.databinding.ActivityEditBinding
- class EditDiary : AppCompatActivity() {
- private lateinit var binding: ActivityEditBinding
- private lateinit var dbHelper: DatabaseHelper
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- binding = ActivityEditBinding.inflate(layoutInflater)
- setContentView(binding.root)
- intent?.extras?.let {
- binding.diaryDate.setText(it.getString("DIARY_DATE"))
- binding.diaryText.setText(it.getString("DIARY_TEXT"))
- }
- dbHelper = DatabaseHelper(this@EditDiary)//データベースの用意
- //データ保存ボタン
- binding.btnSave.setOnClickListener {
- //入力チェック 未入力あればポップアップ
- if (binding.diaryDate.text.isNullOrBlank() || binding.diaryText.text.isNullOrBlank()) {
- Toast.makeText(this, "未入力あり", Toast.LENGTH_SHORT).show()
- return@setOnClickListener
- }
- dbHelper.writableDatabase.use { db ->//データベース開く
- //保存データの用意
- val values = ContentValues().apply {
- put("diary_date", binding.diaryDate.text.toString())
- put("diary_text", binding.diaryText.text.toString())
- }
- //保存&更新(日付が重複したら置き換え
- db.insertWithOnConflict("items", null, values, SQLiteDatabase.CONFLICT_REPLACE)
- //mainactivityを表示
- startActivity(Intent(this@EditDiary, ListDiary::class.java))
- }
- }
- //削除ボタン
- binding.btnDelete.setOnClickListener {
- if (binding.diaryDate.text.isNullOrBlank()) {
- Toast.makeText(this, "日付が設定されていません", Toast.LENGTH_SHORT).show()
- return@setOnClickListener
- }
- dbHelper.writableDatabase.use { db ->
- val params = arrayOf(binding.diaryDate.text.toString())
- db.delete("items", "diary_date = ?", params)
- startActivity(Intent(this@EditDiary, ListDiary::class.java))
- }
- }
- //デートピッカーを開く
- binding.diaryDate.setOnClickListener {
- val datePicker = DatePickerFragment()
- datePicker.show(supportFragmentManager, "datePicker")
- }
- //SNSシェアボタン
- binding.btnSns.setOnClickListener {
- val message = binding.diaryText.text.toString()
- val sendIntent = Intent().apply {
- action = Intent.ACTION_SEND
- putExtra(Intent.EXTRA_TEXT, message)
- type = "text/plain"
- }
- val shareIntent = Intent.createChooser(sendIntent, null)
- if (sendIntent.resolveActivity(packageManager) != null) {
- startActivity(shareIntent)
- } else {
- // シェア用のアプリが見つからない場合のエラーハンドリング
- Toast.makeText(this, "シェア用のアプリが見つかりません", Toast.LENGTH_SHORT).show()
- }
- }
- }
- }
- //データベースを管理する//
- package com.example.diary
- import android.content.Context
- import android.database.sqlite.SQLiteDatabase
- import android.database.sqlite.SQLiteOpenHelper
- class DatabaseHelper(context: Context):
- SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION){//データベースを作成するコードの用意//
- //context:コンテキスト、null:表示データのカスタマイズに使うが基本nullでOK//
- companion object {
- private const val DB_NAME = "diary.sqlite"//DB_NAME:データベース名//
- private const val DB_VERSION = 1//DB_VERSION:バージョンを整数で指定//
- }
- override fun onCreate(p0: SQLiteDatabase?) {
- p0?.let{
- it.execSQL("CREATE TABLE items(" +"diary_date TEXT PRIMARY KEY, diary_text TEXT)")
- //テーブルを作る、データベースを変更するためのSQL文//
- //primary keyはデータを重複させないようにする、ここでは日付のカラムにつけて同じ日付のデータが入らないようにする//
- it.execSQL("INSERT INTO items(diary_date, diary_text)" +
- "VALUES('2024/01/01', 'テスト1')")
- it.execSQL("INSERT INTO items(diary_date, diary_text)" +
- "VALUES('2024/02/01', 'テスト2')")
- //データを追加//
- }
- }
- override fun onUpgrade(p0: SQLiteDatabase?, p1: Int, p2: Int) {
- }
- }
- package com.example.diary
- import android.content.Intent
- import android.os.Bundle
- import androidx.appcompat.app.AppCompatActivity
- import androidx.recyclerview.widget.DividerItemDecoration
- import androidx.recyclerview.widget.LinearLayoutManager
- import com.example.diary.databinding.ListDiaryBinding
- import com.example.diary.recyclerview.MylistAdapter
- class ListDiary : AppCompatActivity() {
- private lateinit var binding: ListDiaryBinding
- private lateinit var dbHelper: DatabaseHelper
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- binding = ListDiaryBinding.inflate(layoutInflater)
- setContentView(binding.root)
- val data = mutableListOf<Map<String, String>>()//データベースから取り出したデータを入力
- dbHelper = DatabaseHelper(this@ListDiary)//データベースの用意
- dbHelper.readableDatabase.use{ db ->//データを読み込む
- val cursor = db.query(//テーブル名、取得カラム、検索条件、検索条件に伴う値、グループ化、グループ化の条件、並び順、取得件数
- "items", null, null, null, null, null,
- "diary_date DESC", null//DESCは降順
- )
- cursor.use{//データの取得
- while(it.moveToNext()){//1行ずつデータを取り出す
- data.add(mapOf("date" to it.getString(0),"text" to it.getString(1)))
- }
- }
- }
- binding.recyclerView.layoutManager = LinearLayoutManager(this)
- binding.recyclerView.adapter = MylistAdapter(data)
- val dividerItemDecoration = DividerItemDecoration(this@ListDiary, DividerItemDecoration.VERTICAL)
- binding.recyclerView.addItemDecoration(dividerItemDecoration)
- binding.fab.setOnClickListener {
- startActivity(Intent(this@ListDiary, EditDiary::class.java))
- }
- binding.home.setOnClickListener {
- startActivity(Intent(this@ListDiary, MainActivity::class.java))
- }
- }
- }
閉じる△