読者です 読者をやめる 読者になる 読者になる

kojiko-android’s blog

開発中にハマった時の解決策や、忘れがちなことを残しておきます。

EditText等で、Enterキーの入力イベントを検出する

ユーザーがEditTextで文字を入力中に、ソフトキーボードのEnterキーが押されたときのイベントを検出する方法です。

EditTextに以下のようにリスナーをセットします。

mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {

    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        if (actionId == EditorInfo.IME_ACTION_DONE){
        
        //    Enterが押されたときに行いたい処理

        }
        return false;
    }
});

ただし、EditTextが複数ある場合だと、Enterを押したときに次のEditTextにフォーカスが移ってしまい、
このイベントが呼ばれません。

ちょっと強引ですが、if分に以下の条件を追加することで、イベントを検出できます。

if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_NEXT){
}

 ソフトキーボードの右下のEnterの位置にあるボタンは、ユーザーの入力の状況によってアクションが「GO」、「次へ」、「検索」のように変化します。
本来ならば、それぞれのアクションに応じた振る舞いを検討するのが良いのかもしれません。

Android実機でデバッグできるようにする

Android実機でデバッグできるようにする方法です。
各メーカーの公式ドライバーをインストールする方法もありますが、
以下の方法を利用するとわずか数分で実機デバッグができるようになります。


端末のほうでは、開発者向けオプションが有効になっているものとします。

実機端末とPCを、データ転送可能なUSBケーブルで接続

PCのデバイスマネージャーをひらく

「ほかのデバイス」のところにある認識されていないアンドロイド実機を右クリックし、
プロパティを開きます。
詳細タブのプロパティからハードウェアIDを選択し

USB\VID_0E8D&PID_201D&REV_????&MI_01
USB\VID_0E8D&PID_201D&MI_01

これらの情報が表示されるウィンドウを開いておきます。


Android SDK をインストールしたディレクトリ内のextras/google/usb_driverにある、android_winusb.infをメモ帳などで開く

SDKのインストール先は、android studioのProject Structureで確認できます。
android_winusb.infが無いときは
AndroidStudioのSDKマネージャーからGoogleUsbDriverをダウンロードします。


[Google.NTx86]と[Google.NTamd64]のところの一番最後に、以下のように記述します

[Google.NTx86]

・・・略・・・

;MIYABI
%SingleAdbInterface% = USB_Install, USB\VID_0E8D&PID_201D&REV_????&MI_01
%CompositeAdbInterface% = USB_Install, USB\VID_0E8D&PID_201D&MI_01

[Google.NTamd64]

・・・略・・・

;MIYABI
%SingleAdbInterface% = USB_Install, USB\VID_0E8D&PID_201D&REV_????&MI_01
%CompositeAdbInterface% = USB_Install, USB\VID_0E8D&PID_201D&MI_01


端末の名前は、特に決まりはなさそうです。
行はコピー&ペーストして、カンマ(,)以降を、先ほどのハードウェアIDに置き換えます。

メモ帳を閉じて、変更を保存

再びデバイスマネージャーに戻り、先ほどの「不明なデバイス」を右クリックし、「ドライバーソフトウェアの更新」をクリック

「コンピューターを参照してドライバーソフトウェアを検索します」をクリック

android_winusb.infを含むフォルダーを入力し、「次へ」をクリック

途中、警告ダイアログが出ることがありますが、内容に問題がなさそうなら承諾して進めてください。


ドライバーの更新に成功すると、AndroidStudioでの実機デバッグが可能になります。

Kojittoアプリ プライバシーポリシー

プライバシーポリシー

2016年12月22日 改訂
開発者:Kojitto
mail:kentaro198477@gmail.com

Kojittoの提供するAndroidアプリ「タイマークラシック」、「Digital Timer - free」(以下、当アプリ)では、
ユーザー様の個人情報(年齢・性別・位置情報等)の一切を取得しておりません。
よって、第三者への提供、開示等行われることもありません。
当アプリのダウンロード時に必要となる権限「カメラ機能の利用」については、
当アプリ内でのタイマー終了時におけるユーザー様への通知の手段として、端末のフラッシュライトを点灯させることがあり、
これは端末のカメラ機能の一部を利用することで実現しています。撮影・録画等を行うプログラムは一切含まれておりません。
プライバシーポリシーの変更を行った場合は、当ブログにて通知いたします。

アクティビティをスタックに残さなくする

例えばタイマーアプリを作っていて、

タイマー終了時に透明なアクティビティを開始して、

アラートダイアログを表示し、

OKが押されたり、dismissされたりしたときに

そのアクティビティをフィニッシュするとします。


この透明アクティビティがスタックに残っていると、

端末の”□”をタップしたときに表示され、ユーザーがそれをタップすると

またアラートダイアログが表示されるので、タイマーの挙動としては不自然です。

透明アクティビティがスタックに残らないようにするには、マニフェストに以下のように記述します。

<activity
    android:name=".MyActivity"
    android:excludeFromRecents="true"
    android:theme="@android:style/Theme.Translucent.NoTitleBar"
         >

android:excludeFromRecentsをtrueにするだけです。

また、テーマを@android:style/Theme.Translucent.NoTitleBarにした場合、
アラートダイアログのUIデザインが昔っぽいものになってしまうので、
これを避けるには、独自にスタイルを定義する必要があります。

【電卓アプリを作るvol.7】アプリ終了時の状態を保存

前回まで・・・
計算結果に端数が出た場合に、
それを丸めて表示する静的メソッドを実装しました。

今回は、
計算中に電話がかかってきて、
戻ったら全てクリアーされていたという、
悲しいことが起こらないように、
終了時の状態を保存し、
再開したときにロードする機能を実装していきます。

何を保存するべきか

電卓を開いたときに、
どのような情報があれば、
同じ状態を再現できるでしょうか?

最後に見ていたものと、再開直後が違っていたら、
不安になってACを押してしまうでしょう。
なので、
①ディスプレイに表示されている文字列
は欠かせません。
見たままを保存します。
単純に、StringBuilder.toString()で得られる文字列を保存します。

電卓には状態があると前途しました。
状態によって呼び分けられるメソッドが変わってきます。
②現在の状態
を保存します。
現在の状態を知るためのメソッド、
int getStateValue()をすでに用意していますので、
それによって得られた定数も保存します。

状態が右辺入力中なら、
③左辺の情報が無いと計算できません。
演算子も同様です。
定数計算のために、
⑤右辺の値も必要です。

今回はメモリー機能やグランドトータル機能は実装していませんので、
これら5つの情報を保存すれば、
どの状況も再現が可能です。

再現するには

アプリのオンポーズ時にこれらを保存し、
オンレジューム時に、
Calcクラスのインスタンスのフィールドに、
上記の情報を格納します。
stateに状態に応じたクラスのインスタンス
value1に左辺の値をdoubleで、
value2に右辺の値を同じくdouble、
enzanshiは、演算子を示す定数、
stringBuilderに、最後に見ていたのと同じ文字列を入れます。

Calc内に、これら5情報を保持するクラスを作ります

コンストラクターも用意します。

    public class CalcHolder{

        public int state;
        public String dispString;
        public double leftValu,rightValue;
        public int enzanshi;

        public CalcHolder(int state,String dispString, double leftValue,
                          double rightValue, int enzanshi) {

            this.state = state;
            this.dispString = dispString;
            this.leftValu = leftValue;
            this.rightValue = rightValue;
            this.enzanshi = enzanshi;
        }
    }

同じくCalc内に、セーブ用とロード用のパブリックなメソッドを定義します。
セーブ用のgetHolderでは、CalcHolderのインスタンスを返し、
ロード用のsetHolderは、CalcHolderのインスタンスを渡すことによって、
前回の状態に復帰します。

   public CalcHolder getHolder(){
        return new CalcHolder(state.getStateValue(this),stringBuilder.toString(),
                value1,value2,enzanshi);
    }
    
    public void setHolder(CalcHolder holder){
        switch (holder.state){
            case State.STATE_A:
                changeState(StateA.getInstance());
                break;
            case State.STATE_B:
                changeState(StateB.getInstance());
                break;
            case State.STATE_C:
                changeState(StateC.getInstance());
                break;
            case State.STATE_D:
                changeState(StateD.getInstance());
                break;
        }
        this.value1 = holder.leftValu;
        this.value2 = holder.rightValue;
        this.enzanshi = holder.enzanshi;
        set(holder.dispString);
    callback.onTextChanged(stringBuilder.toString());
    }


メインアクティビティのonPauseとonResumeに保存・復帰の処理を記述します。
Gsonを利用しますので、Gsonライブラリーを読み込んで置きます。
(Gsonについての関連記事)

  @Override
    protected void onPause() {
        super.onPause();
        Calc.CalcHolder holder = calc.getHolder();
        Gson gson = new Gson();
        String js = gson.toJson(holder, Calc.CalcHolder.class);
        getSharedPreferences("pref",MODE_PRIVATE).edit().putString("holder",js).apply();
    }

    @Override
    protected void onResume() {
        super.onResume();
        final String NULL = "null";
        Gson gson = new Gson();
        String js = getSharedPreferences("pref",MODE_PRIVATE).getString("holder",NULL);
        if (js.equals(NULL)){
            calc.allClear();
            //    初回起動
        } else {
            calc.setHolder(gson.fromJson(js, Calc.CalcHolder.class));
        }
    }

正しく動作するか確かめてみてください。
マニフェストでアプリをポートレイトにしていなければ、
端末の向きを変えてみて、数字がリセットされないことでも確認できます。

ちなみに、onResume内の「初回起動」とコメントアウトした部分は、
そのまま、ダウンロード後の第一回目の起動なので、
サンキューメッセージ等を表示するのもいいかもしれません。

【電卓アプリを作るvol.6】計算結果を成型する

前回まで・・・
とりあえず、必要最低限の電卓ができました。
今回は、計算結果を丸めて表示する機能を実装します。

現状の問題点

今の段階では、1+1=の結果が、

"2.0" (シンプルに "2" でいいんじゃない?)

また、10÷6のように割り切れない計算をすると
桁数が非常に多くなってしまううえ、

"1.6666666666666667"

というふうに、最後が謎の7になっています。
これはコンピューター上の仕様のようですが、
一般的には、「6が永遠と続く」という認識なので、
ユーザーからしたら不具合かと思われかねません。

そこで、一般的な電卓に習い、12桁までの表示にするとか、
小数点以下n位までの表示にする、
といった機能を実装していきます。

一般電卓の多くは、「小数点セレクター」「ラウンドセレクター」
という機能があり、
「小数点セレクター」は、小数点以下第何位まで表示するか、
「ラウンドセレクター」は、小数点セレクターで設定した位置に丸める際、
切り上げか、切捨てか、四捨五入するかなどを設定できる機能です。
カシオ計算機のサイトなどで使い方が詳しく載っています。
今回の実装ではこれらの機能をお手本にしているので、
理解度を高めるためにも、一度ご覧になることをオススメします。

自作のRoundSelectorクラス

Calcから送られてきた文字列を、成型した文字列に変換するクラスです。
引数に、計算結果のdouble、小数点の位置、丸めの方法を定数で指定します。
テキストビューにセットする前にフィルターを挟むように使います。
静的メソッドとして定義しました。

public class RoundSelector {

    public static final int POINT_5 = 5,POINT_4 = 4,POINT_3 = 3,POINT_2 = 2,POINT_1 = 1,POINT_0 = 0;
    public static final int ROUND_F = 10,ROUND_CUT = 11,ROUND_UP = 12,ROUND_5_4 = 14;

    public static String conversion(double value, int point, int round){
        String string;

        BigDecimal bd = new BigDecimal(value);

        int mode = 0;
        switch (round){
            case ROUND_F:
                return bd.toString();
            case ROUND_CUT:
                mode = BigDecimal.ROUND_DOWN;
                break;
            case ROUND_UP:
                mode = BigDecimal.ROUND_UP;
                break;
            case ROUND_5_4:
                mode = BigDecimal.ROUND_HALF_UP;
                break;
        }

        BigDecimal bigDecimal = bd.setScale(point,mode);
        string = bigDecimal.toString();
        Log.d("hogehoge","bd =" + bigDecimal.toString() );
        
        return string;
    }

    public static String conversion(String value,int point,int round){
        return conversion(Double.parseDouble(value),point,round);
    }

    public static String formatBasic(String string){

        DecimalFormat df1 = new DecimalFormat("#,##0.############");
        return df1.format(Double.valueOf(string));

    }

}

メインアクティビティの、Calcからのコールバックで送られてくる文字列に対して、
RoundSelectorを適用します。

calc = new Calc();
calc.setListener(new Calc.Callback() {
            @Override
            public void onTextChanged(String string) {
                displayView.setText(string);
                Log.d("hoge", RoundSelector.conversion(string,0,RoundSelector.ROUND_CUT));
                Log.d("hoge", RoundSelector.conversion(string,3,RoundSelector.ROUND_CUT));
                Log.d("hoge", RoundSelector.conversion(string,5,RoundSelector.ROUND_5_4));
                Log.d("hoge", RoundSelector.conversion(string,3,RoundSelector.ROUND_F));
            }
        });

10÷6は、以下のような結果となります。

D/hoge: 1
D/hoge: 1.666
D/hoge: 1.66667
D/hoge: 1.6666666666666667406815349750104360282421112060546875

ただし、現段階ではコールバックで送られてくるテキスト全てに適用しているので、
ユーザーが少数を入力した際も丸められてしまいます。
テキストが、入力中なのか、計算の答えとして送られてきたものなのか、
判別する処理が必要です。
私の場合は、State.getStateValue()で得られる定数がSTATE_D(答えを表示している状態)
であれば数値の文字列を成型するように修正しましたが、
もっといいやり方もあると思うので、
独自の実装をしてみてください。


端数処理方法の設定として、

・小数点以下の桁数
・丸める方法

の定数を、SharedPreferencesに保存しておくと、
ユーザーの使用環境に合わせた、
利便性の高い電卓にすることができます。

【電卓アプリを作るvol.5】UIをレイアウトする

前回まで・・・
汎用性の高いCalcクラスを作りました
今回は、最低限のUIと電卓として機能するまでを行います。

UIをレイアウトします。
お好みのようにデザインしてください。
基本的には、電卓の液晶画面にあたる部分をTextViewで、
キーはButtonで配置しましたが、
キャンバスで描画する芸術的な電卓もありだと思います。

今後、押したら画像が変わる9パッチのボタン画像や、
レイアウトのサイズに合わせて文字列を自動で拡大・縮小する
ビューもご紹介しようと思いますが、
今回は最低限の作業を行っていきます。

基本的なレイアウトを作りました。
f:id:kentaro198477:20160925025608p:plain

Calcのインスタンス化と液晶表示

Calcクラス型のフィールドを定義します。
calcとしました。
メインアクティビティのオンクリエイト等で、
Calcをインスタンス化します。
Calcで定義したコールバックを受け取れるようにします。

calc = new Calc();
        calc.setListener(new Calc.Callback() {
            @Override
            public void onTextChanged(String string) {
                textView.setText(string);
            }
        });

onTextChangedの引数に入っている文字列が、
電卓の液晶画面に表示すべき文字列です。
まだ、doubleがそのまま文字列になっただけなので、
割り切れない数は、かなり桁数が多くなりますが、
今後、フォーマットして表示できるように改良します。

ボタン操作

ボタン全てにリスナーを付けます。
ボタンが押されたら、それに対応した定数を
calc.input()の引数に渡します。
[=]キーだったら、calc.input(Calc.KEY_EQUAL);という具合です。
数字キーは、ボタンテキストをそのままint型にして
calc.input(5);のようにします。
もし、和風な電卓で、表示が「壱、弐、参」などにしていたら、
それぞれにも定数を用意する必要があります。
Integer.parseInt("壱");ってできるのかな?(笑)

全てのボタンに適用したら、
何かボタンが押されるたびに、コールバックで文字列が送られてくるので、
それを表示すれば、電卓としては目的達成です!

電卓って、意外とボタンが多いことに気づきます(笑)
以下のように、IDで判断して処理を分けてもいいですが、
文字列で判定すると、ローカライズの際に面倒になる可能性があります。

public void onKeyClick(View v) {
    switch (v.getId()) {
            case R.id.b_00:
                calc.input(Calc.KEY_00);
                break;
            case R.id.b_dot:
                calc.input(Calc.KEY_DOT);
                break;
            case R.id.b_equal:
                calc.input(Calc.KEY_EQUAL);
                break;
            case R.id.b_tasu:
                calc.input(Calc.KEY_TASU);
                break;

      ・・・

次回は、”計算結果を丸め”て、会社の端数処理基準等に合わせた表示を可能にする、
小数点セレクター・ラウンドセレクターを実装していきます。