Kuwapp's Blog

Android と Flutter やってます

【Android】画面タップでキーボードを非表示にする

画面タップでキーボードを非表示するよくある実装です。

アクティビティ

public class MainActivity extends ActionBarActivity {

    private View mFocusView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mFocusView = findViewById(R.id.focusView);
        mFocusView.requestFocus();

        EditText editText = (EditText)findViewById(R.id.editText);
        editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if(!hasFocus) {
                    // フォーカスが外れた場合キーボードを非表示にする
                    InputMethodManager inputMethodMgr = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
                    inputMethodMgr.hideSoftInputFromWindow(v.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
                }
            }
        });
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mFocusView.requestFocus();
        return super.onTouchEvent(event);
    }
}

レイアウト

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/focusView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:focusable="true"
        android:focusableInTouchMode="true"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/editText" />

</RelativeLayout>

説明

onTouchEventをオーバライドして画面タップをフックし、フォーカスを移すために用意したView(今回は幅と高さを0dpにしたTextView)にフォーカスを移します。
EditTextのOnFocusChangeListenerはフォーカスに変更があった場合に呼ばれるので フォーカスが外れた場合キーボードを非表示にします。
requestFocusでフォーカスを移すにはxmlで以下を定義する必要があるのでお忘れなく。

android:focusable="true"
android:focusableInTouchMode="true"

参考

ソフトキーボードを非表示にする « Tech Booster

【1人でAndroidアプリ開発】財布の残高管理アプリを作ってリリースしてみた

ここ2ヶ月ほど地道にAndroidアプリを開発していました。
アプリ名は「Walleter」でシンプルなお小遣い帳アプリです。

Walleter - シンプルなお小遣い帳アプリ

今回の開発を通して思ったことや、やったことなどをまとめたいと思います。

・Walleterについて

https://play.google.com/store/apps/details?id=com.kuwapp.walleter

まずはアプリの説明
Walleterは財布の残高の確認に特化したお小遣い帳アプリです。
起動直後に財布の残高を表示し、
今いくら持ってたっけ?みたいなときに使われることを想定しています。

というのも最近気がつくと財布の中身がスッカラカンになってたりして
今の残高といくら使ったかだけ把握しておきたいなと思って作りました。
そのため何にいくら使ったとか、カテゴリ分けにグラフ表示したりはできません。
完全に入ったお金と出ていくお金だけを入力するだけです。

機能としては残高の確認、収支の確認(期間で分けることも可)ができます。
いくら持ってるかと、いついくら使ったかを確認だけできれば良い人におすすめします。

Walleter - シンプルなお小遣い帳アプリ

・ 開発いろいろ

■期間
2ヶ月で毎日1、2時間ぐらい開発していました。
こんなに機能少ないのにけっこう時間かかったなーと。
原因は公開する目的を見失ってひたすらコードを修正していたからです。
途中で公開してから修正もできるし、内部のことは後回しにしようと途中で気付きました。
とりあえず公開!これを目指すべきでした。
時間かけるとモチベも下がってきますしね。。

■デザインとか
自分はプログラムは好きですがデザインや絵書いたりが苦手なので
UIとアイコン作成に非常に時間がかかりました。
アイコンがドロイド君から変わるだけでけっこうモチベが変わるので早めに作った方が良いかも

■BitbukcetとSourceTreeでバージョン管理
個人でもプライベートリポジトリが作成できるBitbucket超おすすめです!
バージョン管理出来るとちょっといじってみて駄目ならすぐに戻したりとかできてかなり作業が捗りました。

■公開前が辛い
公開前はストアの説明文考えたり、宣伝用画像作ったりとかなり面倒です。
宣伝用画像って以前は必要なかった気がするのだけど。。いつからだろ
空いた時間で説明文は考えておくと良かったです。

■いつリリースするか決めておこう
きっちりと公開日を決めておかないとダラダラと伸びてしまいます…。
11月後半にはほぼ完成していたのですが
やっぱりここ変かなとか調整してかなり伸びてしまいました。
まずは公開して修正というのが良いかもしれません。

・積極的にライブラリを使おう

巷にはたくさんの無料で使えるライブラリが存在します。
Walleterでは以下の6つを使用しました。
keyboardsurfer/Crouton · GitHub
JohnPersano/SuperToasts · GitHub
vinc3m1/android-segmentedradiobutton · GitHub
Androguide/HoloGraphLibrary · GitHub
kolavar/android-support-v4-preferencefragment · GitHub
BoD/android-switch-backport · GitHub

たとえばグラフは上記のHoloGraphLibraryを使用しています。
グラフなんて1から書くとかなり面倒ですが既存のライブラリを使用すると大幅に時間を削減できます。
実装の前に使えるライブラリがないか調べてみることも良いかもしれません。

以下のアプリは多くのライブラリがまとめられていて
実際にアプリ内で動作を見ることができるのでライブラリを探すのにかなり使えます。
Libraries for developers - Google Play の Android アプリ

ローカライズ

どうせなら英語にも対応してみたいなと思い人力翻訳サービスのGengoを利用してみました。

クラウドソーシングの人力翻訳「Gengo」

プランがスタンダード、プロ、ウルトラの3つ合ってクオリティが違うようです。
料金は1文字だと順に4円、7円、10円でした。

今回はアプリ内の文言とストアの説明文の翻訳をプロで依頼して約4000円程度かかりました。
翻訳も依頼してから3時間後に完了とけっこう早いです。
また、翻訳した人とメッセージのやり取りもできアドバイスをもらえました。
アプリ内でデータの初期化に「Initialize」という文言を使用していましたが
例えば次のようにアドバイスをもらうことができ大変助かりました。
「Initializeをdeleteに変えました。英語ではInitializeはハードディスクなどの媒体を消すときに使いますのでちょっと違和感ありました」

翻訳した方によっても変わるとは思うのですが、Gengoはおすすめします。

・広告

広告・・・貼りませんでした。
開発当初はバナー広告貼って課金で広告を外せるようにしようと思っていましたが
終盤になって広告を貼るスペースがないことに気付きました。。
バナー貼るなら始めから広告を含めたレイアウトを行うことをおすすめします。

・今後

次のアプリを考え中です。
AndroidiOSにするかはまだ分かりませんが、今回より完成度は高めたいなーと。
次はサーバーを使用するアプリを作成したいと考えています。
サーバーあれば作れる幅が広がるしね。
今回の開発の反省点を次に生かしていきたいと思います。

【Android】AlarmManagerで登録された通知をコマンドラインで確認する

ローカル通知を実装したが、通知が届かない。
Alarmの登録でバグがあるのか、レシーバーが上手く動いていないのか。。
問題の切り分けにけっこう使えます。

adb shell dumpsys alarm

端末を繋いでターミナルを起動し上記コマンドを実行します。
もちろんadbのパスを通す必要があります。

Realtime wakeup (now=2014-12-17 22:54:44):
  RTC_WAKEUP #9: Alarm{407cf0d0 type 0 com.google.android.partnersetup}
    type=0 when=+6d23h38m22s968ms repeatInterval=0 count=0
    operation=PendingIntent{407c50f0: PendingIntentRecord{407cd2d8 com.google.android.partnersetup startService}}
  RTC_WAKEUP #8: Alarm{40a7f548 type 0 com.google.android.gms}
    type=0 when=+5d1h41m0s353ms repeatInterval=0 count=0
    operation=PendingIntent{407dbe68: PendingIntentRecord{408b0ee8 com.google.android.gms broadcastIntent}}
  RTC_WAKEUP #7: Alarm{40a6e418 type 0 com.google.android.gms}
    type=0 when=+1d2h6m41s370ms repeatInterval=0 count=0
    operation=PendingIntent{40716f90: PendingIntentRecord{40a16210 com.google.android.gms broadcastIntent}}
  RTC_WAKEUP #6: Alarm{409f39f0 type 0 com.kuwapp.waleter}
    type=0 when=+23h55m15s943ms repeatInterval=86400000 count=1
    operation=PendingIntent{40abe868: PendingIntentRecord{40be7ab0 com.kuwapp.waleter broadcastIntent}}
  RTC_WAKEUP #5: Alarm{408d3b00 type 0 com.android.providers.calendar}
    type=0 when=+21h39m28s532ms repeatInterval=0 count=0

このように登録されたAlarmが表示されるので確認できます。

【Android】PreferenceFragmentをAPI10以前で使用する

PreferenceFragment

API11で新しく追加された設定画面を容易に作成できるFragmentです。
このPreferenceFragmentはsupport-v4に含まれていないためAPI10以前のバージョンで使用できません。
PreferenceActivityを使用すればAPI10以前に対応できますが、ActionBarActivityを同時に使用できないため2系の端末でActionBarを表示できなくなります。

古い端末でActionBarを表示しつつPreferenceFragmentを使いたい!
と思ったので探すととgithubにsupport-v4-preferencefragmentがありました。
kolavar/android-support-v4-preferencefragment · GitHub

実装

実装…といってもクローンしてきてプロジェクトにリンクさせPreferenceFragmentと同じように使用するだけです。
とても便利なライブラリでした。
作成者に感謝。

注意点

import android.preference.PreferenceFragment;
↓
import android.support.v4.preference.PreferenceFragment;

importするクラスを間違えないこと。
通常のPreferenceFragmentを使用しても落ちますので…。

【Android】support-v7-appcompatにスタイルを対応させる

Android2系でもActionBarを使用するためにsupport-v7ライブラリのActionBarActivityを使用してみました。
単純にActivityをActionBarActivityへ書き換えただけだと次のようなエラーが出ます。

java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.


ActionBarActivityを使用する場合はテーマをTheme.AppCompatなどに変更する必要があります。
ググると大抵はmanifestのテーマをTheme.AppCompatなどに変更する方法が書かれていますが
スタイルをカスタマイズしている場合はそちらのを書き換えが必要。

<application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        <!-- Theme.AppCompatに変更 -->
        android:theme="@style/Theme.AppCompat" >
</application>

スタイルの書き換え

書き換え前

<style name="AppTheme" parent="android:Theme.Holo.Light">
     <item name="android:actionBarStyle">@style/ActionBarStyle</item>
</style>

<style name="ActionBarStyle" parent="android:Widget.Holo.ActionBar">
     <item name="android:titleTextStyle">@style/ActionBarTitleStyle</item>
     <item name="android:background">@color/gray</item>
</style>

<style name="ActionBarTitleStyle" parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Title">
     <item name="android:textStyle">bold</item>
     <item name="android:textColor">@color/white</item>
     <item name="android:typeface">sans</item>
</style>

単純なスタイルでActionBarのタイトルと背景色をカスタマイズしています。

書き換え後

<!-- Theme.AppCompat.Lightに書き換え -->
<style name="AppTheme" parent="Theme.AppCompat.Light">
<!-- android:actionBarStyleからandroid:を省く -->
     <item name="actionBarStyle">@style/ActionBarStyle</item>
</style>

<!-- Widget.AppCompat.ActionBarに書き換え -->
<style name="ActionBarStyle" parent="Widget.AppCompat.ActionBar">
<!-- android:titleTextStyleからandroid:を省く -->
     <item name="titleTextStyle">@style/ActionBarTitleStyle</item>
<!-- android:backgroundからandroid:を省く -->
     <item name="background">@color/gray</item>
</style>

<!-- TextAppearance.AppCompat.Widget.ActionBar.Titleに書き換え -->
<style name="ActionBarTitleStyle" parent="TextAppearance.AppCompat.Widget.ActionBar.Title">
     <item name="android:textStyle">bold</item>
     <item name="android:textColor">@color/white</item>
     <item name="android:typeface">sans</item>
</style>

注意点としてandroid:titleTextStyleなどの一部でandroid:の部分を省く必要があることです。

大抵は公式に書いてあります。
Styling the Action Bar | Android Developers

【Android】ActivityのonTouchEventが呼ばれない

次のActivityとレイアウトの組み合わせで試すとonTouchEventが呼ばれませんでした。

Activity
public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {	
        Log.d("onTouchEvent","call"); 
        return super.onTouchEvent(event);
    }
}
レイアウト
<RelativeLayout 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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.testtouchevent.MainActivity" >

    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </ListView>

</RelativeLayout>


どうやらListViewの下にはタッチイベントが通らないみたい?。
解決策としてはdispatchTouchEventを使用すること。
dispatchTouchEventはonTouchEventより早くコールされるようです。
ListViewのタッチイベントより先に呼ばれているっぽい

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
     Log.d("DispatchTouchEvent","call");
		
     return super.dispatchTouchEvent(ev);
}

【Unity】AssetDatabaseでアセットを操る!

AssetDatabaseとは

AssetDatabaseはプロジェクトのアセットにアクセスできる API です。アセットを見つけてロードするメソッドがあり、さらにアセットを作成、削除、修正できるメソッド等々があります。
〜略〜
Unity はアセットファイルのメタデータを保持するため、絶対にファイルシステムを使用して作成、移動、削除をするべきではありません。

http://docs-jp.unity3d.com/Documentation/Manual/AssetDatabase.html

アセットフォルダのファイルをスクリプトから読み込んだり、削除したりできるようです。
エディタ拡張でアセットを読み込んでシーンに配置とか、特定のアセットを一括で削除したりとかいろいろできそう。
C#でFileクラス使っても良いんでないの?と思っていましたが、単純にファイル移動してもメタデータは移動しないから問題あるんですね。

Assetの読み込み

Texture2D texture = AssetDatabase.LoadAssetAtPath("Assets/Texture.png", typeof(Texture2D)) as Texture2D;
TextAsset text = AssetDatabase.LoadAssetAtPath("Assets/Text.asset", typeof(TextAsset)) as TextAsset;

LoadAssetAtPathを使用します。
上記はテクスチャとテキストアセットを読み込む例
以下の2点に注意が必要です。
・Resrouces.Loadと違ってファイルの拡張子までつける
・パスはAssetsから指定する

Assetの作成

Material material = new Material (Shader.Find("Specular"));
AssetDatabase.CreateAsset(material, "Assets/MyMaterial.mat");
AssetDatabase.Refresh();

CreateAssetを使用します。
アセットを作成してもRefreshしないと反映されないか、反映に時間がかかるので注意

Assetの削除

//ゴミ箱へ移動
AssetDatabase.MoveAssetToTrash("Assets/material.mat");
//完全に削除
AssetDatabase.DeleteAsset("Assets/material.mat");

MoveAssetToTrashとDeleteAssetはどちらもAssetフォルダ内からは削除しますが
前者はゴミ箱に残るのに対し後者はゴミ箱にも残らず削除されます。

Assetのファイル名変更

AssetDatabase.RenameAsset("Assets/text.asset", "MyText");

RenameAssetを使用します。
2つめの引数はパスではなく拡張子を除いたファイル名です。
拡張子も変更できると勘違いして2番目の引数に拡張子を含めて3時間はまりました泣

Assetの変更時の注意

ScriptableObject myAsset = AssetDatabase.LoadAssetAtPath ("Assets/myasset.asset", typeof(ScriptableObject)) as ScriptableObject;
//scriptableObjectに変更を加える

例えば上記のようにScriptableObjectを取得し
データを書き換えるなど手を加えることがあると思います。
が、このコードだけだと多分実行時に行った処理は消えます。

ScriptableObject myAsset = AssetDatabase.LoadAssetAtPath ("Assets/myasset.asset", typeof(ScriptableObject)) as ScriptableObject;
//scriptableObjectにテクスチャを貼ったり変更を加える
EditorUtility.SetDirty (myAsset);

変更後はEditorUtility.SetDirtyを呼ぶ必要があります。
非常にはまりました。


他にもいろいろ機能はありますが、リファレンスで確認できます。

Unity - Scripting API: AssetDatabase