2014年12月28日日曜日

機械学習入門向け資料の作成

そろそろ機械学習を一緒にやっていけるメンバーを増やしたいので、入門者向けの資料を見やすく整えておこうと思った。

機械学習のアルゴリズムを実装できなくても良いので、意味がわかって使えるというレベルまで。

教科書は日本語でも出ている「実践機械学習システム O'REILLY」。日本語の第一版と英語の最新である第二版で内容に差があるので、英語の第二版の内容に変更してまとめを行う。

第2章までは日本語の第一版との差がごく小さいので、第3章から。

第3章 クラスタリング:関連のある文書を見つける
http://nbviewer.ipython.org/github/MasazI/MachineLearningForBegginers/blob/master/PythonML03_Cluster.ipynb

2014年11月29日土曜日

Androidのネットワーク通信に関するメモ

Androidのネットワーク通信


●パーミッション


□ネットワークの接続許可

<uses-permission android:name="android.permission.INTERNET" />

□ネットワークの接続状態を確認する

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

→ 接続不可能な場合のハンドリングに使用


●ネットワークの通信状態


ユースケース

・ネットワークに接続されていない場合は、ローカルにキャッシュされたデータを使用したい。
・ファイルサイズが大きい場合にwifiに接続時にのみダウンロードさせたい。

How


  • ContextからConnectivityManagerを取得する


ex)

public class MyActivity extends Activity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // do something...
        // Context 経由でインスタンスを取得する ←★ContextがあればConnectivityManagerを取得可能
        ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
        NetworkInfo info = cm.getActiveNetworkInfo();
        if (info.isConnected()) { ←★接続の確認
            Toast.makeText(this, info.getTypeName() + " connected", Toast.LENGTH_LONG).show();
        }
    }
}


●接続しているネットワークの種別


How


  • ConnectivityManagerからNetworkInfoを取得


ex)
ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo(); ←★ConnectivityManagerからNetworkInfoを取得
if (info.getType() == ConnectivityManager.TYPE_WIFI) {
    // wifi時のみ動作する処理を記述
}


●Http通信


Androidで用意されているライブラリ


  • HttpURLConnection:I/Oストリームを用いた通信の入出力処理を実装(Froyo以前ではコネクションプールの深刻なバグを含む)
  • Apache HttpClient:HttpURLConnectionより高いレイアの実装。抽象化された入出力を使うことができる(Froyo以前では使用が推奨される)



●非同期処理



  • ネットワーク通信の処理は、メインスレッド(UIスレッド)では実施しないこと。



●URLConnection:APIがシンプル、サイズが小さい



  1. URL#openConnection():接続の開始(レスポンスの型をHttpURLConnection型へキャスト)
  2. ヘッダーの設定
  3. HttpURLConnection#setDoOutput(true):bodyが存在する場合に明示する方法。接続の確率後に、getOutputStream()で取得したOutputStreamにbodyの内容を書き込む
  4. connect()で接続を確立する。setDoOutput(false)の場合、5にすすむ。setDoOutput(true)の場合、getOutputStreamで取得したOutputStreamに情報を書き込み、クローズするまでデータを送信する。closeを呼び出したら5にすすむ
  5. getResponseCode()でステータスコードを確認する
  6. getInputStream()でレスポンスを取得する



□ GET処理


ex)

URL url = new URL("http://mixi.jp");
HttpURLConnection connection = null;
try {
    connection = (HttpURLConnection) url.openConnection();
    connection.connect();
    InputStream is = connection.getInputStream();
    StringBuilder src = new StringBuilder();
    while (true) {
        byte[] line = new byte[1024];
        int size = is.read(line);
        if (size <= 0)
            break;
        src.append(new String(line, "euc-jp"));
    }
} catch (IOException e) {
    e.printStackTrace();
} finally{
    connection.disconnect();
}


□POST処理


ex)

URL url = new URL("http://mixi.jp");
HttpURLConnection connection = null;
try {
    connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod("POST");
    connection.setDoOutput(true);
    String postData = "hoge=fuga&piyo=test";
    OutputStream os = connection.getOutputStream();
    os.write(postData.getBytes());
    os.flush();
    os.close();
    InputStream is = connection.getInputStream();
    StringBuilder src = new StringBuilder();
    while (true) {
        byte[] line = new byte[1024];
        int size = is.read(line);
        if (size <= 0)
            break;
        src.append(new String(line, "euc-jp"));
    }
} catch (IOException e) {
    e.printStackTrace();
} finally{
    connection.disconnect();
}


●ApacheHttpClient:Androidに組み込まれたライブラリはバージョンが古い、必要なら新しいものを別途組み込む


□GET処理

ex)

HttpClient client = new DefaultHttpClient();
try {
    client.execute(new HttpGet("http://mixi.jp"),
            new ResponseHandler<String>() {
                public String handleResponse(HttpResponse response)
                        throws ClientProtocolException, IOException {
                    return EntityUtils.toString(response.getEntity());
                }
            });
} catch (IOException e) {
    e.printStackTrace();
}

Java 非同期処理の同期をとる CountDownLatch

Java 非同期処理の同期をとる


java.util.concurrentパッケージにはJavaの同期・非同期処理を実装する上で便利なクラスある。

その中でも簡単に使えるのが CountDownLatch である。

□CountDownLatch


他スレッドでの操作完了を待機する同期支援クラス

CountDownLatchはON/OFF機能をもったLatch(ドア、掛け金)である。

ex) 待ち合せたい非同期処理の数Nの場合

// インスタンス生成
final CountDownLatch latch = new CountDownLatch(N);
// 待ち合せ実行(メインスレッドで実行しないこと)
try {
    latch.await();
} catch (InterruptedException e) {
    e.printStackTrace();
}

CountDownLatchインスタンスを非同期処理のスレッドに渡す。

非同期処理側

ex)
latch.countDown();

【Notice】
latch.countDown()とlatch.await()を呼び出すスレッドとタイミングに注意すること。
latch.countDown()を呼び出すスレッドが起動したあと、latch.await()を呼び出す方が安全。

Google:
http://developer.android.com/intl/ja/reference/java/util/concurrent/CountDownLatch.html

Androidでも共通して使用できる。というか個人的にはAndroidでしか使っていない。

2014年11月27日木曜日

Androidの非同期処理についてメモ

Androidの非同期処理


●Service


バックグラウンドで動作するAndroidのコンポーネント


●Serviceの状態


呼び出し方によって2種類の状態をとる。

□開始 Context#startService(Intent)による呼び出し:


  • 呼び出し元とは異なるライフサイクルをもつ。呼び出し元のコンポーネントが終了しても生きていることがある。
  • 主に結果を呼び出し元に返さない場合に用いる。
  • Service自身でライフサイクルを終了する。


終了のトリガー

  • 自身で終了を宣言
  • 誰かが終了を命令


ex)

public class StartedService extends Service {
    public static final String TAG = StartedService.class.getSimpleName();
    /**
     * {@link Service} のライフサイクルの開始。
     */
    @Override
    public void onCreate() {
        super.onCreate();
        Log.v(TAG, "onCreate");
    }
    // バインド用のメソッド。今回は特に必要ないので null を返す。
    @Override
    public IBinder onBind(Intent intent) {
        Log.v(TAG, "onBind");
        return null;
    }
    /**
     * {@link Context#startService(Intent)} の呼び出しで呼ばれる。
     * このメソッドの処理は、{@link Context#startService(Intent)}を呼び出したスレッドと同じスレッドで実行されるので
     * メインスレッドで {@link Service} を起動した場合に、ここでネットワーク通信などスレッドをブロックする処理をしてしまうと
     * UI の処理がブロックされ AND となる。
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.v(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }
    /**
     * {@link Service} のライフサイクルの終了。
     */
    @Override
    public void onDestroy() {
        Log.v(TAG, "onDestroy");
        super.onDestroy();
    }
}


□バインド Context#bindService(Intent)による呼び出し:



  • Serviceをbindする。
  • 呼び出し元のコンポーネントとServiceの間にクラサバの関係ができる
  • 連携、リクエスト送信、レスポンス受信などのプロセス間通信が可能
  • bindしたコンポーネントのライフサイクルに合わせることが可能
  • 1つのServiceに複数のコンポーネントをbind可能。bindされた全てのコンポーネントが終了すると、Serviceのライフサイクルも終了する。


終了のトリガー

  • バインドしている呼び出し元がすべて破棄される
  • バインドしている呼び出し元がすべてバインドの解除を行う


ex)

public class BoundService extends Service {
    public static final String TAG = BoundService.class.getSimpleName();
    private final IBinder mBinder = new ServiceBinder();
    @Override
    public void onCreate() {
        super.onCreate();
        Log.v(TAG, "onCreate");
    }
    // 最初にバインドした時のコールバック
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    // 再度バインドした時のコールバック
    @Override
    public void onRebind(Intent intent) {
        super.onRebind(intent);
        Log.v(TAG, "onRebind");
    }
    // バインドを解除された時のコールバック
    @Override
    public boolean onUnbind(Intent intent) {
        Log.v(TAG, "onUnbind");
        return super.onUnbind(intent);
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.v(TAG, "onDestroy");
    }
    // サービスをバインドした後、バインドしたサービスのインスタンスそのものを得るためのインタフェース
    public class ServiceBinder extends Binder {
        public BoundService getService() {
            return BoundService.this;
        }
    }
}


●Serviceを構築する


□Service



  • バックグラウンドで動作(実動作はプロセスのメインスレッド)
  • Service内でブロック処理をする場合、自ら別スレッドの実行を記述する必要がある



□AndroidManufestの記述


ex)

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <service
        android:name="jp.mixi.sample.service.BoundService"/> ←★起動方法によらず、Manufestに記載
    <service
        android:name="jp.mixi.sample.service.StartedService"/> ←★起動方法によらず、Manufestに記載
</application>


●IntentService


  • Intentによる呼び出しで開始するService
  • Serviceの開始要求を1つずづ順に処理するワーカスレッド上で動作する特別なService
  • 一度に複数の処理を並列して行う必要が無い場合、IntentServiceを利用すると実装コストが小さい
  • AndroidManufestへの記述が必要


ex)

public class MyIntentService extends IntentService {
    public static final String TAG = MyIntentService.class.getSimpleName();
    public MyIntentService() {
        this(MyIntentService.class.getSimpleName());
    }
    public MyIntentService(String name) {
        super(name);
    }
    /**
     * {@link Service} のライフサイクルの開始。
     */
    @Override
    public void onCreate() {
        super.onCreate();
        Log.v(TAG, "onCreate");
    }
    /**
     * 親クラスで必要な処理がひと通り揃っているため、通常は Override の必要はない。
     */
    @Override
    public IBinder onBind(Intent intent) {
        Log.v(TAG, "onBind");
        return super.onBind(intent);
    }
    /**
     * 親クラスで必要な処理がひと通り揃っているため、通常は Override の必要はない。
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.v(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }
    /**
     * {@link Context#startService(Intent)} によって呼び出される。
     * ワーカスレッド上で実行されるため、ネットワーク通信等のスレッドをブロックする処理を直接記述しても問題ない。←★ワーカスレッドで実行される
     */
    @Override
    protected void onHandleIntent(Intent intent) {
        Log.v(TAG, "onHandleIntent");
    }
    /**
     * {@link Service} のライフサイクルの終了。
     * {@link IntentService} では、1 回の {@link Context#startService(Intent)} の呼び出しで
     * 1 つのライフサイクルが回るように作られている。
     */
    @Override
    public void onDestroy() {
        Log.v(TAG, "onDestroy");
        super.onDestroy();
    }
}


●Serviceの呼び出し


□開始サービス、IntentService



  • 開始サービスは、サービス自身で終了の命令を実行しない場合、サービスを止めるメソッドを呼び出す
  • IntentServiceは自身で終了するので必要ない


ex)

public class MyActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // do something ...
        Intent intent = new Intent(this, StartedService.class);
        startService(intent);
    }
    @Override
    protected void onStop() {
        Intent intent = new Intent(this, StartedService.class);
        stopService(intent);
        super.onStop();
    }
}


□バインドするサービス



  • 呼び出しの後、バインドしたサービスのインスタンスを受け取り、直接操作が可能
  • バインドするコンポーネントにしたがってライフサイクルを管理する。コンポーネントの終了時にサービスのバインドを解除する。


ex)

public class MyActivity extends Activity {
    private BoundService mBoundService;
    // BoundService を扱う時のインタフェース
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.v(TAG, "onServiceDisconnected");
            mBoundService = null;
        }
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.v(TAG, "onServiceConnected");
            mBoundService = ((BoundService.ServiceBinder) service).getService();
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // do something ...
        // サービスのバインド
        // バインド時の、バインド元とバインド先サービスの橋渡しをするための ServiceConnection インスタンスを一緒に渡す
        // Context.BIND_AUTO_CREATE によって、バインド時に自動で Service のライフサイクルが始まるようになる
        bindService(new Intent(MainActivity.this, BoundService.class), mConnection, Context.BIND_AUTO_CREATE);
    }
    @Override
    protected void onStop() {
        unbindService(mConnection);
        super.onStop();
    }
}


●Loader



  • 非同期で処理を行う新しいフレームワーク
  • ネットワークやファイルI/Oを介してデータを呼び出すためのフレームワークとして設計されている。
  • ActivityやFragmentのライフサイクルと、非同期処理を分類する目的で作られた。AsyncTaskの改良版


□AsyncTaskLoader

  • ネットワークや通信、ファイルI/Oでデータを読み出す場合、クラスを拡張して非同期処理を行う


ex)

// Support Package のものを利用する ←★2.xの対応が必要な場合
import android.support.v4.content.AsyncTaskLoader;
public class MyAsyncTaskLoader extends AsyncTaskLoader<String> {
    public static final String TAG = MyAsyncTaskLoader.class.getSimpleName();
    private String mCachedData;
    public MyAsyncTaskLoader(Context context) {
        super(context);
    }
    // 非同期処理の中身
    @Override
    public String loadInBackground() {
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            Log.e(TAG, "interrupted!: ", e);
        }
        return "hogehoge";
    }
    @Override
    public void deliverResult(String data) {
        // ローダがリセットされ、そのローダのライフサイクルが終了となる場合
        if (isReset()) {
            // キャッシュデータがある場合は、キャッシュを削除して、メモリから破棄可能にする
            if (mCachedData != null) {
                mCachedData = null;
            }
            return;
        }
        // 得られたデータをキャッシュする
        mCachedData = data;
        // ローダが開始されている場合、親にデータが得られたことを通知する
        if (isStarted()) {
            super.deliverResult(data);
        }
    }
    @Override
    protected void onStartLoading() {
        // キャッシュがある場合はそちらを返す
        if (mCachedData != null) {
            deliverResult(mCachedData);
            return;
        }
        // データソースに変更があったり、キャッシュデータがない場合は loadInBackground() に行くようにする
        if (takeContentChanged() || mCachedData == null) {
            forceLoad();
        }
    }
    // ローダの非同期処理がストップする時のコールバック
    @Override
    protected void onStopLoading() {
        cancelLoad();
        super.onStopLoading();
    }
    // ローダがリセットされる時のコールバック
    @Override
    protected void onReset() {
        onStopLoading();
        super.onReset();
    }
}


●Loaderの呼び出しとコールバック



  • LoaderManagerとLoaderCallbacksを用いる


ex)

public class MainActivity extends FragmentActivity implements LoaderCallbacks<String> {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // ローダの管理をするオブジェクト
        LoaderManager manager = getSupportLoaderManager();
        Bundle argsForLoader = new Bundle();
        // ローダを初期化して非同期処理を開始する
        manager.initLoader(0, argsForLoader, MainActivity.this);
    }
    // id に対応した Loader のインスタンスを作って返す
    // args は Loader に渡したい引数を Bundle に詰めたもの
    @Override
    public Loader<String> onCreateLoader(int id, Bundle args) {
        switch (id) {
            case 0:
                return new MyAsyncTaskLoader(this);
            default:
                return null;
        }
    }
    // 結果を受け取るコールバック
    // メインスレッドで動作する
    @Override
    public void onLoadFinished(Loader<String> loader, String result) {
        Toast.makeText(this, result, Toast.LENGTH_LONG).show();
    }
    // ローダがリセットされる時のコールバック
    @Override
    public void onLoaderReset(Loader<String> loader) {}
}


●AsyncTask



  • 非同期処理のためのクラス
  • 非同期に実行したい処理、処理前、処理後のメインスレッド上での処理を記述できる
  • 内部でスレッドプールを保持(バージョンによって異なる)
  • 最大プール数は128
  • インスタンスの使い回しはできない



/**
 * 非同期処理を実行するためのネストクラス。
 *
 * ジェネリクスの仕組みを用いて、非同期処理に渡す引数の型、進捗を監視するコールバック用の型、非同期処理の結果を表す型を指定する。
 *
 * Activity や Fragment のライフサイクルに合わせて、自分で AsyncTask をコントロールする必要があり、これを行わないと
 * 特に {@link AsyncTask#onPostExecute()} で、参照するオブジェクトが既にメモリから破棄されていて NullPointerException となることが起こりえる。
 */
public class MyAsyncTask extends AsyncTask<Void, Void, Void> { ←★ジェネリクスによる引数の型などの宣言
    private Context mApplicationContext;
    public MyAsyncTask(Context applicationContext) {
        super();
        mApplicationContext = applicationContext;
    }
    /**
     * ★非同期処理を実行する前に UI スレッドで実行する処理を書く
     */
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        Toast.makeText(mContext, "onPreExecute", Toast.LENGTH_SHORT).show();
    }
    /**
     * ★非同期処理の進捗を受け取るコールバック。
     */
    @Override
    protected void onProgressUpdate(Void... values) {
        super.onProgressUpdate(values);
        Toast.makeText(mContext, "onProgressUpdate", Toast.LENGTH_SHORT).show();
    }
    /**
     * ★非同期処理の本体
     * 引数は非同期処理内容に渡すためのパラメータ配列。
     */
    @Override
    protected Void doInBackground(Void... params) {
        // 2 秒おきに進捗を通知する
        try {
            publishProgress();
            Thread.sleep(2000L);
            publishProgress();
            Thread.sleep(2000L);
            publishProgress();
            Thread.sleep(2000L);
            publishProgress();
            Thread.sleep(2000L);
            publishProgress();
            Thread.sleep(2000L);
            publishProgress();
        } catch (InterruptedException e) {
            Log.e(MyAsyncTask.class.getSimpleName(), "thread interrupted: ", e);
        }
        return null;
    }
    /**
     * ★非同期処理の実行後に、UI スレッドで実行する処理。
     * 引数は {@link AsyncTask#execute(Object...)} の返り値。
     */
    @Override
    protected void onPostExecute(Void result) {
        super.onPostExecute(result);
        Toast.makeText(mContext, "onPostExecute", Toast.LENGTH_SHORT).show();
    }
}

□呼び出し元のコンポーネント


public class MyActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // do something...
        // 非同期処理の開始
        new MyAsyncTask(getApplicationContext()).execute(); ←★executeで実行
    }
}


※参考:mixi Android
https://github.com/mixi-inc/AndroidTraining/wiki/2.08.-%E9%9D%9E%E5%90%8C%E6%9C%9F%E5%87%A6%E7%90%86

2014年11月17日月曜日

Android in-App Billing 注意点

Android の in-App Billing については近いうちまとめようと思うが、検証中にいくつかハマったことをメモ

1. AndroidManifest.xmlの記述

基本的なことなのだが、何度もプロジェクトを作っていると忘れることがあった。疑わないので質がわるい。

<uses-permission android:name="com.android.vending.BILLING" />


2. アプリケーションのバージョン

アプリケーションがGoogle Developer Console でアップロードしたものをより低いと、課金対象にならない。注意が必要。

上記の場合、「このバージョンのアプリではアプリ内課金が許可されません」となる。


3. アプリケーションのパッケージ構成

Google Developer Consoleに登録したパッケージの配下に「プロダクトID」がひもづく。

そのため、アプリケーションのパッケージ構造が違う場合、プロダクトIDが見つからないエラーがでる。

2014年11月16日日曜日

Android コレクション、直列化、永続化について

Android コレクション、直列化、永続化


●コレクション


Collectionインターフェースを実装したコレクションクラスのことで、
データセットの管理に重要。


●Bundle

コレクション実装の1つ。
Bundleはキー(String)、バリューの組を保持する。
バリューには、Androidに最適化されたオブジェクト郡を使うことができる。

IntentのExtrasは、内部でBundelオブジェクトを管理している。

□マッピングの追加・取得
・put~():追加
・get~():取得
※~は型が入る

ex) Intentの例(呼び出し元のActivity)

public void send(View v, int value) {
    Intent intent = new Intent(this, ResultActivity.class);
    Bundle bundle = new Bundle();
    bundle.putString("data","value"); ←★put(型はString)
    intent.putExtra("bundlePrams", parcelableData);
    startActivity(intent);
}


ex) Intentの例(呼び出し先のActivity)

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Bundle extras = getIntent().getExtras();←★get(型はString)
    Bundle bundlePrams = extras.getParcelable("bundlePrams");
    Toast.makeText(this, "data:" + bundlePrams.getString("data"), Toast.LENGTH_LONG).show();
}


●SparseArray


key(int)としたHashMapのようなもの。keyをint型に固定し、HashMapより高速に動作する。
Sparseという名の通り、keyに使用するint型が連続でなく(Sparse)とも使用できる。

AndroidのR.idで管理されるidはint型であるため、SparseArrayを用いることができるケースが
多くなります。(Android Listが警告を出してくれる。)

SparseArrayはvalueの型によってはvalueも最適化したSparseBooleanArray、SparseIntArrayを
使うことができる。

大規模である場合は、LongSparseArrayを使用することもできる。

ex)

SparseArray<String> array = new SparseArray<String>();
array.add(R.id.TextView1, "hoge");
array.add(R.id.TextView2, "fuga");
array.add(R.id.TextView3, "foo");
array.add(R.id.TextView4, "bar");


●直列化


Androidでは2種類の直列化フレームワークを利用可能。
Javaで提供されるものと、Androidで提供されるものである。

□Serializable(シリアライズ)



  • Javaフレームワーク
  • オブジェクトの保存と復旧の方法を決めたもの
  • 永続化


利用するI/Oの仕組み→データ化・データ復旧を保証する

  • ObjectInputStream
  • ObjectOutputStream


マーカーインターフェースである:メソッドの宣言のないインターフェース
クラスのフィールドがもつデータを扱う。

staticやtransientな変数、メソッドは直列化しない。

ex)

public class MyObject implements Serializable {
    public static final long serialVersionUID = -4324129709521L;
    private String mName;
}

【Notice】クラスのフィールドの互換性を管理する必要がある
※参考
http://d.hatena.ne.jp/asakichy/20120216/1329343931
http://d.hatena.ne.jp/Kazuhira/20130929/1380443535
→柔軟性、品質、安全性を熟考する必要がある

□Parcel、Parcelable



  • Androidフレームワーク
  • 永続化ではない
  • Serializableより軽量
  • プロセス間通信でハイパフォーマンスを得るために設計されたインターフェイス
  • IntentやBundleはParcelableを実装している


Parcelableのインターフェースに書き込み処理と復元処理を定義することにより、
メッセージングで型を限定されずにオブジェクトを渡すことが可能。


  • Parcelableの実装クラスの中にParcelableではないクラスを含めることが可能
  • 異なるアプリケーションへのメッセージングでも同一オブジェクトの復元を保証


Parcelableの実装手順


  1. Parcelable#writeToParcel()メソッド内でParcelに対してデータを書き込む
  2. CREATORという定数を定義し、Parcelable.Creator<T>を実装
  3. Parcelable.Creator#createFromParcel()でParcelからオブジェクトを生成する処理を実装


【Notive】Parcelから読み込む順序は、Parcelable#writeToParcel()で書き込んだ順序と同じにする

ex) ParcelableをIntentで受け渡しする


・受け渡すParcelableの実装

public class MyParcelable implements Parcelable { ←★Parcelbleをimplements
    public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() { ←★CREATORの実装
        public MyParcelable createFromParcel(Parcel in) { ←★Parcelからオブジェクトを生成する処理
            return new MyParcelable(in);
        }
        public MyParcelable[] newArray(int size) {
            return new MyParcelable[size];
        }
    };
    private int mData;
    public MyParcelable() {}
    private MyParcelable(Parcel in) {
        mData = in.readInt();
    }
    @Override
    public int describeContents() {
        return 0;
    }
    @Override
    public void writeToParcel(Parcel out, int flags) { ←★writeToParcelでParcelにデータを書き込む
        out.writeInt(mData);
    }
    public void setData(int data) {
        mData = data;
    }
    public int getData(){
        return mData;
    }
}

・Parcelの送信元(呼び出し元Activity)

public void send(View v, int value) {
        Intent intent = new Intent(this, ResultActivity.class);
        MyParcelable parcelableData = new MyParcelable();
        parcelableData.setData(value);
        intent.putExtra("parcelableData", parcelableData);
        startActivity(intent);
    }

・Parcelの受信先(呼び出し先Activity)

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Bundle extras = getIntent().getExtras();
        MyParcelable parcelData = extras.getParcelable("parcelableData");
        Toast.makeText(this, "data:" + parcelData.getData(), Toast.LENGTH_LONG).show();
    }


□JSONObjectとJSONArray



  • JSONはオブジェクトのシリアライズ・デシリアライズ形式の1つ
  • JSONを用いたフレームワーク
  • JSONRPCなど主にネットワーウ経由でのデータの転送に利用する


JSONObject:JSONにおけるオブジェクトに対応
JSONArray:JSONにおける配列に対応

JSONObjectからの値の取得方法は2種類

1. JSONObject#get~():~は型


  • JSON文字列上の型と異なる型を取得するメソッドを用いた場合、適宜型が変換される(安全ではない)
  • 存在しないkeyの場合、JSONExceptionがスローされる


2. JSONObject#opt~():~は型


  • 存在しないkeyを指定した場合、フォールバックの値が使用される。


【Notive】値がnullかどうか判定するには、JavaのNULLを使用できない。
JSONObject#isNull() を使うこと。

□Gsonフレームワーク



  • JSON文字列からJavaのオブジェクトへマッピングするフレームワーク


【Notice】端末依存に注意
http://alpha.mixi.co.jp/entry/2013/11572/


●永続化


Androidで用意された永続化手法

□ここで扱うもの


  • Shared Preferences
  • Internal Storage
  • External Storage


□別で扱うもの


  • SQLite Databases(データベース)
  • Network Connection(ネットワーク通信)



●SharedPreferences



  • Android標準で用意されている永続化の方法の1つ
  • keyとvalueの組み合わせでプリミティブ型やString型のデータを永続化する
  • ファイルの読み書き、状態管理・監視を請け負うので、自前でI/Oを準備するより簡単


□ファイルの実体



  • ファイルはアプリのもつ内部ストレージに保存される
  • アプリのアンインストール時は、すべてのデータが削除される
  • アプリが再インストールされてもなおデータを永続化したいユースケースには適用できない
  •  →そのような場合は適宜サーバにデータを保存するようにする


【Notive】保存場所が違うことに起因する問題
http://alpha.mixi.co.jp/entry/2013/11572/

→ Android-Device-Compatibilityライブラリの使用の検討
https://github.com/mixi-inc/Android-Device-Compatibility

□ファイルへのアクセス権


  • モード:SharedPreferencesのファイル作成時に設定するアクセス権限。アプリの領域内のアクセス権をファイル単位でコントロールする。
  • 他のアプリからのアクセスを許可することは推奨されない。
  •  →アプリ間連携のための仕組みを使用する。
  • 複数のプロセスをもつアプリケーションで同じファイルを参照する場合、モード:MODE_MULTI_PROCESSが存在するが、API Level 11以降となる。


□SharedPreferencesの使用


・インスタンスの取得

Context#getSharedPreferences(String, int)
:第1引数(ファイルの名前)
:第2引数(モード、デフォルトはMODE_PRIVATE)


□データの保存

SharedPreferences.Editorを介して行う


  1.  SharedPreferences.Editorオブジェクトの取得:SharedPreferences#edit()
  2.  値の設定:SharedPreferences.Editor#putString(String,String)
  3.  保存:SharedPreferences.Editor#commit()、SharedPreferences.Editor#apply()


【Notice】
SharedPreferences.Editor#apply()は保存の完了を待たない、かつAPI Level9以降となる。

SharedPreferencesを扱っていて、保存の成功・失敗を意識しなくていい場合は、
非同期処理の中でSharedPreferences.Editor#commit()を呼ぶ。


  • 値の削除:SharedPreferences.Editor#remove(String)
  • すべての値の削除:SharedPreferences.Editor#clear()


【Notive】
値の削除も、SharedPreferences.Editor#commit()またはapply()を呼び出すまで完了しない。

ex)

// データの保存
public boolean savePerson(String name, int age) {
    SharedPreferences sp = getSharedPreferences("person", MODE_PRIVATE);
    Editor editor = sp.edit();
    editor.putString("name", name);
    editor.putInt("age", age);
    return editor.commit();
}

□データの取得



  • SharedPreferences#get~():~は型


【Notive】
保存時の型と取得時の型が一致していること

ex)

// データの取得
private String mName;
private int mAge;
public void readPerson() {
    SharedPreferences sp = getContext().getSharedPreferences("person", MODE_PRIVATE);
    mName = sp.getString("name", "no name"); ←★String型を取得(第2引数はフォールバック)
    mAge = sp.getInt("age", 0);
}

□データ変更の監視

保存されている値に変更が合った場合、UIに反映する処理などに使用する。


  • SharedPreferences.OnSharedPreferenceChangeListener:データ変更監視のリスナーオブジェクト
  • SharedPreferences#registerOnSharedPreferenceChangeListenerでリスナーオブジェクトを登録


【Notive】
このリスナーオブジェクトのライフサイクル管理は実装で行う必要がある。
すなわち、SharedPreferences#unregisterOnSharedPreferenceChangeListener()をContextのライフサイクルの終わりに行う。

ex)

public class MainActivity extends Activity implements OnSharedPreferenceChangeListener {
    private SharedPreferences mSharedPreferences;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mSharedPreferences = getSharedPreferences("sample", MODE_PRIVATE);
        mSharedPreferences.registerOnSharedPreferenceChangeListener(this);
    }
    protected void onDestroy(){
        mSharedPreferences.unregisterOnSharedPreferenceChangeListener(this); ←★リスナーの解除
        super.onDestroy();
    }
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        String value = sharedPreferences.getString(key, null);
        if (value != null) {
            TextView tv = (TextView) findViewById(R.id.PreferencesValue);
            tv.setText(value);
        }
    }
}


●Internal Storage


  • 内部ストレージのこと。
  • モードによってアクセス制御が可能
  • 容量の制限があることが多いので大量データの保存はできない


□Contextが提供する内部ストレージ利用のインターフェイス



・ファイルを保存



  1.  FileOutputStreamの取得:Context#openFileOutput()
  2.  バイト列をストリームに格納:FileOutputStream#write()
  3.  ストリームを閉じる:FileOutputStream#close()


【Notice】
FileOutputStream#close()処理はfinallyで行うこと

ex)

String FILENAME = "hello_file";
String string = "hello world!";
FileOutputStream fos = null;
try {
  openFileOutput(FILENAME, MODE_PRIVATE);
  fos.write(string.getBytes());
} catch (IOException e) {
  // 例外処理
} finally {
  try {
    if (fos != null) {
      fos.close();
    }
  } catch (IOException e) {
  }
}

・ファイルの内容を取得


  1.  FileInputStreamを取得:Context#openFileInput()
  2.  ファイル内容を読み出し:FileInputStream#read()
  3.  ストリームを閉じる:FileIntputStream#close() 【Notice】finallyで呼び出すこと



●CacheDirectory



  • 一時ファイルとしてキャッシュする特別なディレクトリ
  • 内蔵ディレクトリにキャッシュ用ディレクトリが作成される



●External Storage



  • 外部ストレージ
  • SDカード
  • 端末によっては、内部とは別の内蔵メモリが存在する。(ハイエンド端末に多い。Galaxy、Nexus)
  • アクセス制御できない
  • 全てのアプリケーションからアクセスが可能
  • ルートディレクトリの取得:getExternalFilesDir()
  • 取得するディレクトリのtype

   DIRECTORY_MUSIC
   DIRECTORY_PODCASTS
   DIRECTORY_RINGTONES
   DIRECTORY_ALARMS
   DIRECTORY_NOTIFICATIONS
   DIRECTORY_PICTURES
   DIRECTORY_MOVIES

2014年11月15日土曜日

Android SDK Tools をバージョンアップして問題がおこったときに試すこと

Android SDK Tools をバージョンアップして問題がおこったときに試すこと


Andrid SDK Toolsをバージョンアップして問題が起こるケースは珍しくない。

support libraryのgen配下が生成されないなどの障害が発生する。

そのたびに解決策を考えるのだが、まずは以下のような対策を行うのでメモ。


1.ビルドターゲットバージョンのAndroid SDK Build-toolsがインストールされているか


Android SDK Build-toolsにもたくさんのバージョンが存在する。

project.propertiesで指定しているビルドターゲットに対応したSDK Build-toolsをいれておこう。



2.最新のJDKに入れ替える


新しいAndroid SDKが、最新のJDKに依存しているケースもある。最新のJDKに変更しよう。


3.project.propertiesの見直し


そもそもターゲットが間違っていないだろうか。最新のBuild-toolsを導入した場合、targetも最新に切り替えておこう。


4.EclipseのUpdateを試す

新しいアップデートが促されていないかどうか確認しよう。

Eclipse → 「Help」 → 「Check for Update」


5.Eclipseの再起動


変更した項目によっては、反映に再起動を必要とする場合がある。


6.SDK Managerから以下をインストール、アップデートする


Android Support Library

Google Play services
Goole Repository


7.Android L のライブラリを認識できない場合


SDK Managerを使って、L(API 21)のアンインストール、インストールを行う




これらで解決しない場合、ADTを丸ごと入れ替えるなど、力技を使う。

2014年11月14日金曜日

Android ListViewとViewPager

ListViewとViewPager


●2つの特殊なView


□ListView

縦にスクロールするView。ListView自身には、一覧の中身を管理する機能が無い。
Adapterを用いて、データソースの管理とデータのViewへのBindを行い、スクロール位置に合わせたデータをAdapterから取得する仕組み。この仕組みは、UI作成の際の汎用的な手法である。

リストデータは、Listインターふぇおーすを実装したデータソースや、DBへ問い合わせ結果のデータソースも可能。

□ListAdapter

データをbindして表示する際に、ListAdapterインターフェースを実装したAdapterを使用する。
Adapterにはいくつかの種類が提供されている。


  • BaseAdapter:共通基底クラス。ListViewとSpinnerで使用可能。ListViewでは、ListAdapter用のinterfaceを実装する。
  • ArrayAdapter:配列やリストデータソースをBindする。
  • CursorAdapter:データベースへの問い合わせ結果をBindする。
  • SimpleCursorAdapter:データベースへ問い合わせた結果をViewにBindするための簡易Adapter



●ListViewの表示

以下の2つだけで、ListViewへの表示ができる。


  • ListViewをレイアウトxmlに配置
  • ListViewにAdapterをセットする


レイアウトのex)

<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"
    tools:context=".MainActivity" >
    <!-- ListViewを配置 -->
    <ListView ←★これ
        android:id="@+id/ListView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" /
</RelativeLayout>

□レイアウトxmlを使うActivity側では、以下の様にAdapterを用意してBindする。


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main); ←★ListViewを定義したレイアウトxml
    mActivity = this;
    // ListViewに表示するデータを作成する ←★ListViewに表示するデータ
    ArrayList<String> list = new ArrayList<String>();
    for (int i = 0; i < 20; i++) {
        list.add("hoge" + i);
    }
    ListView listView = (ListView) findViewById(R.id.ListView);
    // android.R.layout.simple_list_item_1はAndroidで既に定義されているリストアイテムのレイアウトです
    ArrayAdapter<String> adapter = new ArrayAdapter<String>(mActivity,
            android.R.layout.simple_list_item_1, list); ←★Adapterの生成
    listView.setAdapter(adapter); ←★Bind
   
   
    // タップした時の動作を定義する
    listView.setOnItemClickListener(new OnItemClickListener() { ←★タップのリスナーを設定
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            // Adapterからタップした位置のデータを取得する
            String str = (String) parent.getItemAtPosition(position); ←★タップしたPositionなどの情報を取得可能
            Toast.makeText(mActivity, str, Toast.LENGTH_SHORT).show();
        }
    });
}


●カスタマイズしたリストアイテムの表示


以下が必要な作業。


  • リストアイテムのレイアウトxmlに配置
  • 独自アダプタの作成


□リストアイテムのレイアウトxml


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" ←★ここではRelative(横)レイアウトを使う
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="4dp" >
    <ImageView ←★各行に表示する画像
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:src="@drawable/ic_launcher" />
    <TextView ←★各行に表示するテキスト
        android:id="@+id/TitleText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_toRightOf="@+id/imageView1" ←★imageViw1の右に配置
        android:textSize="18sp" />
    <TextView ←★
        android:id="@+id/SubTitleText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/TitleText"←★TitleTextと左端をあわせる
        android:layout_alignParentRight="true"
        android:layout_below="@+id/TitleText" /> ←★TitleTextの下に配置
</RelativeLayout>

□独自Adapterの作成


public class CustomListItemAdapter extends ArrayAdapter<String> { ←★ArrayAdapterを継承
    private LayoutInflater mLayoutInflater;
    public CustomListItemAdapter(Context context, List<String> objects) {
        // 第2引数はtextViewResourceIdとされていますが、カスタムリストアイテムを使用する場合は特に意識する必要のない引数です
        super(context, 0, objects); ←★表示するデータobjectsの設定
        // レイアウト生成に使用するインフレーター
        mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view = null;
        // ListViewに表示する分のレイアウトが生成されていない場合レイアウトを作成する
        if (convertView == null) {
            // レイアウトファイルからViewを生成する
            view = mLayoutInflater.inflate(R.layout.custom_list_item, parent, false);
        } else {
            // レイアウトが存在する場合は再利用する
            view = convertView;
        }
        // リストアイテムに対応するデータを取得する
        String item = getItem(position);
        // 各Viewに表示する情報を設定
        TextView text1 = (TextView) view.findViewById(R.id.TitleText);
        text1.setText("Title:" + item);
        TextView text2 = (TextView) view.findViewById(R.id.SubTitleText);
        text2.setText("SubTitle:" + item);
        return view;
    }
}


●パフォーマンス view holder デザインパターン


findViewByIdメソッドを頻繁に呼び出すとパフォーマンスが低下する。その場合、view holderデザインパターンを利用する。

□Adapter パターン

【参考】少し古い資料ですが、丁寧でわかりやすいのでおすすめ。
http://www.slideshare.net/yanzm/adapter-listview-expandalbelistview
Adapterもデザインパターンの1つである。
メリット
・既存のクラスに修正不要
・インターフェースを変更可能
・ListViewのadapterは委譲のパターン

□view holder パターン

【参考】使い方などの丁寧な説明。
http://y-anz-m.blogspot.jp/2010_08_01_archive.html

findViewByIdでviewを探す処理を削減することで、パフォーマンスを改善することができる。


●ViewPager

横にフリックしてViewを切り替えるためのView

複数のFragmentを切り替える用途にも使用可能
SupportPackageで提供される機能

□ViewPagerの管理

Adapterを使用する。

以下が必要な作業。


  • ①ViewPagerをレイアウトxmlに配置
  • ②独自PagerAdapterを実装
  • ③ViewPagerにAdapterをセットする


①レイアウトxml

レイアウトxmlの例)

<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"
    tools:context=".MainActivity" >
    <android.support.v4.view.ViewPager ←★SupportPackageで提供されるViewPager
        android:id="@+id/Pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>

②PaerAdapterの実装

PagerAdapterにimplementsが必要なメソッドは以下のとおり。

Object instatiateItem(ViewGroup container, int position); ← ★ページの生成
void destroyItem(ViewGroup container, int position, Object object); ← ★ページの削除
int getCount(); ← ★ページ数を返却
boolean isViewFromObject(View view, Object object); ← ★instantiateItemで返却されたキーObjectとページが関連づいているかどうかを確認。


ex)
class SamplePagerAdapter extends PagerAdapter {
private static final int PAGE_COUNT = 5; ← ページ数
private Context mContext;
public SamplePagerAdapter(Context context) {
    super();
    mContext = context;
}

@Override
public Object instantiateItem(ViewGroup container, int position) { ←★ページの生成
    // TextViewを生成
    TextView textView = new TextView(mContext);
    textView.setText("Position:" + position);
    //コンテナに追加
    container.addView(textView);
    // ここではTextView自体をキーとして返しています←★Adapter内のuniqueな識別子として利用
    return textView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) { ←★ページ削除
    // viewの削除
    // objectはinstantiateItemで返却したオブジェクトです
    container.removeView((View) object);
}
@Override
public int getCount() { ← ★ページ数
    // ページ数を返します。今回は固定値としています。
    return PAGE_COUNT;
}
@Override
public boolean isViewFromObject(View view, Object object) { ←★instantiateItemのレスうポンスObjectとページが等しいかどうか?
    // キーが正しいかを判定
    return view == (TextView) object;
}

③ViewPagerにAdapterを設定する


MainActivityでViewPagerを生成し、Adapterを設定する。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ViewPager pager = (ViewPager) findViewById(R.id.Pager);
    pager.setAdapter(new SamplePagerAdapter(this));
}


●FragmentPagerAdapter

Fragmentの切り替えで使うケースが多い。全てのページをメモリで保持する。

メモリで保持しきれない数のページがある場合、FragmentStatePagerAdapter を使用する。

以下が必要な作業

  • ①ViewPagerをレイアウトxmlに配置
  • ②Fragmentを作成
  • ③独自FratmentPagerAdapterを実装
  • ④ViewPagerにAdapterをセットする



①Fratmentのレイアウト(fragment_main.xml)


ex)

<FrameLayout 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:background="@android:color/black" >
    <TextView
        android:id="@+id/TextView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="4dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:textSize="90sp"
        android:textStyle="bold" />
</FrameLayout>


②SampleFragment(fragmentクラス)


ex)

public class SampleFragment extends Fragment {
    public static SampleFragment newInstance(int position) { ←★ページのpositionをいれておく
        SampleFragment sampleFragment = new SampleFragment();
        Bundle bundle = new Bundle();
        bundle.putInt("position", position);
        sampleFragment.setArguments(bundle);
        return sampleFragment;
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Bundle bundle = getArguments();
        int position = 0;
        if (bundle != null) {
            position = bundle.getInt("position");
        }
        View view = inflater.inflate(R.layout.fragment_main, container, false); ←★Viewにfragmentのレイアウトを設定
        TextView text = (TextView) view.findViewById(R.id.TextView1);
        text.setText(String.valueOf(position));
        return view;
    }
}


③独自FragmentPagerAdapter SampleFragmentPagerAdapter


implementsが必要なデータ

  • Fragment getItem(int position); ←★Fragmentの生成
  • int getCount(); ←★ページ数を返す


ex)

public class SampleFragmentPagerAdapter extends FragmentPagerAdapter {
    private static final int PAGE_COUNT = 5; ←★ページ数を返す
    public SampleFragmentPagerAdapter(FragmentManager fm) {←★ FragmentPagerAdapterを受け取る
        super(fm);
    }
    @Override
    public Fragment getItem(int position) { ←★Fragmentを新しく生成する
        return SampleFragment.newInstance(position);
    }
    @Override
    public int getCount() { ←★ページ数を返す
        return PAGE_COUNT;
    }
}


④ViewPagerにAdapterをセットする


FragmentManagerを独自のFragmentPagerAdapterに渡す。
FragmentPagerAdapterをViewPagerに設定する。


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ViewPager pager = (ViewPager) findViewById(R.id.Pager);
    FragmentManager fm = getSupportFragmentManager();
    SampleFragmentPagerAdapter sampleFragmentPagerAdapter = new SampleFragmentPagerAdapter(fm);
    pager.setAdapter(sampleFragmentPagerAdapter);
}

2014年11月11日火曜日

Android ActionBarとインタラクション

ActionBarとインタラクション制御


●ActionBarの基本


ActionBar:アプリにある複数の画面で恒久的に表示される、主にナビゲーションと適切なアクションの提示のために利用される重要なUIコンポーネント。

●4つのコンポーネント

□AppIcon

アプリのアイコン。ホームに戻る、階層を1つ戻るなどの役割を持たせることも可能。


□ViewControl

異なるViewに様々なデータを表示するような場合に、ユーザがすばやくViewを切り替えるためのもの。


□ActionItem Buttons(Action Buttons)

配置されるボタン類は、アプリやその画面のなかで最も重要なアクションを促す。シンプルな抽象化されたアイコンで表現することが多い。個数が多い場合は分割して、画面下部に表示させることも可能。


□Action Overflow

画面の大きさの影響で収まらなかったり、意図的にAction Overflowに入る様に設定したActionButtonsがここにまとめられる。
優先度が低い位置づけ。


●重要な要素1 NavigationMode

ActionBarの3つのナビゲーションモード。1つのActivityでは、3つのうちのいずれかを選択する。

□Standard Navigation

デフォルトのナビゲーションモード

□ListNavigation

ドロップダウンのリストから項目を1つ選択するタイプ。右下隅に▲が表示される。

ex)

getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);

□TabNavigation

複数のタブから1つを選択してコンテンツを切り替えるタイプのナビゲーションモード。

タブの位置は画面の大きさによって決定される。

・横幅の狭い画面:アクションバーの直下
・横幅の広い画面:タイトル部分の右端に統合
→ 画面の縦横切り替えでもアクションバーのレイアウトが入れ替わる。

ex)

getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);


●重要な要素2 ActionItem


OptionMenu(メニューキーを押して表示するメニュー)の代替で、アクションバーに含められるメニュー。

□android:showAsAction:アクションバーへの出し方を決定する属性


・always:常に表示します。もっとも重要度が高い。
・ifRoom:ほかのActionBarのコンポーネントが占める領域を見て、空があれば表示する。
・never:常にActionBar上に表示せず、その他(右端の3つの点)のメニューの中にまとめられる。もっとも重要度が低い。

always、ifRoom、neverのいずれか1つを選択

・withText:横幅の広い画面において、アイコンだけでなく、文言も一緒に表示するオプション。

端末の画面サイズに応じて、ActionBarが自動でAction Itemの表示をハンドリングする。


・タブレット型のような大きなディスプレイでは、アイコンとラベルを同時に表示。
・モバイル向けの端末では、横幅によってアイコンのみの場合と、ラベルとアイコンの同時表示がある。
・横画面が480dp以上ない場合、常にどちらかだけの表示。


●重要な要素3 App Icon Navigation


標準でアクションバーの左端に表示されるアプリのアイコンを、ナビゲーションのためのUIとして機能させる。

Androidの流儀としての使い方として、下記の2つの役割が想定されている。

・ホームボタンの役割
・階層構造を上に戻る役割(左向きの矢印が一緒に表示される。)

●重要な要素4 Split ActionBar


ActionItemを画面上部のActionBarから切り離し、画面下部に1列に表示するモード。

ex) <activity>の属性して以下を設定
android:uiOptionsにsplitActionBarWhenNarrow



●ActionBarSherlock

Android2.x でも ActionBarの導入を行うときに使用する。


●ActionBarの見た目を変える


StyleResourceとして、Drawablを利用してカスタマイズ可能。

***

●インタラクション制御

□ActivityやFragmentのコールバックについて


扱うメニューによって、コールバックメソッドが異なる。

□OptionsMenu

・onPrepareOptionsMenu:表示前に、メニュー要素の状態を管理するために呼び出す
・onCreateOptionsMenu:構成を初期化するため、ActivityやFragmentのライフサイクルの中で1度だけ呼ば荒れう
・onOptionsItemSelected:OptionMenuの選択状況を見て、適切なアクションを起こすために呼び出される

ex)

public class MainActivity extends Activity {
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        // ここで、状態に応じてメニューの有効・無効を切り替えたりなどの処理をする
        return super.onPrepareOptionsMenu(menu);
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // ここで、この Activity で利用するメニューのリソースを読み込む
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // 選択されたメニューに対応するイベント処理をここで実行する
        return super.onOptionsItemSelected(item);
    }
}

□ContextMenu

Viewの長押しで表示されるメニューを構成する。

・onCreateContextMenu:ContextMenuの構成を初期化する
・onContextItemSelected:ContextMenuの選択状況を見て、適切なアクションを起こすために呼び出される


ex)

public class MainActivity extends Activity {
    @Override
    protected void onStart() {
        super.onStart();
        // View に長押しメニュー用のコールバックを設定する
        // 実際には、View に OnCreateContextMenuListener を設定するだけ
        // 登録処理を Activity が肩代わりしている
        View helloWorld = findViewById(R.id.HelloWorld);
        registerForContextMenu(helloWorld);
    }
    @Override
    protected void onStop() {
        // View に設定した長押しメニュー用のコールバックを解除する
        View helloWorld = findViewById(R.id.HelloWorld);
        unregisterForContextMenu(helloWorld);
        super.onStop();
    }
    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        // ここで、長押しメニューで利用するメニューリソースを読み込む
        super.onCreateContextMenu(menu, v, menuInfo);
    }
    @Override
    public boolean onContextItemSelected(MenuItem item) {
        // ここで、選択されたメニューリソースに対応するイベント処理を実行する
        return super.onContextItemSelected(item);
    }
}

●Viewの状態とコールバックメソッド


View には、以下のような「状態(State)」が定義される。
View の種類によって、取り得る状態は様々。たとえばクリックされたとき、フォーカスがあたったとき、のActionを実施するために必要である。

□Viewの状態

・normal:何もしていない
・enabled:Viewがユーザ操作を受け付ける状態
・focused:Viewがユーザ操作によってフォーカスを持っている状態
・selected:Viewがユーザ操作によって選択されている状態
・checked:Viewがユーザ操作によってチェックされている状態
・pressed:Viewがユーザ操作によって押されている(クリック、タップ)状態

上記の各状態について、Observer パターンでイベントを監視する仕組みがある。
監視するためのオブジェクトの登録方法によって、Activityのライフサイクルに合わせて監視オブジェクトの管理をする必要がある。
以下のようなObserver(リスナー)がある。

・OnClickListener:View をタップした時に呼び出されるイベントを拾うためのObserver
・OnLongClickListener:Viewを長押ししたときに呼び出されるイベントを拾うためのObserver
・OnFocusChangedListener:Viewのフォーカスが移動したときのイベントを拾うためのObserver
・OnCheckedChangeListener:CheckBoxなどで、チェック状態が変化したときのイベントを拾うためのObserver
・TextWatcher:EditTexdtなどで、入力テキストが変更された時のイベントを拾うためのObserver

【Notice】TextWatcherは、ActivityやFragmentのライフサイクルに合わせて、適宜Viewへの登録と解除を行う。

ex)

public class MainActivity extends Activity {
    private TextWatcher mTextWatcher = new TextWatcher() {
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {}
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
        @Override
        public void afterTextChanged(Editable s) {}
    };
    @Override
    protected void onStart() {
        super.onStart();
        // ライフサイクルに合わせて、Observer オブジェクトを登録する
        TextView helloEdit = (TextView) findViewById(R.id.HelloEdit);
        helloEdit.addTextChangedListener(mTextWatcher);
    }
    @Override
    protected void onStop() {
        // ライフサイクルに合わせて、Observer オブジェクトを解除する
        TextView helloEdit = (TextView) findViewById(R.id.HelloEdit);
        helloEdit.removeTextChangedListener(mTextWatcher);
        super.onStop();
    }
}

【参考】
https://github.com/mixi-inc/AndroidTraining/wiki/2.05.-ActionBar%E3%81%A8%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%A9%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E5%88%B6%E5%BE%A1

2014年11月7日金曜日

Eclipse Erro Could not open Selected VM debug port (8700) Make sure you do not have another instance of DDMS or of the eclipse plugin running

タイトルのエラーが出ると、デバッグが出来なかったりして不便。

Could not open Selected VM debug port (8700) Make sure you do not have another instance of DDMS or of the eclipse plugin running

hostsファイルのlocalhostの記述がコメントアウトされてしまってる場合にも出るので注意。

2014年11月6日木曜日

Android 5.0 (L) のインストール Nexus5

Android5.0 インストールメモ

準備


LG製のNexus5(SIMフリー)にインストールした。ここではwindow8を用いる。

まず、Nexus5をadb接続する。

adbがデバイスとして認識しないときは、Androidのストレージの設定が「MTP」接続でない場合がある。MTP接続にしておく。

以下のコマンドでデバイスが認識できたら、USBデバッグをONにして処理を開始する。

> adb devices 

OSの取得


以下のページからNexus5用のAndroid5.0 Preview Imageをダウンロードする。



ローカルで解凍しておく。


ブートローダのアンロック

bootloaderに接続

> adb reboot bootloader


ロックをはずす

> fastboot oem unlock


音量ボタンの大でYESを選び、電源ボタンでGOする。

OSのインストール

解凍しておいたImageのインストール用bat(Linux系の場合shell)を使うだけ。

> flash-all.bat


以上、新しいUIやARTを楽しもう。

Android メッセージと通知

Androidのメッセージングと通知についてのメモ


ざっと全体を把握し、Tipsとして使いやすく編集。

●Intent

Androidのメッセージで最も頻繁に使われる。やりとりするデータのまとまり。

送信者:Contextがメッセージとして送る仕組みをもつ。

受信者:Activity、Service、BroadcastReceiver

●Intentオブジェクト


□目的

メッセージを送信した相手に、処理を実行してもらう。

□Action 処理内容の記述

実行してほしい処理を示す。

・Activity用の代表的な処理内容

ACTION_VIEW
ACTION_MAIN
ACTION_SEND
ACTION_SENDTO
ACTION_EDIT
ACTION_PICK
ACTION_DELETE
ACTION_INSERT
ACTION_SEARCH
ACTION_CALL


・Broadcast用

ACTION_BOOT_COMPLETED
ACTION_SHUTDOWN
ACTION_PACKAGE_ADDED
ACTION_PACKAGE_REPLACE
ACTION_PACKAGE_REMOVED


□Category 処理すべき対象に期待する属性


CATEGORY_DEFAULT
CATEGORY_LAUNCHER
CATEGORY_HOME
CATEGORY_PREFERENCE
CATEGORY_BROWSABLE


□Data Actionの対象となるデータURI


ex)
ACTION_VIEWとしてDataを渡す → Dataを表示する


□Type Dataの種類を表すMIMEタイプ



□Component 期待するActionを実行するコンポーネント

すなわち、Intentっを送る対象のコンポーネント

・明示的Intent
Componentを明示する

・暗黙的Intent
Componentを明示しない


□Extras Intentを送る対象にわたす追加情報


KeyとValueのペアをBundleに追加してわたす。


□Flag

Activityの起動方法をシステムに通知するための情報


●Intent Filter


暗黙的インテントをうけとるコンポーネントが、Intentをハンドリグ可能かどうかを宣言するための仕組み
Androidフレームワークは、IntentFileterをもとにして暗黙的Intentの対象となるコンポーネントをリストアップし、
ユーザに選択肢を提供する。

□暗黙的Intentのハンドリング基準


・Action、Data、category の3つ

→ AndroidManifest で宣言が可能

<activity>の要素に<intent-filter>要素を追加する。

ex) 起動Activity

<intent-filter>
    <!-- アプリのメイン(入り口)となる Activity を起動するときの Action を受け取る -->
    <!-- Intent クラスに定義されている ACTION_MAIN 定数の実態は "android.intent.action.MAIN" という文字列 -->
    <action android:name="android.intent.action.MAIN" />
    <!-- ランチャーからの起動のものを受け取る -->
    <!-- Intent クラスに定義されている CATEGORY_LAUNCHER 定数の実態は "android.intent.category.LAUNCHER" という文字列 -->
    <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

ex) 画像共有Activity

 <intent-filter>
    <!-- ACTION_SEND または ACTION_SEND_MULTIPLE のいずれかを受け取る -->
    <!-- Intent に設定可能な Action は 1 つだけなので、<intent-filter> に Action を複数宣言すると -->
    <!-- その中からいずれかに該当するものを受け取る、という意味になる -->
    <action android:name="android.intent.action.SEND" />
    <action android:name="android.intent.action.SEND_MULTIPLE" />
    <!-- 暗黙的 Intent を扱う際に必須のカテゴリ -->
    <!-- システムは、Activity の起動に暗黙的 Intent を発行すると、 -->
    <!-- このカテゴリが付与されているものとして扱うため、Activity で暗黙的 Intent を受け取りたい場合は -->
    <!-- 必ずこのカテゴリを <intent-filter> に宣言しておく -->
    <!-- 複数のカテゴリを <intent-filter> に宣言した場合は、 -->
    <!-- 全てのカテゴリにマッチするもののみを受け取る、という意味になる -->
    <category android:name="android.intent.category.DEFAULT" />
    <!-- Data の種類の制限 -->
    <!-- MIME タイプのほか、URI のスキームを制限することもできる -->
    <data android:mimeType="image/jpeg" />
</intent-filter>


●Intentをつかった1対1のメッセージング


□Activityの起動


ex)
Intent intent = new Intent(this, NextActivity.class);
// Intent を Context に渡して、メッセージを送る
// この場合、NextActivity クラスにメッセージが送られ、NextActivity が立ち上がる
startActivity(intent);


□Activityを起動して、処理結果を期待する


ex) Intent送信
 startActivityForResult(new Intent(this, SubActivity.class), SubActivity.REQUEST_CODE_HOGE);


ex) 結果の受信
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // super.onActivityResult(int, int, Intent) の呼び出しは、条件に関係なくすること
    // Fragment から startActivityForResult(Intent, int) した場合の戻りの判定ができなくなってしまう
    super.onActivityResult(requestCode, resultCode, data);
    // requestCode には、startActivityForResult(Intent, int) の第 2 引数で指定したものが来る
    // resultCode には、呼び出し先で setResult(int, Intent) をコールした時の第 1 引数が来る
    // data には、呼び出し先で setResult(int, Intent) をコールした時の第 2 引数が来る
    switch (requestCode) {
        case SubActivity.REQEUST_CODE_HOGE:
             // REQUEST_CODE_HOGE の戻りが来た時の処理
             return;
        default:
             // 知らない requestCode の戻りが来た時の処理
             return;
    }
}

ex) Intent受信

public class SubActivity extends Activity {
    public static final int REQUEST_CODE_HOGE = 1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 何かする
        // 呼び出し元に返す Intent オブジェクトをセットする
        // 第 1 引数には、RESULT_OK または RESULT_CANCELLED、あるいは、RESULT_FIRST_USER を起点にした独自の int 型定数を使う
        setResult(RESULT_OK, new Intent());
        finish();
    }
}


□Serviceを起動するIntent

Serviceを呼び出す。

ex)
// サービスのバインド要求
bindService(new Intent(this, MyService.class), mServiceConnection, Context.BIND_AUTO_CREATE);

// サービスの開始要求
startService(new Intent(this, MyIntentService.class));


●Intentを用いた、1対多のメッセージング

ブロードキャスト:アプリ内外に問わず、Intentを全体へ投げかける仕組み。Androidでは端末の状態を全アプリに通知するための仕組みとして利用している。

□BroadcastReceiver

ブロードキャストされるIntentを受信するためのコンポーネント

【注意】宣言の仕方によって、使われ方が異なる。


□BroadcastReceiverのライフサイクル


Intentを受け取るためのコールバックメソッド onReceive をオーバーライドする。
BroadcastReceiverのライフサイクル = オーバーライドした onReceive の実行されている間

ex)
public class MyBroadcastReceiver extends BroadcastReceiver {
    // Broadcast された Intent を受け取るコールバック
    @Override
    public void onReceive(Context context, Intent intent) {
    }
}

□BroadcastReceiverプロセスの優先順位


最も優先順位の高いフォアグラウンドプロセス

・BroadcastReceiverのライフサイクルが終了したプロセス = 動作している他のコンポーネントがもつ優先順位となる
・BroadcastReceiverのみがプロセス上動作している場合、BroadcastReceiverのライフサイクルが終了すると、空プロセスとなり、システムが優先してkillする。

どちらの場合も、BroadcastReceiverを実行したプロセスはシステムによって終了可能で、プロセス上で非同期処理を実行した場合、処理が終わってもどる前に、
プロセスがkillされる可能性がある。よって、BroadcastReceiverのプロセスは非同期処理に向いていない。

非同期処理には、Service と呼ばれる仕組みを利用する。

また、BroadcastReceiverの中でダイアログを表示することも推奨されない。これも、ライフサイクルがダイアログのイベント実行より先に終了し、プロセスがkillされるからである。
ダイアログは、Notification と呼ばれる仕組みを利用する。


□Static BroadcastReceiver(静的ブロードキャスト)

AndroidManifestに宣言されるBroadcastReceiverでありアプリがインストールされている間はずっとBroadcastReceiverが動作し、ブロードキャストするIntentの監視を続ける。
ただし、Honeycomb以降は、1度でも起動することが条件である。

ex) AndroidManifest <application>要素の子として登録する
<receiver
    android:name="jp.mixi.sample.MyBroadcastReceiver">
    <intent-filter>
    </intent-filter>
</receiver>

ex) 受信コード

public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
    }
}

□Dynamic BroadcastReceiver(動的ブロードキャスト)


ActivityなどのContextで動的に登録/解除が行われるBroadcastReceiver。

・ActivityのContext
ActivityのContextの中で動的に登録する場合、解除もActivityのContextの中で実施する必要がある。
どのライフサイクルのContextで登録を行うかも大事。

「ActivityのContextで動的に登録 → ActivityのContextで解除する」のようにセットで行う必要がある。もし解除がされていない場合、
システムが自動で登録を解除し、適切に解除を行う指示をエラーログに出力する。

ex)
public class MyActivity extends Activity {
    private BroadcastReceiver mMyReceiver = new MyBroadcastReceiver();
    @Override
    protected void onStart() {
        super.onStart();
        // ACTION_PACKAGE_ADDED の Action を通す IntentFilter オブジェクトを作成
        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
        // MyBroadcastReceiver オブジェクトを、指定した IntentFilter オブジェクトで、Activity の Context に登録
        registerReceiver(mMyReceiver, filter);
    }
    @Override
    protected void onStop() {
        // MyBroadcastReceiver オブジェクトの登録を、Activity の Context 上から解除する
        // Activity のライフサイクルが終わりに向かうコールバックの中で実装する
        unregisterReceiver(mMyReceiver);
        super.onStop();
    }
}


・ApplicationのContext
アプリケーションの中でグローバルなスコープでBroadcastReceiverが動作する。
解除を忘れても、システムは自動で登録を解除しないので、リークの原因となる。


□Intentのブロードキャスト


・ブロードキャストの送信

ex)

Context#sendBroadcast(Intent)

ex) 定義したActionやCategoryで受け付けてくれるすべてのBroadcastReceiverがIntentを受信する。

public class MyActivity extends Activity {
    // 自分で独自の Action を定義することも可能
    public static final String ACTION_HOGEHOGE = "jp.mixi.sample.android.intent.action.HOGEHOGE";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // do something...
        // Intent のブロードキャスト
        Intent intent = new Intent();
        intent.setAction(ACTION_HOGEHOGE);
        sendBroadcast(intent);
    }
}


□LocalBroadcastManager アプリ内全体へのブロードキャスト

他のアプリに知られたくない、自プロセス内※の閉じたBroadcastを実現する。
※LocalBroadcastManagerはマルチプロセスをサポートしない。
・Privateデータを送信しても安心
・エクスプロイト対策
・軽量に利用可能

ex)
// LocalBroadcastManager は Support Package に含まれている
import android.support.v4.content.LocalBroadcastManager;
public class MainActivity extends Activity {
    public static final String TAG = MainActivity.class.getSimpleName();
    public static final String ACTION_HOGEHOGE = "jp.mixi.sample.android.intent.action.HOGEHOGE";
    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.v(TAG, "local broadcast received.");
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    @Override
    protected void onStart() {
        super.onStart();
        // この Activity の Context の中での、Local な Broadcast を管理する為の LocalBroadcastManager オブジェクト
        LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);
        manager.registerReceiver(mReceiver, new IntentFilter(ACTION_HOGEHOGE));
    }
    @Override
    protected void onStop() {
        LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);
        manager.unregisterReceiver(mReceiver);
        super.onStop();
    }
    // ボタンなどのクリックハンドラ
    public void onHogeClick(View v) {
        // Local な Broadcast として Intent を投げる
        // 通常の sendBroadcast(Intent) メソッドと違い、この仕組で投げた Intent は他のアプリ(プロセス)では拾うことが出来ない
        LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);
        manager.sendBroadcast(new Intent(ACTION_HOGEHOGE));
    }
}


□Broadcastのバーミッション

Manifestにパーミッションを設定することで、パーミッションを得ているアプリのみがブロードキャストを受信できるようにする。
この場合、パーミッションをもっていれば、他のアプリでも受信が可能。

●Intentへの付加情報


□Intent Extras

いくつかのプリミティブ型と文字列、コレクション、Parcelebleオブジェクト(シリアライズ)を付加。


ex) Intent送信側

public class MyActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // do something...
        List<String> list = new ArrayList<String>();
        list.add("fuga");
        list.add("piyo");
        Intent intent = new Intent(this, NewActivity.class);
        intent.putExtra("StringExtraKey", "hogehoge");  // 文字列の場合
        intent.putExtra("BooleanExtraKey", true); // boolean の場合
        intent.putExtra("IntegerExtraKey", 10); // int の場合
        intent.putStringArrayListExtra("StringArrayListExtraKey", list); // ArrayList<String> の場合
        startActivity(intent);
    }
}

ex) Intent受信側

public class NewActivity extends Activity {
    @Override
    protected void onCreate(savedInstanceState) {
        super.onCreate(savedInstanceState);
        // do something...
        // この Activity への Intent オブジェクトを取得する
        Intent received = getIntent();
        String stringExtra = received.getStringExtra("StringExtraKey"); // 文字列の Extra を取り出す
        boolean booleanExtra = received.getBooleanExtra("BooleanExtraKey"); // boolean の Extra を取り出す
        int integerExtra = received.getIntExtra("IntegerExtraKey"); // int の Extra を取り出す
        List<String> listExtra = received.getStringArrayListExtra("StringArrayListExtraKey"); // ArrayList<String> の Extra を取り出す
    }
}


●Notification

Anroidのステータスバーにアプリからのお知らせを知らせる仕組み。

Notificationの種類

□ Normal View

・通知のアイコン
・アプリのタイトル
・詳細メッセージ
・情報量
・小さいアイコン
・通知表示した時間

□ Big View (JellyBean以降)

・通知のアイコン
・アプリのタイトル
・詳細メッセージ
・情報量
・小さいアイコン
・通知表示した時間
・詳細表示View:スタイル(Bit Picture Style、Big Text Style、Inbox Style)

●Notificationの仕組み


通知表示用の窓口:NotificationManager

窓口が受け取るもの:Notificationオブジェクト

Notificationオブジェクトの生成:NotificationCompat.Builderを使う。(直接のインスタンス生成は不具合のもと)

Notificationの必要条件:アプリのアイコン、通知のタイトル、詳細メッセージの3つを保持

□PendingIntent

通知では、仕組みをシステム(この場合Androidシステム)にあわせる。PendingIntentを使うと、Intentの送信タイミングの遅延、Intentオブジェクトのハンドリングを委譲することが可能。

ex)

public class MyActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // do something...
        // Intent の準備。明示的 Intent でも、暗黙的 Intent でもどちらでも構わない
        Intent intent = new Intent(this, SubActivity.class);
        // PendingIntent オブジェクトの生成。このオブジェクトを他のアプリに渡すことで、引数に渡した Intent の送信を委ねることができる
        // PendingIntent は、Intent の送信先のコンポーネントの種類によって使い分けること
        PendingIntent activityIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    }
}

PendingIntentオブジェクトは、遅延させるIntentオブジェクトのターゲットしているコンポーネントによって、利用するメソッドを変える。

・ActivityがターゲットのPendingIntentを作成する
PendingIntent#getActivity(Context, int, Intent, int)


・ActivityがターゲットのPendingIntentを作成するが、複数(配列でわたす)のActivityを扱う:Honeycomb以降
PendingIntent#getActivity(Context, int, Intent[], int)

・BroadCast用のIntentをもつPendingIntentオブジェクトを作成する
PendingIntent#getBroadcast(Context, int, Intent, int)

・ServiceがターゲットのPendingIntentオブジェクトを作成
PendingIntent#getService(Context, int, Intent, int)

全てのメソッドで第2引数にint型をrequireしている。これは、PendingIntentを一意に認識するためのIndexの役割をもつ。

第4引数のint型は、PendingIntentオブジェクトを受け取る側が、どのように扱うかを支持するもの。
以下の定数から選択する。

・既存の古いPendingIntentをキャンセルして、新しいPendingIntentを作成する場合
PendingIntent.FLAG_CANCEL_CURRENT

・既存のPendingIntentを使う(既存が無い場合はnull)
PendingIntent.FLAG_NO_CREATE

・ただ一度PendingIntentを使用する場合
PendingIntent.FLAG_ONE_SHOT

・既存のPendingIntentのExtrasのみ入れ替える
PendingIntent.FLAG_UPDATE_CURRENT


●NotificationCompat.Builder

Notificationオブジェクトの作成

ex)
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, SubActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
        Notification notification = builder
                // 通知の日時
                .setWhen(System.currentTimeMillis())
                // 通知のタイトル
                .setContentTitle("通知だヨ!")
                // 通知の詳細メッセージ
                .setContentText("通知の詳しい内容をここに書きます。")
                // 通知のアイコン
                .setSmallIcon(R.drawable.ic_launcher)
                // 通知を表示した瞬間、通知バーに表示するショートメッセージ
                .setTicker("通知だヨ!")
                // 通知をタップした時に使う PendingIntent
                .setContentIntent(pendingIntent)
                // この通知が未だ表示されていない時だけ、音やバイブレーション、ショートメッセージの表示を行う
                .setOnlyAlertOnce(true)
                // タップしたら消えるようにする
                .setAutoCancel(true)
                .build();
    }
}

●作成したNotificationオブジェクトの通知


ex)
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Notification notification = /* 通知オブジェクトの作成(省略) */
        // 直接インスタンス化せず、Context を経由してインスタンスを取得する
        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        // 通知の種類に応じて id を割当てることが出来る。
        // id の異なる通知は違うものとして扱われる。
        manager.notify(0, notification);
    }
}

【参考】https://github.com/mixi-inc/AndroidTraining/wiki/2.04.-%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%83%B3%E3%82%B0%E3%81%A8%E9%80%9A%E7%9F%A5#Intent

2014年11月4日火曜日

Android Resource

Androidアプリケーションのリソースに関するメモ


Androidアプリのリソース

Androidアプリのリソースとは、コードとは分離された静的なデータである。
主にresディレクトリ配下を使用する。

Javaのプログラムからresディレクトリに配置した静的なデータにアクセスする場合、
生成されるRオブジェクトを介して行う。

【参考】Rについて
http://developer.android.com/intl/ja/reference/android/R.html

●扱うことができる読み取り専用リソース


String:文字列
Drawble:ビットマップ、ベクタ画像
Style:UIスタイル、テーマ
Menu:メニュー項目
ColorStateList:状態に対応した色の管理
Value:プリミティブ型(http://goo.gl/aQRTSN)
Animation:アニメーション
Raw:音声・動画・画像などのRawデータ
Layout:画面レイアウト
XMLResource:任意のXMLファイル

●アニメーションの種類

Animationには大きく3種類あります。

・Tweenアニメーション:1つのイメージを連続的に変化させるタイプ。ある対象に対するアフィン変換+フェードイン、アウト
・Propertyアニメーション:FragmetnやDrawableなど様々なオブジェクトにアニメーションを提供する。背景変化、透過変化(API Level 11 HoneyComb)
・Frameアニメーション:順番にイメージを並べて表示するタイプ。パラパラ漫画のような効果。

□Alpha:フェードイン、アウト

<alpha>要素
・android:fromAlpha属性
 アニメーション開始時のアルファ値
・android:toAlpha属性
 アニメーション終了時のアルファ値

□Scale:拡大・縮小

<scale>要素(全ての属性を設定する必要はない)
・android:fromXScale属性
 アニメーション開始時の、横方向の拡大率です。1.0 を基準に拡大率を決定。
・android:toXScale属性
 アニメーション終了時の、横方向の拡大率です。1.0 を基準に拡大率を決定。
・android:fromYScale属性
 アニメーション開始時の、縦方向の拡大率です。1.0 を基準に拡大率を決定。
・android:toYScale属性
 アニメーション終了時の、縦方向の拡大率です。1.0 を基準に拡大率を決定。
・android:pivotX属性
 拡大の中心点のX座標です。指定しないと 上端。
・android:pivotY属性
 拡大の中心点のY座標です。指定しないと 左端。

□Translate:並行移動

自分の大きさに対する % を指定。%pは、親要素の大きさに対するパーセンテージ
<trnslate>要素
・android:fromXDelta属性
 アニメーション開始時の X 座標。-100 から 100 までの % 単位または %p 単位の数字か、単位のない任意の数字を設定可能。
・android:toXDelta属性
 アニメーション終了時の X 座標。-100 から 100 までの % 単位または %p 単位の数字か、単位のない任意の数字を設定可能。
・android:fromYDelta属性
 アニメーション開始時の Y 座標。-100 から 100 までの % 単位または %p 単位の数字か、単位のない任意の数字を設定可能。
・android:toYDelta属性
 アニメーション終了時の Y 座標。-100 から 100 までの % 単位または %p 単位の数字か、単位のない任意の数字を設定可能。

□Rotate:回転

<rotate>要素
・android:fromDegrees属性
 アニメーション開始時の角度。
・android:toDegrees属性
 アニメーション終了時の角度。
・android:pivotX属性
 回転の中心点の X 座標。
・android:pivotY属性
 回転の中心点の Y 座標。

□複合

<set>要素を使用することで、複数のアニメーションを組み合わせることが可能。

□共通

・android:duration属性
 アニメーションを再生する時間です。millisecond 単位で指定します。
・android:repeatMode属性
 アニメーションが終了した時に、アニメーションを最初から再開するか、最後から最初へ戻るようにするかを設定する。
この設定が有効になるのは、後述するandroid:repeatCount属性が 0 以上か、若しくは infinite に設定された時のみ。
デフォルト値では、アニメーションを最初から再開するように設定される。
・android:repeatCount属性
 アニメーションを繰り返す回数です。infinite と設定することで、無限に繰り返すようにもできる。
・android:startOffset属性
 アニメーションのスタートの遅延時間です。指定した時間(msec)だけ遅延させることができる。

ex)
<set
    xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 回転は無限回 -->
    <!-- アニメーションする View のちょうど真ん中を中心に一周する -->
    <rotate
        android:duration="1000"
        android:repeatCount="infinite"
        android:fromDegrees="0"
        android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%"/>
    <!-- 平行移動は 1 回だけ -->
    <!-- アニメーションする View の親の 40% の位置に移動する -->
    <translate
        android:duration="1000"
        android:repeatCount="0"
        android:toXDelta="40%p"
        android:toYDelta="40%p"/>
</set>

適用はJavaコードで行う。

ex)
View textView = findViewById(R.id.TextView1);
// Animation Resource を読み込んで、Animation オブジェクトを得る
Animation animation = AnimationUtils.loadAnimation(this, R.anim.sample_animation);
// Animation オブジェクトを View に渡して、アニメーションを開始する
textView.startAnimation(animation);


これらは同じアニメーションでも、静的データを配置する場所が異なるので注意が必要。


●読み取り専用リソースの配置場所(リソースディレクトリ)


res/animator:Propertyアニメーション
res/anim:Tweenアニメーション
res/color:ColorStateList
res/drawable:Drawable
res/layout:Layout
res/menu:Menu
res/raw:Raw
res/values:Values、Strin、Style
res/xml:任意のXML、GAの設定ファイル

●リソースディレクトリの命名規則


<resources_name>-<configuration_qualifier>

ex) drawable-hdpi(hdp向けdrawable)、values-en(英語ロケール向けvalues)、drawable-land-xhdpi(横画面xhdpi向けdrawable)

●リソースにアクセスする

ResourceID:aaptが自動生成する。リソースファイルに問題があるとaaptがコンパイルと最適化に失敗し、IDを生成できない。
aaptが生成した結果は Rクラスに反映され、IDに対応したint型の整数値が割り当てられる。

・XMLの属性でIDを割り当てるもの
・ファイル名をIDとするもの

以下の記述でアクセスする。package_nameは任意である。

(package_name).R.(resource_type).{resource_di}

AndroidフレームワークのもっているRクラスは直接importせず、FQDNでアクセスする。

import android.R;
R.id.text_1

android.R.id.text_1;

●XMLからリソースにアクセスする

layoutなどの記述で頻繁に記載する方法です。

ex)
@android:id/text_1

上記は、リソースIDを用いてアクセスする例。リソースの種類によって書き方が異なる。(http://goo.gl/kA8cl1)


●リソースの記述方法

□String Resource

UIで使用する定型文は直接XMLに記述せずに、String Resourceで定義することが推奨される。

ex)

<resources>
    <string name="hello_world">Hello World</string>
    <string name="image_recognize">Image Recognize</string>
</resources>

XMLファイルで使う場合、前述の「XMLからリソースにアクセスする」を参考に。
Contextをもっているクラスでは、以下のように取り出すことができる。

ex)

String hello = getString(R.string.hello_world);

□String Array

文字列の配列を定義する。受け取る側は配列として受け取ることができる。

ex)

<resources>
    <string-array name="foo_bar">
        <item>Foo</item>
        <item>Bar</item>
    </string-array>
</resources>

ex) Contextをもっているクラスでの受け取り。

getResources().getStringArray(R.array.foo_bar);


同じ構造でint型を使用した、Integer Array がある。


□Format

プレースホルダを用いて、可変な文字列をリソースで提供する。後から文字列の一部を変更したい場合などに用いる。$dは数字を示す。文字列を渡したいときは$sとする。Formatterで定義される(http://developer.android.com/intl/ja/reference/java/util/Formatter.html)。


ex)
<resources>
    <string name="my_format">私は、%1$d社製のギターをもっている。</string>
    <string name="my_format_2">私は、%1$d社製のベースと、%2$d社製のギターをもっている。</string>
</resources>

ex) 受け取り側で値を設定する

String formatted = getString(R.string.my_format_2, 'Fender', 'Gibson');


□Plurals

複数形の表示。

ex)
<resources>
    <plurals name="my_guiters">
        <item quantity="zero">I don't have guiter.</item>
        <item quantity="one">I have a guiter.</item>
        <item quantity="other">I have %d guiters.</item>
    </plurals>
</resources>

quantity属性は、zero、one、two、few、many、other から選択する。

ex) 受け取り側

String quantity = getResources().getQuantityString(R.plurals.my_guiters, 2, 2);
日本語端末の場合は注意が必要で、単数・複数形を持たない日本語の場合、常にotherが使用される。otherが定義されていないと落ちる。
http://qiita.com/mstssk/items/6697db6947918e58593b

□Drawable Resource(グラフィックリソース png/jpeg/gif/9patch形式の画像/XML定義の図形/アニメーションGIFは非対応)

推奨:png、容認:jpeg、非推奨:gif

解像度ごとのディレクトリ階層で分けていない場合、あるいは該当する解像度のリソースディレクトリがない場合は、システムによって適宜拡大・縮小が行われるため、画像が鮮明でなくなる可能性がある。

Drawableに配置したリソースはアプリのビルドで最適化される。最適化されたくない、元の状態を保持したい場合は、Rawに配置すること。

Drawableには種類がある。Bitmap、9-patch、Layer List、State List、Level List、Transition、Inset、Clip、Scale、Shape である。順に見ていく。【参考】http://goo.gl/7kvVdY

Bitmap
pngやjpg、gif
9-patch
ストレッチ可能な領域を指定した画像でデータ
Layer List
複数のDrawable要素をレイヤ上に並べたXMLのグラフィックリソース<item>要素でDrawableへの参照を記述したり、各種Drawableを子にもつレイヤをつくったりする。
ex)
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 一番下のレイヤに配置する、グレーで塗りつぶされた四角形の Drawable を持つ item 要素 -->
    <item>
        <shape android:shape="rectangle">
            <solid android:color="#f5f5f5"/>
        </shape>
    </item>
    <!-- 一番上のレイヤに配置する、薄茶色で塗りつぶされた四角形の Drawable を持つ item 要素 -->
    <!-- 上端から 30dp 分のオフセットが設定されている -->
    <item android:top="30dp">
        <shape android:shape="rectangle">
            <solid android:color="#deb887"/>
        </shape>
    </item>
</layer-list>

State List Drawable
UIの状態に応じたDrawable要素を管理する。ボタンのタップ、非タップで状態を変えたいときになどに使用。

ex)
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- View が押された状態の Drawable -->
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <solid android:color="#696969"/>
        </shape>
    </item>
    <!-- View が通常の状態の Drawable -->
    <item>
        <shape android:shape="rectangle">
            <solid android:color="#f5f5f5"/>
        </shape>
    </item>
</selector>

Level List
一定の規定値に依存して、Drawable要素を管理する。例えば、カーソルによって変化する値によって表示状態を変えたい場合などに使用。

ex)
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Level が 最高 0 までの時の Drawable への参照 -->
    <item android:maxLevel="0" >
        <shape android:shape="rectangle">
            <solid android:color="#f5f5f5"/>
        </shape>
    </item>
    <!-- Level が 最高 1 までの時の -->
    <item android:maxLevel="1" >
        <shape android:shape="rectangle">
            <solid android:color="#696969"/>
        </shape>
    </item>
</level-list>


ex) 取り出し方(Drawable系共通)

Drawable drawable = getResources().getDrawable(R.drawable.hogehoge);

Transition
複数のDrawableのクロスフェードを管理する。例えばフェードインやフェードアウト効果に使用する。コード上で実行時間を与えるなどしてstartTransitionして実行する。

ex)
<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 最初の状態 -->
    <item>
        <shape android:shape="rectangle">
            <solid android:color="#f5f5f5"/>
        </shape>
    </item>
    <!-- 次の状態 -->
    <item>
        <shape android:shape="rectangle">
            <solid android:color="#696969"/>
        </shape>
    </item>
</transition>

Inset
Drawableの中に別のDrawableを差し込むXMLリソース。ViewがView自身より小さい背景グラフィックを要求する時に便利。また、Drawableのマージン(画像の外側の余白)をつけることにも利用可能。

ex)
<inset
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_launcher"
    android:insetTop="10dp"
    android:insetLeft="10dp"/>


Clip
Drawableのレベルによって、別のDrawableを切り抜く。Levelを変換させることで、徐々に画像を表示させるなどが可能。

ex)
<clip xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_launcher"
    android:clipOrientation="horizontal"
    android:gravity="right"/>

コード側で、レベルをセットすることで切り抜き状態を変更する。

ex)
ClipDrawable clip = (ClipDrawable) clipView.getBackground();
clip.setLevel(1000);

Scale
大きさを変化させる。初期状態は0(0倍 = 表示されない)、コードでレベルを変化させる。

ex)
<scale
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_launcher"
    android:scaleGravity="center"
    android:scaleWidth="50%"
    android:scaleHeight="50%"/>

ex)
ScaleDrawable scale = (ScaleDrawable) scaleView.getBackground();
scale.setLevel(1);


Shape
色やグラデーション情報を含む、幾何学的図形をXMLで宣言するグラフィックリソース

ex)
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
        <!-- solid: 塗りつぶし -->
    <solid
        android:color="#f5f5f5"/>
        <!-- stroke: 枠線 -->
    <stroke
        android:width="1dp"
        android:color="#000000"/>
</shape>

●Style

Viewの各要素の属性値をまとめて、一定のフォーマットを基底するリソース。アプリ内で共通のUIを提供する際に活用する。

ex)
<resources xmlns:android="http://schemas.android.com/apk/res/android">
    <style name="BigTextViewStyle" parent="@android:style/Widget.TextView">
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:textSize">22sp</item>
    </style>
</resources>

上記のように定義したStyleを、各Viewに設定することで、Viewのフォーマットを指定します。

ex)
<TextView
        style="@style/BigTextViewStyle"
        android:id="@+id/HelloWorld1"
        android:text="@string/hello_world"/>


●Styleの継承

システムで定義されたスタイルを継承する。style要素にparent属性を追加。parent属性には、継承したいスタイルを設定。

ex)
<resources xmlns:android="http://schemas.android.com/apk/res/android">
    <style name="BigTextViewStyle" parent="@android:style/Widget.TextView">
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:textSize">22sp</item>
    </style>
</resources>

●parent属性以外の継承

名称で継承する。

ex)

  • <style name="BigTextViewStyle.Red">
  •     <item name="android:textColor">#FF0000</item>
  • </style>
  • <!-- BigTextViewStyle.Red を継承する -->
  • <style name="BigTextViewStyle.Red.Bold"> ←元のStyle名に.~で新しく定義するスタイルの名前を宣言する。
  •     <item name="android:textStyle">bold</item>
  • </style>


●Menu

メニュー(OptionMenu、ContextMenu)の各種項目。

・OptionMenu:デバイスのMenuキー。または、ActionBarの並びに入るメニュー。
・ContextMenu:UIの長押しで呼び出されるメニュー

□メニューのDefine


ex)
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item ← メニュー1つ文に相当する要素。子にmenu属性を配置して、submenuにできる。
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:showAsAction="never"
        android:title="@string/action_settings"/>
</menu>

□OptionMenu


以下のようなコードでメニューリソースにアクセスして、リソースファイルの状態を反映する。

ex)
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return super.onCreateOptionsMenu(menu);
}

□ContextMenu

明示的にviewに対して、ContextMenuを割り当てることでリッスン可能。ただし、リスナーのコールバックはonCreateContextMenu()で受け付けるため、Activityへの参照を他のオブジェクトがもつことになる。そのため、適切なタイミングでコールバックを受け付けないようにライフサイクルを管理する必要がある。詳細は後述。


●Color State List

例えばボタンがタップされたとき、そうでないときの色を変化させるなどの際に使用する。
ただし、カラーリソースはbackgroundなどのグラフィックリソースへの参照が必要な属性には指定できない。

ex) sample_color_state_list.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- View が押された状態の Drawable -->
    <item android:color="#696969" android:state_pressed="true"/>
    <!-- View が通常の状態の Drawable -->
    <item android:color="#f5f5f5"/>
</selector>

ex)
<Button
    android:id="@+id/ColorStateListButton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textColor="@color/sample_color_state_list"
    android:text="@string/hello_world"
    android:textSize="16sp"/>

●Value Resource

プリミティブ型を使用する。

ex)
<resources>
    <!-- 個別の値を定義する -->
    <integer name="DefaultEggs">1</integer>
    <integer name="DefaultBooks">3</integer>
    <integer name="DefaultPickaxe">100</integer>
</resources>

ex) 受け取る側
int eggs = getResources().getInteger(R.integer.DefaultEggs);

●Typed Array

様々な型を配列にできる。取り出すときは型に中ウイする。

ex)
<resources>
    <!-- String や Integer 以外のものでも配列にできる -->
    <array name="Drawables">
        <item>@drawable/ic_launcher</item>
    </array>
    <array name="Colors">
        <item>@color/Red</item>
        <item>@color/Green</item>
        <item>@color/Blue</item>
    </array>
    <!-- 異なる型のものを混ぜても良いが、Java のコード上で扱う際に、場所と型を気をつけないといけないので注意 -->
    <array name="MixedArray">
        <item>@drawable/ic_launcher</item>
        <item>@color/Blue</item>
    </array>
</resources>

ex) 取り出し側(Colors)
TypedArray colors = getResources().obtainTypedArray(R.array.Colors);
        int color2 = colors.getColor(0, Color.BLACK);


●Boolean

bool値

ex)
<resources>
    <bool name="isCompany">true</bool>
    <bool name="isPerson">false</bool>
</resources>

ex) 受け取り側
boolean bool = getResources().getBoolean(R.bool.isCompany);


●Dimension

寸法を指定する。Viewの大きさや文字サイズ。Javaコードで取り出すとFloat型になる。

ex)
<resources>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>
    <!-- 文字の大きさも定義可能 -->
    <dimen name="TitleSize">16sp</dimen>
    <dimen name="BodySize">14sp</dimen>
</resources>

ex) 受け取り側
float titleDim = getResources().getDimension(R.dimen.TitleSize);

●Color

色コードを定義する。Javaコードで取り出すとint型整数値になる。

ex)
<resources>
    <color name="Red">#FF0000</color>
    <color name="Green">#00FF00</color>
    <color name="Blue">#0000FF</color>
</resources>

ex) 受け取り側
int color = getResources().getColor(R.color.Blue);

●ID

レイアウトのIDをまとめる。

ex)
<resources>
    <item type="id" name="HelloWorld"/>
    <item type="id" name="Hoge"/>
</resources>

●Raw(Binary)

最適化除外対象のバイナリデータ。
InpurStreamを使用してアクセスする。

ex)
InputStream in = getResources().openRawResource(R.raw.ファイル名);


●多様なデバイスへの対応

Contextを通してリソースへアクセスする設計により、リソースを使う側が端末に依存したリソースの取得を意識しないで済むようになっている。
configuration_qualifierは、各デバイスのリソースに関する特徴を決める部分であり、重要である。

□configuration_qualifierの仕様

以下の上から順にハイフン区切りでconfiguration_qualifierを連結し、リソースを定義する。

・Language and Region
 言語
 jaやenやfrなどと指定する
・Smallest Width
 ディスプレイの短辺の大きさ
 sw600dpやsw720dpなど、sw<N>dpの形式で、dpの単位で指定する
・Screen Orientation
 画面の向き
 landまたはport
・Screen Pixel Density
 ピクセル密度
 ldpi、mdpi、hdpi、xhdpi、xxhdpi、nodpi、tvdpiなどと指定する。
・Platform Version
 OS のバージョン
 v4、v7などと指定する。数字は API Level のものを使う。

ex)

values-en-v4:良い
values-v4-en:ダメ

●ロケール

端末で仕様している言語に応じてリソースを定義する。
言語コードISO 639-1、地域コードISO 3166-1-alpha-2 を使える。


●高解像度スクリーン

デフォルトで用意されており、頻繁に利用する。

・ldpi:drawable-ldpi
・mdpi:drawable-mdpi
・hdpi:drawable-hdpi
・xhdpi:drawable-xhdpi ← 現在主流である。
・xxhdpi:drawable-xxhdpi

●画面向き

向きによってリソースファイルを切り替えることが可能。

・縦:layout-land, drawable-land
・横:layout_port, drawable-port