Learn to Live and Live to Learn

IT(たまにビジネス)に関する記事を読んで、考えて、使ってみたことをまとめる場。

Sources for 'Android API xx Platform' not found.

概要

Build.javaを開くとSources for 'Android API 22(1) Platform' not found.というエラーが出てきました.

インストールしても直らない🤔
(インストールされているかは,Tools > Android > SDK Manager > Show Pachage Details > Sources for Android xxがInstalledになっているかで確認できます)
Refreshを押しても反応しない🤔
(バグらしいです.泣)

という時に以下の方法で解決できたので紹介します.実行環境はAndroid2.1系と2.3系です.

解決方法

①Tools > Android > SDK Managerを開く
Android SDK LocationのEditを押す
③何もいじらず,NextをポチポチしてFinish
これでRefreshされ解決されました.

参考

kokufu.blogspot.jp

Android Spinner#onItemSelectedが起動時にも実行されることへの対策

Spinner#onItemSelected

初回起動時もSelectされたとして反応してしまいます.
初回は処理を避けたい時の対応方法を紹介します.

実装

よくある方法にSpinnerがフォーカス可能かをフラグとして使うものがあります.
falseが初回起動,trueがそれ以降です.
バックグラウンドから復帰した時は初回と判定されます.
フォーカス可能かはSpinner#setFoucusableで設定できて,値はSpinner#isFocusableで取得できます.

もちろんフォーカスの代わりにメンバー変数でFlagを持っといてもOKです. メンバー変数バージョンはコメントアウトに記載しました.

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private Spinner mSpinner;
    // private boolean mIsFirstBoot = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mSpinner = (Spinner) findViewById(R.id.spinner);
        mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                // 初回起動時の動作
                if (mSpinner.isFocusable() == false) {
                // if (mIsFirstBoot) {
                    mSpinner.setFocusable(true);
                    // mIsFirstBoot = false;
                    Toast.makeText(getApplicationContext(), "初回起動", Toast.LENGTH_SHORT).show();
                    return;
                }
                // 初回以降の動作
                Toast.makeText(getApplicationContext(), "初回以降", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {

            }
        });
        // 初回起動時の対応
        mSpinner.setFocusable(false);
    }
}

activity_main.xml

(略)
    <Spinner
        android:id="@+id/spinner"
        android:entries="@array/list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"></Spinner>
(略)

strings.xml

<resources>
    <string name="app_name">TestApp001</string>
    <string-array name="list">
        <item>hoge</item>
        <item>fuga</item>
        <item>piyo</item>
    </string-array>
</resources>

参考

d.hatena.ne.jp

Android Web to Appする(アプリ編)

App Indexing

AndroidにはApp Indexingという仕組みがあります.

AppIndexingとは、ウェブページURLやキーワードとアプリの特定画面へのディープリンクを紐付け、Google検索結果から直接アプリの特定画面を起動させるための仕組みです。 by Qiita

ウェブもアプリも持っているサービスなんかは,この仕組みを使うと簡単にWeb to Appできます.
※ウェブからもアプリからも実装が必要なんですが今回はアプリ側の実装を紹介します.

qiita.com

設定はAndroidManifest.xmlだけで可能です.

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data
        android:scheme="https"
        android:host="m.yahoo.co.jp"
        android:path="/" />
    <data
        android:scheme="http"
        android:host="m.yahoo.co.jp"
        android:path="/" />
</intent-filter>

actionタグにはVIEW,categoryタグにはBROUSABLEDEFAULTを書きます.
DEFAULTは任意です.指定しないとGoogle検索結果以外からディープリンクできなくなります.
dataタグにはschemeなどを指定します.何が指定できるかは公式ドキュメントをご覧ください→<data> | Android Developers

今回はこんな感じ.
scheme https://m.yahoo.co.jp/
host https://m.yahoo.co.jp/
path https://m.yahoo.co.jp/

techbooster.jpn.org

起動できるかはasbコマンドでテストできます.

$ adb shell am start -a android.intent.action.VIEW -d "https://m.yahoo.co.jp/" {package name}

実装状況をテストする  |  Firebase

Android Homeキーのタップを検知する

dispatchKeyEventでいけるかなと思っていましが,これはハードキー時代の栄光のようです.

Homeキーのタップを検知する方法には

1). onUserLeaveHint
2). BroadcastReceiver

の2つあります. 1). はIntentで他アプリに遷移するときも呼ばれるので注意が必要です.

1). onUserLeaveHint

    @Override
    protected void onUserLeaveHint() {
        Toast.makeText(getApplicationContext(), "ホームボタンが押されました", Toast.LENGTH_LONG).show();
    }

y-anz-m.blogspot.jp

2). BroadcastReciever

public class MainActivity extends AppCompatActivity {

    private HomeKeyReceiver mHomeKeyReceiver;

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

        // Homeキーを押された時の Reciever の登録
        mHomeKeyReceiver = new HomeKeyReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
        registerReceiver(mHomeKeyReceiver, filter);
    }

    private class HomeButtonReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent){
            Toast.makeText(getApplicationContext(), "ホームボタンが押されました", Toast.LENGTH_LONG).show();
            finish();
        }
    }

    @Override
    public void onPause(){
        super.onPause();
        unregisterReceiver(mHomeKeyReceiver);
    }

}

wada811.blogspot.com

this version of Android Studio is incompatible with the Gradle Plugin used. Try disabling Instant Run

エラー内容

git cloneしてきたアプリをrunしようとしたら,こんなメッセージが出てヒーってなったんですが,あっさり解決できたんで方法を記したいと思います.

this version of Android Studio is incompatible with the Gradle Plugin used. Try disabling Instant Run
(Android Studioのこのバージョンは使われているGradle Pluginと互換性がないよ.Instant Runを無効にしてみてね)

解決方法

Android Studioのバージョンによって設定方法が微妙に異なると思いますが,私の使っているAndroid Studio2.1系のやり方です.

①Instant Runの設定変更

Android Studio>Preferences>Build, Execution, Deployment>Instant Runで

  • Enable Instant Run to hot swap code/resource change on deploy (default enabled)←Instant Runを有効にするか
  • Show Instant Run status notifications←Instant Rundに関する通知を表示するか(こちらはチェックつけてもつけなくても変わらない気がする)

のチェックを外す.

②Clean Project

Build>Clean Projectをする.

③いつも通りrun!

参考

stackoverflow.com

App ShortcutsのStatic Shortcutsを実装してみる

App Shortcutsとは

Android 7.1(SDK 25)で追加されたショートカットを作成する機能です.

アプリを長押しすると,ショートカットの機能が出てきます.

吹き出しをタップすることで指定したintentを起動できます.

f:id:A_01:20170401200738p:plain

図1: ショートカット(左上)

ショートカットは先ほどの吹き出しを長押しすることで,ホーム画面にピン留めできます.

f:id:A_01:20170401192347p:plain

図2: ピン留めされたショートカット(左上)

App Shortcutsには二種類あります.

  • Static Shortcuts:xmlで定義する静的なショートカット
  • Dynamic Shortcuts:Shortcut Managerを利用する動的なショートカット

です.

実装

今回はStatic Shortcutsを作ります.

①AndroidManifest.xmlを定義する

ルートActivityにmeta-dataを追加します.@xml/shortcutはショートカットを設定したxmlです.コードは②に載せています.

(略)
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data android:name="android.app.shortcuts"
                android:resource="@xml/shortcuts" />
        </activity>
        <activity android:name=".ComposeActivity" />
    </application>
(略)

②shortcuts.xmlを定義する

<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
    <shortcut
        android:shortcutId="compose"
        android:enabled="true" <!-- falseにするとこのショートカットは無効化される -->
        android:icon="@drawable/icon"
        android:shortcutShortLabel="@string/compose_shortcut_short_label1" <!-- アプリ長押し時に出るラベル名 -->
        android:shortcutLongLabel="@string/compose_shortcut_long_label1" <!-- ピン留めしたショートカットのラベル名 -->
        android:shortcutDisabledMessage="@string/compose_disabled_message1"> <!-- 無効化されたショートカットをタップした時に出る文言 -->
        <intent
            android:action="android.intent.action.VIEW"
            android:targetPackage="com.example.a01.appshortcut"
            android:targetClass="com.example.a01.appshortcut.ComposeActivity" />
        <categories android:name="android.shortcut.conversation" />
    </shortcut>
</shortcuts>

参考

EventBusを使って簡単にCallbackを実現する

EventBusとは

あるClassでのイベントを検知して,別のClassで何かしたい時ありませんか?

Androidには簡単にそんなCallbackを実現する仕組みがあります.それがEventBusです.

例えば,ショッピングアプリの商品ページでお気に入りボタンが押された時,未ログインならログインページに飛ばし,ログイン完了後,再び商品ページに戻してお気に入り登録処理をしたい,なんて時に使えます(^^)

今回はgreenrobotのEventBusを使いますが,EventBusの提供元は一つではないです.

こちらにはsquere社のEventBusも紹介されています. qiita.com

実装

以下のgithubに載っていますが,EventBusは3stepで実現できちゃいます. github.com

今回は「ボタンがクリックされるとMessageEventというイベントが投げられ,MainActivityにToastを表示する」アプリを実装します.

イベントを投げるActivityも受けるActivityも同じなんで有難味がないかもしれませんが,簡略化のためそうします.

①イベント定義 (MessageEvent.java)

まず,ボタンがクリックされたことを通知するイベントClassを作ります.

MessageEvent.java

public class MessageEvent {
}

②イベントを監視する準備 (MainActivity.java)

次にイベントの監視を設定します.

通知を受け取りたいクラスのonStartにEventBus.getDefault().register(this);と書き

立つ鳥跡を濁さず,onStopではEventBus.getDefault().unregister(this);をして登録解除します.

③イベントを投げる (MainActivity.java)

最後に,イベントを発火したいタイミングでEventBus#getDefault#postします.

引数は発火したいイベントクラスです.

EventBus.getDefault().post(new MessageEvent());

MainActivity.java

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EventBus.getDefault().post(new MessageEvent());
            }
        });
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MessageEvent event) {
        Toast.makeText(this, "イベント発火", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
    }

    @Override
    public void onStop() {
        super.onStop();
        EventBus.getDefault().unregister(this);
    }
}

build.gradleはこちら

compile 'org.greenrobot:eventbus:3.0.0'

簡単で便利なのでよかったら使ってみてくださーいノシ