大学院生のアプリ開発奮闘記

将来への不安を抱く大学院生二人がアプリ開発に奮闘する困難と過程を綴っていくブログです.現在androidアプリ開発中

6行書くだけで誰でもできるウィジェット開発

皆さん,はじめまして.
CheerAppsで主にAndroidアプリ開発をしているshiitaです.
Androidアプリ開発で役立つTIPSなどについて、
伝えていけたらいいなと思います!

しばらくの間は、ウィジェットカウンターの開発で学んだことについて,
記事をいくつか書いていきます!


今回は簡単なウィジェットの開発について話したいと思います.
具体的には付箋のようにメモができるウィジェットを作ります!
使用するプログラミング言語は,私の一番好きなKotlinで書いていきたいと思います.

AndroidStudioで自動生成されるサンプルのプログラムをうまく活用して,
数行のコード編集だけで作れるものを紹介します.




プロジェクトの作成

今回作るウィジェットは次の2つの機能を持ちます.

  1. ホーム画面に貼り付けられるウィジェットの本体
  2. ウィジェットの内容を書き換える設定画面

アプリを起動すると最初に表示される画面は不要なので,
下の画像のようにAdd No Activityを選択します.


f:id:cheerapps:20180417003635p:plain




ウィジェットの追加

右クリックをして出てくるメニューから,Widgetを選択し,追加します.


f:id:cheerapps:20180417003643p:plain


下の画像のように,クラス名やウィジェットのサイズなどを設定していくのですが,
ここで1つポイントがあります.


f:id:cheerapps:20180417003651p:plain


Configuration Screenにチェック


これを行うことで,ウィジェットの設定画面もAndroidStudioが自動生成してくれます!
できるだけ簡単にウィジェットを作って行きたいため,
楽できるところは楽して行きましょうwww




コードの編集

付箋のようにメモができるウィジェットを作るために,
コードの編集をしていきます.
編集する箇所はたったの6行です!!!

エラーの除去

AndroidStudioで自動生成されるコードはJavaで書かれたもので,
その後にKotlinへのコンバートが行われます.
このときに入り込んでしまうエラーを解消していきます.

class NoteConfigureActivity : Activity() {
    internal var mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID
    internal lateinit var mAppWidgetText: EditText      // onCreateで初期化するのでlateinitをつける
    internal var mOnClickListener: View.OnClickListener = View.OnClickListener {
        val context = this@NoteConfigureActivity

        // When the button is clicked, store the string locally
        val widgetText = mAppWidgetText.text.toString()
        saveTitlePref(context, mAppWidgetId, widgetText)

        // It is the responsibility of the configuration activity to update the app widget
        val appWidgetManager = AppWidgetManager.getInstance(context)
        Note.updateAppWidget(context, appWidgetManager, mAppWidgetId)

        // Make sure we pass back the original appWidgetId
        val resultValue = Intent()
        resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId)
        setResult(Activity.RESULT_OK, resultValue)
        finish()
    }

    public override fun onCreate(icicle: Bundle?) {
        super.onCreate(icicle)

        // Set the result to CANCELED.  This will cause the widget host to cancel
        // out of the widget placement if the user presses the back button.
        setResult(Activity.RESULT_CANCELED)

        setContentView(R.layout.note_configure)
        mAppWidgetText = findViewById<View>(R.id.appwidget_text) as EditText
        findViewById<View>(R.id.add_button).setOnClickListener(mOnClickListener)

        // Find the widget id from the intent.
        val intent = intent
        val extras = intent.extras
        if (extras != null) {
            mAppWidgetId = extras.getInt(
                    AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID)
        }

        // If this activity was started with an intent without an app widget ID, finish with an error.
        if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
            finish()
            return
        }

        mAppWidgetText.setText(loadTitlePref(this@NoteConfigureActivity, mAppWidgetId))
    }

    companion object {

        private val PREFS_NAME = "jp.cheerapps.note.Note"
        private val PREF_PREFIX_KEY = "appwidget_"

        // Write the prefix to the SharedPreferences object for this widget
        internal fun saveTitlePref(context: Context, appWidgetId: Int, text: String) {
            val prefs = context.getSharedPreferences(PREFS_NAME, 0).edit()
            prefs.putString(PREF_PREFIX_KEY + appWidgetId, text)
            prefs.apply()
        }

        // Read the prefix from the SharedPreferences object for this widget.
        // If there is no preference saved, get the default from a resource
        internal fun loadTitlePref(context: Context, appWidgetId: Int): String {
            val prefs = context.getSharedPreferences(PREFS_NAME, 0)
            val titleValue = prefs.getString(PREF_PREFIX_KEY + appWidgetId, null)
            return titleValue ?: context.getString(R.string.appwidget_text)
        }

        internal fun deleteTitlePref(context: Context, appWidgetId: Int) {
            val prefs = context.getSharedPreferences(PREFS_NAME, 0).edit()
            prefs.remove(PREF_PREFIX_KEY + appWidgetId)
            prefs.apply()
        }
    }
}


コードを全て載せましたが,この部分で変更した箇所は

internal var mAppWidgetText: EditText

internal lateinit var mAppWidgetText: EditText

のようにlateinitを追加しただけです!
mAppWidgetTextの初期化はonCreate()で行うため,
後から初期化をすることを,明示的に示す必要があるのです.


設定画面の呼び出し

現在,設定画面はウィジェット追加時だけしか表示されません.
そこで,ウィジェットに記述したテキストを後から編集できるようにします.
具体的な処理は,ウィジェットをタップすると設定画面を呼び出すようにしたいと思います.

class Note : AppWidgetProvider() {

    override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
        // There may be multiple widgets active, so update all of them
        for (appWidgetId in appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId)
        }
    }

    override fun onDeleted(context: Context, appWidgetIds: IntArray) {
        // When the user deletes the widget, delete the preference associated with it.
        for (appWidgetId in appWidgetIds) {
            NoteConfigureActivity.deleteTitlePref(context, appWidgetId)
        }
    }

    override fun onEnabled(context: Context) {
        // Enter relevant functionality for when the first widget is created
    }

    override fun onDisabled(context: Context) {
        // Enter relevant functionality for when the last widget is disabled
    }

    companion object {

        internal fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager,
                                     appWidgetId: Int) {

            val widgetText = NoteConfigureActivity.loadTitlePref(context, appWidgetId)
            // Construct the RemoteViews object
            val views = RemoteViews(context.packageName, R.layout.note)
            views.setTextViewText(R.id.appwidget_text, widgetText)

            // クリックで設定画面を開く
            val intent = Intent(context, NoteConfigureActivity::class.java).apply {
                putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
            }
            val pendingIntent = PendingIntent.getActivity(context, appWidgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
            views.setOnClickPendingIntent(R.id.appwidget_text, pendingIntent)

            // Instruct the widget manager to update the widget
            appWidgetManager.updateAppWidget(appWidgetId, views)
        }
    }
}


追加部分は以下の5行です.

val intent = Intent(context, NoteConfigureActivity::class.java).apply {
    putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
}
val pendingIntent = PendingIntent.getActivity(context, appWidgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
views.setOnClickPendingIntent(R.id.appwidget_text, pendingIntent)

設定画面であるNoteConfigureActivityに対するPendingIntentを作成し,
ウィジェットのViewであるRemoteViewsに対してsetOnClickPendingIntent()
でクリック時の処理を追加します.
RemoteViewsはクリックイベントの処理は,通常のViewと異なることに注意が必要です.




実行結果

ウィジェット追加 ウィジェット選択
f:id:cheerapps:20180417003719p:plain f:id:cheerapps:20180417003733p:plain
ウィジェット設定 ウィジェット
f:id:cheerapps:20180417003744p:plain f:id:cheerapps:20180417003505p:plain

実行結果はこの表のようになっています.
ウィジェットの設定画面で書いたテキストが,
ウィジェットにちゃんと追加されていることが確認できます.




default activity not found エラー

default activity not foundのエラーが出て,以下の画像の様に実行できない場合があります.


f:id:cheerapps:20180417003658p:plain


これはConfigurationのLaunchをNothingにすることで解消することができます.

f:id:cheerapps:20180417003706p:plain




おわりに

ここまで読んでいただきありがとうございました!
いかがでしたか?記事の感想,コードへの指摘など頂けると嬉しいです.
CheerAppが開発した最初のアプリ,「ウィジェットカウンター」は
こちらからダウンロードできます!

play.google.com


ではでは.

gitについてのとても基礎的な話②〜アプリ開発への道 その3〜

皆さん、こんにちは!

大学院生のきゅうりです!

 

本日も読んでいただいてありがとうございます!!

 

 

 

今日は前回に引き続いて、

 

git

 

に関するごくごく簡単な話です!!

 

今日の目標は

 

「Aさんはlayoutのブランチを切ってレイアウトに関する作業を、

 Bさんはmasterブランチでシステムに関する作業しよう」

 

マージするときにコンフリクトしてないか確認してください」

 

これら二つが何を言っているのか、理解することです!

 

 

この2回の記事を読めば、

gitの基礎の基礎については

マスターしたと言っても過言ではないです!!(多分)

 

 

 

では、それぞれの用語について解説していきます!

 

 

 

 

 

ブランチ:

 元のリポジトリに対して全く同じ複製を作成し、分岐を作ることです。

簡単にいうと、並行作業をする場所の作成です。

複数の作業を並列的に行いたい時に重宝します。

Aというブランチでは、レイアウトに関する作業を進める、

Bというブランチでは、システムに関する作業を進める

といった感じです。

gitでは初めから存在しているメインのブランチをmasterブランチと呼びます。

ウィジェットカウンターの場合

図で説明します。

f:id:cheerapps:20180416162640p:plain

 

この場合はlayoutブランチを作成し、

レイアウトやフォントに関する変更をlayoutブランチで、

カウンター機能に関する作業をmasterブランチで、

並列的に行っています。

最後のマージについては次で説明します、

 

新たなブランチを作ることをブランチを切るとも言います。

 

 

 

マージ:

複数のブランチのプログラムを統合することです。

複数のブランチそれぞれでの変更した部分を共に反映したリポジトリが作成されます。

具体的には、上の図のマージの部分ですね!

 

 

 

 

コンフリクト:

マージをする際に、

二つのブランチでそれぞれ同じ部分を変更してしまっていて

どっちをとったらいいのかわからない!!

という状況のことです。

つまり、衝突です(直訳ですが汗)。

 

ウィジェットカウンターを例にとって

図で説明すると次のような感じです!!

f:id:cheerapps:20180416165546p:plain

 

この場合は、

masterブランチでもlayoutブランチでも

カウンター機能の同じ部分を変更してしまったために、

コンフリクトが起きてしまったといった状況です。

 

 

コンフリクトしてしまった場合は、

自動でマージすることができず、

手動でマージすることになります。 

 

 

以上です!

 

 

 

2回に分けて、gitを最近学んだ初心者が初心者に向けて

記事を書いてみました。

 

僕なりに分かりやすく説明したつもりですが、

いかがだったでしょうか?

コメント等々お待ちしています!

 

 

また、今回gitGitHubについて学ぶきっかけなり、

例として挙げたウィジェットカウンターについては

google play で先日リリースしていますので、

是非ダウンロードお願いします!!

play.google.com

ウィジェットカウンターリリースの記事はコチラです 

 

 

本日も読んでいただいて、

ありがとうございました!

 

 

それでは!

gitについてのとても基礎的な話① 〜アプリ開発への道 その2〜

皆さんこんにちは!

大学院生のきゅうりです!! 

 

 

 自分でも完全に理解しきれていなかった部分を

復習しながら記事を書いているので、

大変じかんがかかってしまいます、、、

申し訳ありません(-_-;)

 

 

さて、前回、バージョン管理システムがどのようなものなのか

簡単に説明しました!

プログラムのバージョン管理の話

 

 

本日は、

 

“git”

 

について簡単に説明していきたいと思います!

 

 

 

gitとは、、、

現在最もメジャーなバージョン管理システムのひとつです。

 

多くのプログラマーや企業が

GitHubといったWedサービスを用いて

gitを使用しています。

 

 

そのためネット上で検索するだけでもたくさんの解説サイトが存在しています。

詳しい説明については

「git 初心者」

などで検索すればヒットする他サイトに譲りたいと思います!

 

僕もshiitaくんとcheerappsを結成し、

開発を行うにあったてネット上のサイトを参照したのですが、

 

リポジトリ??コミット??

ダイエットでもするの??

 

ナニコレ、ムズカシイ…

 

知らないカタカナ語ばっかで、

全然頭に入ってこないって感じでした( ;∀;)

 

というわけで今回から、

gitについてのごくごく簡単な部分について

二回に分けて説明していこうと思います!

 

 

今回の目標は、

 

リモートリポジトリからローカルリポジトリクローンしよう!」

 

プルして×××を変更して、コミットしてプッシュしよう!」 

 

これらが何を言っているのか理解する。ことです!

 

 

理解するために、それぞれの用語について説明していきます!

 

 

 

リポジトリ

ざっくり言えば開発するプログラムが入っているフォルダのことです。

ウィジェットカウンターを例にすると、

各プログラムやフォントや画像などすべてのファイルをまとめたものです!

f:id:cheerapps:20180412022408p:plain

 

 

ローカルリポジトリ

個人で作業するためのリポジトリです。

各個人のPC上に作られるものです。

つまり、個人の作業場所です。

 

 

 

リモートリポジトリ:

ネット上に存在しているリポジトリです。

GitHubなどのWebサービスのよってWeb上に作られます。

開発者みんなの共有の作業場所ってイメージです。

 

 

 

クローン:

リモートリポジトリ(共有の作業場所)を全てコピーして、

ローカルリポジトリ(個人の作業場所)に持ってくる、

つまり、ダウンロードです。

リモートリポジトリ上で作業をしてしまうと、

他の人の作業に影響が出てしまうので、

自分のPC上(ローカルリポジトリ)にクローンして作業します。

 

 

プル:

リモートリポジトリ(共有の作業場所)の中から、

ローカルリポジトリ(個人の作業場所)にないファイルや

更新されたファイルだけをローカルリポジトリ(個人の作業場所)に持ってくること、

つまり、差分だけダウンロードです。

 

 

コミット:

プログラムを変更した際に、変更部分とそれに付随する情報を保存することです。

つまり、バージョンアップです

 

 

プッシュ:

コミットしたものをリモートリポジトリ(共有の作業場所)に送信する、

つまり、アップロードです。

プッシュすることでリモートリポジトリ(共有の作業場所)が更新され、

その情報を開発者全員が共有することができます。

 

 

以上、ごくごく簡単なgitの説明でした。

今回目標にした二つの文章も理解できるようになっていると思います!

 

次回は引き続きgitについて、ブランチなどの説明をしていきます!

 

それでは!!

 

【早い者勝ち!】 あなたのお名前、残ってる?

 

 

プログラムのバージョン管理の話 ~アプリ開発への道 その1~

こんにちは!

大学院生のきゅうりです!

昨日はブログをお休みしてすみません。

今日も見ていただいてありがとうございます!

 

今日からは、今回のアプリ開発の経過とその際に得た知識を

できるだけ皆さんに理解してもらえるように

わかりやすく書いていこうと思います!

 

今まで、アプリ開発やプログラミングを

したことがない人にも(僕もほぼ初心者ですし)

分かるように書いて行きたいと思いますので、

ぜひ皆さんご覧ください!

 

 

 

さて、初回の本日は

 

”バージョン管理”

 

について話をしたいと思います!

 

今回は二人で開発をするということで

バージョン管理が不可欠でした!

 

バージョン管理とは、、、

 

アプリやプログラムを開発する際に、

「誰が」「いつ」「どのような」変更をしたのかといった

変更の履歴を管理することです。

 

 

百聞は意見に如かずということで、

まずは僕たちが開発したアプリでの

バージョン管理の一例をご覧ください!!

 

ざっくりと説明すると以下のような感じになります!

f:id:cheerapps:20180409172628p:plain

 

こんな感じです!

ただプログラムを上書き保存していくのと比較すると、

圧倒的に分かりやすいと思います!

 

次にバージョン管理を行うメリットを説明します!

 


バージョン管理のメリット

  • 以前の状態に戻ることができる
  • 変更者と変更内容を知ることができる
  • ブランチ(枝分かれ)によって
    複数の作業を並列的に行うことができる

 

 ・以前の状態に戻ることができる

 

皆さんも、

大幅にプログラムを改変した後に、

「やっぱり前の状態に戻したい!!」

とか

間違って必要な内容を消去した後に上書きしてしまって、

「オーマイガー!!!」

と後悔した経験があると思います、、、。

バージョン管理を行うことで、以前の状況に簡単に戻ることができるようなります!

 

 

・変更者と変更内容を知ることができる

 

先ほどの例で挙げたように、

変更した部分や内容を一目で分かりやすく管理することができます。

特に複数人で、開発を行う際に大変役に立つと思います!

 

 

・ブランチ(枝分かれ)によって
 複数の作業を並列的に行うことができる

gitなどのバージョン管理システムで導入されている

ブランチという機能を用いることで複数の作業を並列的に行うことができます。

図の例のように、

Aさんはレイアウトを変更、Bさんはシステムを変更といったように

同時に作業を行い、最後に統合(マージ)することができます。

 

f:id:cheerapps:20180409182733p:plain

 

 

以上、今回はバージョン管理についての簡単な説明でした。

最近覚えたての初心者なりに恐縮ですが、

頑張って説明してみました!

 

 

ここまで聞いて確かに便利そうだけど、

実際にどうやって使うの??って話になりますよね(-_-;)

 

ですので、ウィジェットカウンター開発の際にも利用した

メジャーなバージョン管理システムであるgitと

それを利用するウェブサービスGitHubについて

説明していこうと思います!

 

 

また、この部分について詳しく説明してほしい、

分かりにくかったor分かりやすかった、感想等々

頂けるととても励みになりますので、

是非気軽にコメントよろしくお願いします!

 

本日も読んでくれて

ありがとうございました!

 

それでは!!

 

 

1作目のアプリをリリースしました!

皆さんこんばんは!

大学院生のきゅうりです!

 

昨日の記事で報告した通り、本日

cheerapps初めてのアプリをリリースしました!!

 

 

リリースしたandroidアプリはこちらです!

 play.google.com

 

ホーム画面上にウィジェット

カウンターを設置できるアプリです

実際に使用するとこんな感じになります!

 

f:id:cheerapps:20180407201119p:plain

 

複数のカウンタを同時に配置することができ、

また3種類のテーマから選択できるようになっています。

 

是非ダウンロードして使っていただけると幸いです!!

 

shiitaくんとアプリ開発を始めたのが

まだ大学生だった3月中旬でした。

そこから約3週間、色々な苦労もありながら、

ようやくリリースすることができました!!

 

今回の開発で大変多くのことを経験することができました。

この経験を活かして次のアプリを開発していきます!

是非応援よろしくおねがいします!

 

それでは!!

 

ホームページ作成サービス Z.com Studio


アプリ完成までの経過の話

皆さんこんばんは!

ブログ見ていただいてありがとうございます!

大学院生でアプリ開発に奮闘中のきゅうりです!

 

 

さて前回の記事で、

最初に作るandroidアプリを決定したわけですが、

今日はそのアプリ開発の経過について説明したいと思います!

 f:id:cheerapps:20180406211820p:plain

これから開発したいの方の反面教師として

参考になればと思います。

 

 

まずは、紙にアバウトなデザインなどを書いてイメージを共有しました。

 

次に、プログラムを管理するために

shiitaくんに教えてもらいながら、gitやGitHubの使い方を勉強しました。

 

過去に一度書籍を読みながらアプリを開発しようとしたのですが、

サンプルプログラムを入力しただけなのにめっちゃエラー出るし、

わけわかんないって感じで断念していました。

 

というわけでアプリ開発に関してはほぼ初心者の僕、、、

 

正直わかんないことだらけで、一からshiitaくんに教えてもらいました!

聞くことだらけでホントに申し訳なかったです(-_-;)

意外とここで時間がかかってしまいました。

 

 

需要があればgitやGitHubについても初心者として学んだことを

記事にしようと思いますので、コメント待ってます!

 

コミットやプルなどの単純な操作を覚えたので、実装開始!!

 

システム部分はshiitaくん、デザインやプロモーションに関しては僕が担当するという割り振りにしました。

 

僕は、Twitterアカウントやこのブログの開設を行ったりなど、

なれない作業に戸惑いながらも、色々と調べながらなんとか

作業を完了しました!!

 

shiitaくんは、アプリ開発経験はあったものの、

ウィジェットを用いたアプリ開発は初めてだったので苦戦していました

この点については今後shiitaくんがブログ記事を書いてくれる予定です!

 

後から、デザインや使用を変更するために、

プログラムを大きく変更しなくてはいけないことが何度かあり、

大きく時間をロスしてしまいました。

 

こんな感じでアプリ開発に奮闘していました。

 

 

 

今回の反省として、

実装する前に仕様やデザインは

ある程度固めておくべきだと実感しました。

 

次回以降に活かしていきたいと思います!!

 

 

cheerappsによる初めてのアプリ

ウィジェットカウンター開発中を行っていましたが、

なんとか、リリースの目途が立ちました!!

リリース日は

 

 

明日です!!

(頑張って明日中にリリースします笑)

 

 

 

明日はリリースに関する記事を載せる予定ですので、お楽しみに!!

それでは

 

ホームページ作成サービス「グーペ」

開発するアプリ決定までの話

皆さんこんにちは!

大学院生のきゅうりです!

 

本日は初めに開発するアプリを決定するまでのお話です!

 

先日、晴れてアプリ開発チームcheerappsを結成したわけですが、

まだ読んでいない方はこちらをご参照ください
開発チーム結成までの話

 

どんなアプリ作るの??

どれくらいのダウンロード数が目標??

などなど、決めることが山積みでした。

 

エンジニア担当のshiitaくんと協議した結果、androidアプリを開発することに決定!

そして当面の目標として、

 

”1アプリで1000ダウンロードの達成”

 

を掲げて頑張っていくことに!

 

androidでアプリを作るのならウィジェット(ホーム画面に設置できる小型化アプリ)を活用したい!

ってことでウィジェットを利用したアプリを作る”ことに決定しました。

 

しかし、どれくらい需要があるのかわからないし、

初めに大掛かりな計画をして大失敗するのも辛すぎる、、、

 

そこで、ウイジェットの機能を活用する練習になるし、

短期間で開発できて一石二鳥ってことで

“比較的単純なツールアプリを作成する”という方針に定めました!!

 

どんなアプリを作ろうか、Google Playでタイマーやメモアプリなどリリースされているアプリを色々眺めてました。

 

そんな中、リリースされてるアプリ数が少なく、その割に意外とダウンロードされてるツールアプリを発見しました。それは、、、

 

 

 

 

 

 

カウンターです!!!

f:id:cheerapps:20180405133231p:plain

どーん!!笑

 

 

どんな用途でよく使われているのかよくわかりませんでしたが、

(使ってる人いたら是非コメントください!)

意外にもダウンロード数が多い!!

この分野はワンチャンあるのでは??と感じて

 

ウィジェットを利用したカウンターアプリ

を開発することに決定しました。

(もっとすごい物を期待してた方ごめんなさい(-_-;)、

今後開発していくのでしばしお待ちを、、、!!)

 

 

というわけで、ウィジェットとしてホーム画面に設置できるカウンターアプリ

ウィジェット カウンター”(名前そのままです笑)

を一生懸命開発中です!!

 

 

リリースまでの経過や苦難等の記事をこれからバシバシ書いていく予定です!

技術的に苦労した部分についてはshiitaくんが記事を書いてくれるのでお楽しみに!

これからアプリ開発を始めたい初心者の方々や同じような部分で苦労している方々の参考になればと思います!!

 

 

リリースしましたら、こちらのブログで報告したいと思います!!

それでは 

 

美しいホームページを作るなら Z.com