258 Lab アプリ開発日記

Andorid,iOSアプリ開発してます。

2020年5, 6月のアプリ開発収益

こんにちは。こんばんは🐶

資格試験のため、少し離れていました。泣
勉強とアプリ開発を並行してできればよかったのですが、不器用なもので、、😅

遅ればせながら、5月、6月の収益報告をいたします!

収益報告

  • 5月の収益報告
    「133円」(前月から+100円)

  • 6月の収益報告
    「526円」(前月から+393円)

f:id:dev_258lab:20200706000355p:plain

振り返り

収益が上がっている要因は、以下の2点が影響しているのかなと思いました🤔

  1. 説明文の変更に伴うインストール数の増加
    4月にAndroid版MyReviewの説明文を変えてみた。それによりインストール数が増加した! (今までAndroid版MyReviewの説明文がかなり雑だったので、iPhone版に合わせただけだが、、)
    インストール数の増加に伴い、クリック数も少し上がり、収益も上がっているようだ!

  2. レビュー投稿で高評価コメントを頂けた
    これもAndroid版MyReviewだが、高評価のコメントを頂くことができた!
    これのレビューを見て、訪問してくれた方がインストールしてみようかという気持ちになっているのかもしれない🙌

今回でわかった事は、

  • 説明文もかなり重要な要素!
    スクリーンショットだけではインストールまでいかないみたい。今まで疎かにし過ぎました。。今度からはちゃんと書きます😅

  • レビューを書いてもらえる仕組み作りがあった方が良い!
    今回は優しいユーザー様にレビューを書いて頂きましたが、こちらからレビューを依頼する機能があった方が良いと思った👨‍💻

今後のアクション

  • レビュー依頼の機能を実装してみる
    レビューをより頂けるように仕組み作りを構築したい。👨‍💻

  • ユーザー様にご要望頂いている機能を実装
    今まで手がつけられていなかった機能を実装していく!👨‍💻

  • 5月の新規アプリのリリース
    月1アプリ開発が遅れているので、そろそろリリースせんとヤバイ、、

雑談

自宅で手料理するようになった🍳
料理系のアプリも作ってみたいなーと思ったりしています。

では〜🐈

 

2020年4月のアプリ開発収益

こんにちは!こんばんは!🐶 もう5月も終わりそうですが、4月の収益報告をしたいと思います。

収益報告

2020年4月の合計収入は「33円」でした。。 やはりクリックされないと厳しい状況ですね。

前月が「185円」でしたので、-152円減💸

振り返り

やはり無料アプリは、クリックされないと厳しいようだ。。 アプリ開発の現実は厳しい。。。

今後のアクション

・クリックされる≒ユーザー数を増やすことだと思うので、前月同様、アプリのユーザー数を増やす&維持するため、以下の対応をしてみる。   
 ①スクリーンショットを少し変えてみる。   
 ②アプリの説明文を変えてみる。
・有料版アプリを出してみたい。
・ 新規アプリの開発

雑談

個人アプリ開発で万単位で稼いでる人って本当にすごいなーと日々思いますな。。 そういう人も私みたいに下積み時代があるのかしら。。

では〜🐈

【Android】KotlinでRecyclerViewの無限スクロール

マイレビューは初期表示時、一度に全データをロードする処理だったが、 テスト的に大量データを登録したらロード時に時間がかかりこれはいかんと思い修正。。 対応策として、無限スクロールを採用した。

1. RecycleViewにスクロールイベントを追加

無限スクロールを実現するために、リストをスクロールし、末尾付近に差し掛かったら、データをロードする処理を追加。 まずはスクロールリスナーを追加する!今回はこちらを参考にさせて頂きました。

qiita.com

abstract class ScrollPageListener(layoutManager: LinearLayoutManager): RecyclerView.OnScrollListener() {

    private var mLayoutManager: LinearLayoutManager? = null
    private var mPage: Int = 0
    private var mPreTotalCount: Int = 0
    private var mIsLoading: Boolean = false
    private var visibleThreshold = 0

    init {
        mLayoutManager = layoutManager
    }

    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
        super.onScrollStateChanged(recyclerView, newState)
    }

    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        super.onScrolled(recyclerView, dx, dy)

        // ロードするときに走るのは無駄なので、先にreturnする
        if (dx == 0 && dy == 0) {
            return
        }

        val visibleItemCount: Int = recyclerView.childCount
        val totalItemCount: Int = mLayoutManager!!.itemCount // グリッドの表示個数
        val firstVisibleItem: Int = mLayoutManager!!.findFirstVisibleItemPosition()

        if (mIsLoading) {
            if (totalItemCount > mPreTotalCount) {
                mIsLoading = false
                mPreTotalCount = totalItemCount
            }
        }

        if (!mIsLoading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
            mIsLoading = true
            load(++mPage)
        }

    }

    abstract fun load(page: Int)

}

次にActivity側でRecycleViewのスクロールイベントにスクロールリスナーを設定する!

recycleView.addOnScrollListener(object: ScrollPageListener(reviewRecycleView.layoutManager as LinearLayoutManager) {
        override fun load(page: Int) {
                Log.d("scroll", "末尾だよ! $page")
                // ここでデータをロードする処理を追加!
        }
})

2. 末尾を検知し、データをロードした後の処理

末尾を検知したらデータをロード!ロードしたら、RecycleViewのアダプター側でデータを追加する! ここでつまずいた。。Adapterの「notifyItemRangeInserted」を実行しても行が追加されなかった。 色々調べると、データを追加し、Adapterで「notifyItemRangeInserted」を実行した後、 RecycleViewの「recycleView.scrollToPosition」を実行しないと行が追加されないみたい🐶

Activity側の処理

// ロードしたデータをAdapterに追加する(selectedReviewList→ロードしたデータのリスト)
reviewAdapter!!.addItem(selectedReviewList)
// ロードしているデータの末尾までスクロール(これをやらないとスクロールしないとデータが表示されない!)
recycleView.scrollToPosition(reviewRowList.size)

Adapter側の処理

    override fun getItemCount(): Int {
        // ・・・
    }

    @Override
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        // ・・・
    }

    @Override
    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        // ・・・
    }

    /**
     * 行を追加する
     */
    fun addItem(reviewList: ArrayList<Review>) {

        val fromIdx = this.itemCount

        for (review in reviewList) {
            val idx = this.reviewList.indexOf(review)

            if (idx == -1) {
                this.reviewList.add(review)
            }
        }
       // ここでデータを追加されたことを通知!(通知しないと追加したデータが表示されない!)
        this.notifyItemRangeInserted(fromIdx, reviewList.size)
    }

リストって色々と考慮しないといけないなー🐈

マイレビューも是非インストールしてみてください!

play.google.com

今回は以上!

2020年3月のアプリ開発収益

こんにちは!こんばんは!🐶 ありがたいことに最近、利用者様が徐々に増えて収益がコンスタントに3桁になってきたので、テキトーなタイミングで個人アプリ開発の収益を振り返り、 次のアクションを考えられたらと思います。

収益報告

2020年3月の合計収入は「185円」でした!(185円で何ができる。。。!)

f:id:dev_258lab:20200401003347p:plain

前月が「121円」でしたので、+64円増!💰

振り返り

今月はクリック数が多く、収益が上がった。以前はクリックしてもらうことなどほとんどなかった。恐らく使って頂く回数が増えていることが要因と考える。 当面は、現在のプロダクトを改善していき、利用して頂く方々を増やしていきたい!🐈 最近は「MyReview」をよく使って頂けているようだ!ありがとうございます!

今後のアクション

・既存プロダクトのデザイン(レイアウトやストアのスクリーンショットなど)を見直し、ブラッシュアップすることで、新規ユーザー様の獲得、既存ユーザー様に長く使って頂く努力をする。 ・月に1回アプリのリリースを行うので、4月は新しいアプリを開発する!

雑談

アプリ収益で知人とご飯を食べに行くのが目標なのですが、まだまだ道のりは長い。。めげずに頑張ろうと思います。。 これからもよろしくお願い致します!

では〜🐈

【アプリ】MyReviewのデータバックアップ・復元手順について(詳細)

いつもMyReviewをご利用頂きありがとうございます。

当記事は、MyReviewのデータバックアップ、復元方法の詳細記事です。

データバックアップ手順

バックアップ手順を以下に記します。なお、データバックアップはアプリに登録されているデータ・写真をバックアップし、ソート設定などは引き継がれませんのでご了承下さい。

1. MyReviewを開き、バックアップ・復元画面を開きます。その後、[バックアップ]ボタンをタップします。

f:id:dev_258lab:20200321201951p:plain

2. 確認画面が表示されるので、「OK」ボタンを選択します。

f:id:dev_258lab:20200321202045p:plain

3. バックアップが完了すると、お使いの端末のストレージに「MyReview」というフォルダが作成されており、その中に「BackupFile」というフォルダが作成されます。このフォルダ内に現在アプリで登録されているデータ・写真が保管されています。

f:id:dev_258lab:20200321202339p:plain

4. BackupFileのフォルダをGoogle Driveやメールに添付などして、適宜保管して下さい。

データ復元手順

データの復元手順を以下に記します。 お使い端末のMyReview>ImportFile>BackupFileを読み込んでデータを復元します。

1. お使いの端末ストレージ内の「MyReview」フォルダの中の「ImportFile」というフォルダが存在するか確認します。存在しない場合は、MyReviewのアプリを開き、バックアップ・復元画面を一度開いてみて下さい。

f:id:dev_258lab:20200321202856p:plain

2. ImportFileの中にデータバックアップで取得したBackupFileフォルダを置きます。(BackupFileというフォルダの名前は編集しないで下さい。)

f:id:dev_258lab:20200321203050p:plain

3. MyReviewのアプリを開き、バックアップ・復元画面を開きます。その後、[復元]ボタンをタップします。

f:id:dev_258lab:20200321203242p:plain

4. 確認画面が表示されるので、「OK」ボタンを選択します。(現時点でアプリ内に登録されているデータは一度削除されますので、ご注意下さい。)

f:id:dev_258lab:20200321203422p:plain

5. データが復元されていることを確認します。

f:id:dev_258lab:20200321203504p:plain

以上です。その他ご不明点等ございましたら、コメント欄にご連絡頂ければ幸いです。

【Swift】アプリの表示名を変更する

久々に更新します。笑 今年は去年よりも多くの記事を書けることを目標にします!笑

ところで最近は私、Androidを少し離れ、先人の知識をお貸し頂きながら、SwiftでiOSアプリの開発を行っています。 そこで毎回アプリの表示名ってどう変えるんだっけ?と忘れてしまうので、メモ📝

まずは、Info.plistの「Bundle display name」を追加。(valueは初期値でOK)

f:id:dev_258lab:20200114235049p:plain

次にプロジェクトのファイルを開いて、「General」の「Display Name」を変更すれば完了🐶

f:id:dev_258lab:20200114235344p:plain

これで、アプリの表示名が変わります。

f:id:dev_258lab:20200114235647p:plain

では!

【Android】画像保存の際、カメラからの画像か保存されている画像かを選ばせる方法

仕事の方が忙しく、全く書けていませんでした。。

最近は、色々と落ち着いてきて、またアプリ開発の方に着手ができてきました。

先日、MyReviewの機能追加を行いました。 MyReviewは、自身で好きなもののレビュー記録を付けることができるアプリです。 レビュー記録を作成する際、画像を一緒に付けることができます。

play.google.com

以前はカメラの機能が使えなかったのですが、使えるようにしました!

・カメラを起動させて撮影した画像を保存

・予め保存している画像を保存

どちらかを選ばせる様な挙動にしています。

こんな感じです。(画質が粗いですが、アプリ上は問題ありません。)

f:id:dev_258lab:20191006155353g:plain
ギャラリーからかカメラからか選ばせる

流れとしては、以下のイメージです。

  1. ギャラリーからのIntentを生成
  2. カメラ起動のIntentを生成
  3. createChooserメソッドに1と2を渡し、1と2を選ばせる画面を表示

今回はKotlinで書きました。

まずは、Intentの生成

// ギャラリー、カメラのIntentを入れるList
val targets: MutableList<Intent> = mutableListOf()

// ギャラリーのIntent
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*"

targets.add(intent)

// カメラのIntent
val intent2 = Intent(MediaStore.ACTION_IMAGE_CAPTURE).apply {
      addCategory(Intent.CATEGORY_DEFAULT)
      putExtra(MediaStore.EXTRA_OUTPUT, createSaveFileUri())
      }

// ギャラリーとカメラのIntentをListに追加
targets.add(intent2)

val chooserIntent = Intent.createChooser(Intent(), "選択して下さい").apply {
      putExtra(Intent.EXTRA_INITIAL_INTENTS, targets.toTypedArray())
      }

startActivityForResult(chooserIntent, requestCode)

画像が選ばれた後のコールバック処理

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (resultCode == Activity.RESULT_OK) {
        // ここの中で受け取った画像に対して処理する
    }
}

では〜🐶