Androidの最近のブログ記事

Android Wear 1.3 から対話型ウォッチフェイス(Interactive watch faces)を作成するためのAPIが追加されました。

 

公式ブログ(Android Developers)

 

このAPIを使用すると、ウォッチフェイスの画面上で ユーザーがタップした座標を取得できる ため、ユーザーのタップ位置に応じて画面表示を切り替えるなど、ユーザーのタップに連動したウォッチフェイスを作ることができます。

具体的な動作イメージについては、「Pujie Black」というウォッチフェイスの [動画ページ] を確認してみるのがよいでしょう。

なお、この API を使用するには、Android Wear のバージョンが 1.3 以上である必要がありますが、このバージョンを搭載したAndroid Wear エミュレーターイメージは既に公開されているため、実機がなくても確認することができます。

Version.png

 

対話型ウォッチフェイスに対応するには?

1. Wearアプリの起動時にWear端末のバージョンが対話型ウォッチフェイスに対応していることを確認する

 ※ XXXWatchFaceService.Engine を継承したクラスの onCreate() で行い、フラグなどで覚えておく

try {
    PackageManager pm = getPackageManager();
    PackageInfo packageInfo = pm.getPackageInfo("com.google.android.wearable.app", 0);
    if (packageInfo.versionCode > 720000000) {
        // 対話型ウオッチフェイスAPI(タップイベントの応答)に対応
    } else {
        // 対話型ウオッチフェイスAPI(タップイベントの応答)に非対応
    }
} catch (PackageManager.NameNotFoundException e) {
    e.printStackTrace();
}

ちなみに versionCode の判定に使用している 720000000 (=Android Wear 1.3)については、定数がないようで困ったものです。

以下、わかる範囲で調べた過去の Android Wear バージョンと versionCode の紐付けです。

  • 1.0.1.1379611 → 201379611
  • 1.0.2.1476973 → 201476973
  • 1.0.4.1580939 → 401580939
  • 1.0.5.1630507 → 501630507 (Android 5.0.2)
  • 1.1.1.1944630 → 701944630 (Android 5.1.1)
  • 1.3.0.2139573 → 722395730 (Android 5.1.1)

 

2. ウオッチフェイススタイルにタップイベントを許可する設定を追加する

 ※ XXXWatchFaceService.Engine を継承したクラスの onCreate() で行う

setWatchFaceStyle(new WatchFaceStyle.Builder(MyWatchFace.this)
    .setAcceptsTapEvents(true) // <---------- この行を追加
    .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
    .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
    .setShowSystemUiTime(false)
    .build());

 

3. タップ時に呼び出される onTapCommand() をオーバーライドし、ユーザーのタップ位置に応じた処理を行う

引数の tapType は、タップ時に TAP_TYPE_TOUCH(0)、離すと TAP_TYPE_TAP(2) がセットされます。

 ※ TAP_TYPE_TOUCH_CANCEL(1) は、長押し時に TAP_TYPE_TOUCH(0) の後に呼び出されていました

@Override
public void onTapCommand(int tapType, int x, int y, long eventTime) {
    super.onTapCommand(tapType, x, y, eventTime);

    // 動作確認用ログ出力
    Log.d(TAG, "onTapCommand(" +
            String.valueOf(tapType) + ":" +
            String.valueOf(x) + "," +
            String.valueOf(y) + ")");
}

 

上記のように、既存のウォッチフェイスのコードに対して、少しのコードを追加するだけで、タップに応答することができます。

タップに応じて、どのような処理をするかは、あなたのアプリケーション次第ですが、これまでよりも機能や表現力が高いウォッチフェイスを作る事ができるため、非常に嬉しいAPIだと思います。

 

Android Developers のサイトには、対話型ウォッチフェイスのパターン例が紹介されていますので、確認してみてください。

android_developers_design.png

出典:https://developer.android.com/design/wear/watchfaces.html#interactive

Android 6.0(Mashmallow)API Level 23から、MIDI機器を制御できるAPIが追加されたので試してみました。

 

■ 準備するもの

  • Android SDKとAndroid StudioをインストールしたPC
  • Android 6.0をインストールした端末
  • USB接続可能なMIDI機器
  • Android端末とMIDI機器を接続するUSBケーブル(USB B-A + A-microB など)

 

必要なものが準備できたら、以下の手順でAndroid端末とMIDI機器をセットアップします。
接続の順番を間違えると、WiFi越しのadb接続が切れたり、MIDI側がAndroid端末を認識しなかったりする可能性があります。

 

■ Android端末をセットアップする

  • あらかじめPC上でAndroid Studioを起動しておく(あとでadbがrestartされないように)
  • Android端末とPCをUSBでつなぎ(通常どおりMTP)接続する
  • Android端末をWiFiネットワークに接続する
  • 「adb tcpip 5555」「adb connect IPアドレス:5555」でWiFi下でadbを使用できるようにする
  • 「adb device」でWiFiのIPに接続された事を確認する
  • Android端末とPC間のUSBケーブルを外す(いったんadbは切れる)

 

■ MIDI機器をセットアップする

  • MIDI機器の電源をオンし、起動時の入力をUSBにして動作するように設定して電源オフ
  • Android端末上で開発者向けオプションなどから「USB設定の選択=MIDI」に切り替える
  • Android端末とMIDI機器をUSB接続してから、MIDI機器の電源をオン
  • 再度「adb connect IPアドレス:5555」でWiFi経由で再接続する

 

セットアップ後は、Android Studio上で適当なプロジェクトを作成し、
以下の解説を参考に、MIDI機器を制御するアプリを作成します。
なお、minSdkVersionは23で作成してください。

 

■ MIDI APIを使用するには?

AndroidManifest.xmlへ以下を追加します。

<uses-feature android:name="android.software.midi" android:required="true"/>

 

■ MIDI機器の情報を取得するには?

システムサービスからMidiManagerを取得後、MidiManager#getDevices()を呼び出すと、
Android端末に接続されているMIDI機器の情報が、MidiDeviceInfoとして取得できます。
MidiDeviceInfoは、MidiDeviceInfo#getProperties()を呼び出すことで、 各種プロパティー情報にアクセスできます。

MidiManager midiManager = (MidiManager)getSystemService(Context.MIDI_SERVICE);
MidiDeviceInfo[] infos = midiManager.getDevices();
if (infos.length > 0)
{
    MidiDeviceInfo info = infos[0]; // 説明用に先頭の情報のみアクセスしてます

    Bundle prop = info.getProperties();

    // 製造元文字列("Roland"など)
    String manufacturer = prop.getString(MidiDeviceInfo.PROPERTY_MANUFACTURER);

    // 製品名文字列("EDIROL SD-90"など)
    String product = prop.getString(MidiDeviceInfo.PROPERTY_PRODUCT);

    // 機器名文字列("Roland EDIROL SD-90"など)
    String name = prop.getString(MidiDeviceInfo.PROPERTY_NAME);

    // バージョン文字列("1.16"など)
    String version = prop.getString(MidiDeviceInfo.PROPERTY_VERSION);

    // MIDI機器とUSB接続している場合のみインスタンスが取得できる
    UsbDevice usbDevice = (UsbDevice) prop.get(MidiDeviceInfo.PROPERTY_USB_DEVICE);

    // MIDI機器とBluetooth接続している場合のみインスタンスが取得できる
    BluetoothDevice btDevice = (BluetoothDevice) prop.get(MidiDeviceInfo.PROPERTY_BLUETOOTH_DEVICE);

    // 入力ポート数(4など)
    int numInputs = info.getInputPortCount();

    // 出力ポート数(4など)
    int numOutputs = info.getOutputPortCount();
}

 

■ MIDI機器の接続/切断/状態変化を知るには?

MidiManager#registerDeviceCallback()を呼び出し、MidiManager.DeviceCallbackインスタンスを登録することで、各種コールバックを受け取ることができます。

midiManager.registerDeviceCallback(new MidiManager.DeviceCallback() {
    @Override
    public void onDeviceAdded(MidiDeviceInfo device) {
        super.onDeviceAdded(device);
        // デバイスが接続された時に呼び出される
        Toast.makeText(MainActivity.this, "onDeviceAdded()", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onDeviceRemoved(MidiDeviceInfo device) {
        super.onDeviceRemoved(device);
        // デバイスが切断された時に呼び出される
        Toast.makeText(MainActivity.this, "onDeviceRemoved()", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onDeviceStatusChanged(MidiDeviceStatus status) {
        super.onDeviceStatusChanged(status);
        // デバイスが状態が変化した時に呼び出される
        Toast.makeText(MainActivity.this, "onDeviceStatusChanged()", Toast.LENGTH_SHORT).show();
    }
}, new Handler(Looper.getMainLooper())); // 第2引数

 

■ MIDI機器をオープンするには?

MidiManager#openDevice()を呼び出します。
第1引数には、上述したgetDevices()で取得した1つ分のMidiDeviceInfoをセットし、
第2引数には、MidiManager.OnDeviceOpenedListenerインスタンスをセットします。

midiManager.openDevice(info, new MidiManager.OnDeviceOpenedListener() {
    @Override
    public void onDeviceOpened(MidiDevice device) {
        if (device == null) {
            // オープン失敗
        } else {
            // オープン成功
            // ...引数として渡された device インスタンスを使用してMIDI機器を制御する...
        }
    }
}
, new Handler(Looper.getMainLooper())); // 第3引数

 

■ MIDIメッセージを送るには?

オープンして取得したMidiDeviceインスタンスから入力ポートを取得後、
入力ポートに対してMIDIメッセージ(バイト配列)を送信します。
以下の例では、MIDI機器から「ぽーん♩」という感じの音が鳴なります。

MidiInputPort inputPort = device.openInputPort(0);
byte[] buffer = new byte[32];
int numBytes = 0;
int channel = 3; // MIDI channels 1-16 are encoded as 0-15.
buffer[numBytes++] = (byte)(0x90 + (channel - 1)); // note on
buffer[numBytes++] = (byte)60; // pitch is middle C
buffer[numBytes++] = (byte)127; // max velocity
int offset = 0;
try {
    inputPort.send(buffer, offset, numBytes);
} catch (IOException e) {
    e.printStackTrace();
}

 

ここまでで、Android端末とMIDI機器を接続し、MIDI機器を制御することができました。
さらなる情報は、公式サイトから得られますので、参考にしてみてください。

http://developer.android.com/reference/android/media/midi/package-summary.html

Notification.png

Android M(API Level 22)から、Notification#Builder.setSmallIcon() に Icon を指定できるようになりました。

これにより、ソースコード上で「動的に描画した画像」を「通知アイコン」として表示することができるため、
従来よりも簡単に「状態によって変化するテキストや図形」などを表現することができます。

例えば、従来は、電池残量アプリで、0%〜100%のアイコン表示をするために、101枚の画像リソースを用意していましたが、
今後は、アイコンを1枚も用意することなく、同様の表現が可能となります。

 

ソースコード サンプル
// 動的に描画したビットマップからアイコンを作成
Bitmap bitmap = Bitmap.createBitmap(96, 96, Bitmap.Config.ARGB_8888);
{
    // 好きな絵を描く(ここでは適当な数字を描画)
    Canvas canvas = new Canvas(bitmap);
    canvas.drawARGB(0, 0, 0, 0xFF);
    TextPaint textPaint = new TextPaint();
    textPaint.setAntiAlias(true);
    textPaint.setColor(Color.BLACK);
    textPaint.setFakeBoldText(true);
    textPaint.setTextSize(72);
    canvas.drawText("12", 0, 72, textPaint); // サンプルなのでハードコードですが
}
Icon icon = Icon.createWithBitmap(bitmap); // 動的に描画した画像からアイコンを作成

//  通知
Notification n = new Notification.Builder(this)
        .setContentTitle("Title")
        .setContentText("Text")
//     .setSmallIcon(R.mipmap.ic_launcher)   // 従来は静的なリソースしか指定できなかった
        .setSmallIcon(icon)                   // API Level 22からIconも指定できるようになった
        .build();
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(1, n);

Android L Developer Preview、Material Design で追加された新ウィジェット(※1)である RecyclerView を使用してみました。

 

RecyclerView を使用すると、アイテムのサイズが固定長の場合に、従来よりも良いパフォーマンスが得られる(※2)ほか、

アイテムの追加、削除時などのアニメーション(※3)についても、デフォルトで利用することができます。

 

ちなみに RecyclerView のコードは、現時点(2014/7/11)では見れなかったのですが、前身のコード?っぽいのは、

sdk / sources / android-20 / android / support / v7 / widget / RecyclerView.java

にありました(約5,700行)。

(ソースコード読んで、notifyItemInserted() や notifyItemRemoved() の存在に気づくなど。。。)

 

以下、

従来の ListView & BaseAdapter で実装した場合のソースコードと、

RecyvlerView & RecyvlerView.Adapter で実装した場合のソースコードを貼っておきました。

文章で書くよりもコードを見た方が分かり易いと思いますので、コード内の番号をヒントに比較してみてください。

 

BaseAdapter(従来)のコード

public class SampleAdapter1 extends BaseAdapter {
    private LayoutInflater mLayoutInflater;
    private ArrayList<String> mDataList;

    // コンストラクタは、RecyclerView.Adapter でも変更なし
    public SampleAdapter1(Context context, ArrayList<String> dataList) {
        super();
        mLayoutInflater = LayoutInflater.from(context);
        mDataList = dataList;
    }

    // 4.RecyclerView.Adapter の場合、getItemCount() に書き換える
    @Override
    public int getCount() {
        return mDataList.size();
    }

    // RecyclerView.Adapter では不要
    @Override
    public Object getItem(int position) {
        return mDataList.get(position);
    }

    // RecyclerView.Adapter では不要
    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;
        if (convertView == null) {

            // 1.RecyclerView.Adapter の場合、この部分を onCreateViewHolder() で実装する
            // ViewHolder の引数には、インフレートしたビューを渡すように変更する
            convertView = mLayoutInflater.inflate(R.layout.list_item, parent, false);
            viewHolder = new ViewHolder();

            // 2.RecyclerView.Adapter の場合、この部分を ViewHolder のコンストラクタ内で実装する
            viewHolder.text = (TextView) convertView.findViewById(R.id.text);

            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        // 3.RecyclerView.Adapter の場合、この部分を onBindViewHolder() で実装する
        String data = (String) getItem(position);
        viewHolder.text.setText(data);

        return convertView;
    }

    static class ViewHolder {
        TextView text;
    }
}

 

RecyvlerView.Adapter に書き換えた場合のコード

public class SampleAdapter2 extends RecyclerView.Adapter<SampleAdapter2.ViewHolder> {
    private LayoutInflater mLayoutInflater;
    private ArrayList<String> mDataList;

    public SampleAdapter2(Context context, ArrayList<String> dataList) {
        super();
        mLayoutInflater = LayoutInflater.from(context);
        mDataList = dataList;
    }

    @Override
    public SampleAdapter2.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // 1
        View v = mLayoutInflater.inflate(R.layout.list_item, parent, false);
        ViewHolder viewHolder = new ViewHolder(v);
        return viewHolder;
    }

    // 4
    @Override
    public int getItemCount() {
        return mDataList.size();
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        // 3
        String data = (String) mDataList.get(position);
        holder.text.setText(data);
    }

    static class ViewHolder extends RecyclerView.ViewHolder {
        TextView text;

        public ViewHolder(View v) {
            super(v);
            // 2
            text = (TextView) v.findViewById(R.id.text);
        }
    }
}

 

ListView(従来)のコード

ListView listView = (ListView) view.findViewById(R.id.listview);

// アダプターの設定(mDataList は、ここでは ArrayList<String>)
listView.setAdapter(new SampleAdapter1(getActivity(), mDataList));

 

RecyvlerView に書き換えた場合のコード

RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recyclerview);

// コンテキスト:Activity内ではthis、Fragment内ではgetActivity()など
recyclerView.setLayoutManager(new LinearLayoutManager(コンテキスト));
recyclerView.setHasFixedSize(true); // アイテムは固定サイズ

// アダプターの設定(mDataList は、ここでは ArrayList<String>)
recyclerView.setAdapter(new SampleAdapter2(getActivity(), mDataList));

 

ListView(従来)のリソース

<ListView
    android:id="@+id/listview"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

 

RecyvlerView に書き換えた場合のリソース

<android.support.v7.widget.RecyclerView
    android:id="@+id/recyclerview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="vertical" />

 

RecyvlerView.Adapter でデフォルトアニメーションを使用する例

// アイテム追加後、notifyItemInserted(index)を呼び出す(ここでは最後に追加)
recyclerView.getAdapter().notifyItemInserted(mDataList.size() - 1);

// アイテム削除後、notifyItemRemoved(index)を呼び出す(ここでは先頭を削除)
recyclerView.getAdapter().notifyItemRemoved(0);

 

備考

※1:N5だと、従来のListView と動作比較してみたところ、目に見えてわかるような差は体感できませんでした

※2:Material Design の追加ウィジェットは2つだけで、もう一方は以前紹介した CardView

※3:アニメーションは RecyclerView.ItemAnimator で自作したものへ置き換え可能

Android L(API Level 21)のViewAnimationUtils.createCircularRevealメソッドを使用すると、

ビューに対して広がる(/またはその逆の)円状のクリッピングを適用したアニメーション効果を得る事ができます(表現困難...)。

 

サンプル動画

※画面右端中央やや下のMapアイコンをタップしてビューを切り替えています

 

切り替え前後のビュー(参考)

before.png after.png

 

サンプルソースコード

// Revealアニメーションを適用したいビュー
final View animationTarget = findViewById(R.id.information_container);

int cx = (view.getLeft() + view.getRight()) / 2;
int cy = (view.getTop() + view.getBottom()) / 2;
float radius = Math.max(animationTarget.getWidth(), animationTarget.getHeight()) * 2.0f;

if (animationTarget.getVisibility() == View.INVISIBLE) {
    // 切り替えたいビューが非表示の場合(最初は、こっちを通る)

    // これから表示するビューを表示する(アニメーション開始時には円の半径が0なので実質見えない)
    animationTarget.setVisibility(View.VISIBLE);

    // 円状のクリッピングを適用したアニメーションを開始する
    // 円は(数百ミリ秒で)あっという間に大きくなります
    ViewAnimationUtils.createCircularReveal(
            animationTarget, // アニメーション対象のビュー(これから表示したいビュー)
            cx, cy, // アニメーションの原点(=円の中心)
            0,      // アニメーション開始時の円の半径
            radius  // アニメーション終了時の円の半径
    ).start(); // アニメーション開始
} else {
    // 切り替えたいビューが表示済の場合

    // 円状のクリッピングを適用したアニメーションを開始する
    // 円は(数百ミリ秒で)あっという間に小さくなります
    ValueAnimator reveal = ViewAnimationUtils.createCircularReveal(
            animationTarget, // アニメーション対象のビュー(これから表示したいビュー)
            cx, cy, // アニメーションの原点(=円の中心)
            radius, // アニメーション開始時の円の半径
            0       // アニメーション終了時の円の半径
    );
    // アニメーション終了時に呼び出されるリスナを設定
    reveal.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            // アニメーション終了時に対象のビューを非表示にする
            animationTarget.setVisibility(View.INVISIBLE);
        }
    });
    reveal.start(); // アニメーション開始
}

※デモアプリのソースコードは、https://github.com/romainguy/google-io-2014

 

Android L(API Level 21)のandroid.support.v7.graphics.Palette クラスを使用すると、指定した画像から、画像に使われている色を解析し、いい感じのパレット(6色分の色値)を計算してくれるようです。

取得した色をテキストやグレーアイコンに適用することで、画像にマッチした配色ができそうです(例えば、下図のフラミンゴだと、フラミンゴの色から、ピンク、赤、暗めの紺色の色などが Palette クラスから返される)。

 

Palette 呼び出し方

Palette palette = Palette.generate(photo); // photo は Bitmap

// get○○○Color()で任意のパレット色が取得できる

// VibrantColor
palette.getLightVibrantColor().getRgb();    // 明るい
palette.getVibrantColor().getRgb();
palette.getDarkVibrantColor().getRgb();     // 暗い

// MutedColor
palette.getLightMutedColor().getRgb();      // 明るい
palette.getMutedColor().getRgb();
palette.getDarkMutedColor().getRgb();       // 暗い

 

Google I/O 2014 Material Witness で紹介されていたデモアプリ

1_PaciflcaPier.PNG 2_PinkFlamingo.PNG

3_AntelopeCanyon.PNG 4_LonePine.PNG

※デモアプリのソースコードは、https://github.com/romainguy/google-io-2014

※キャプチャは、取得できるパレット色の名前と値を表示するように手を加えています

 

どんな風に色を選択しているか興味深く、Android L のソースコード公開が待ち遠しいです。

Android L Developer Preview で追加された UI 部品である CardView を試してみました。

 

動画

カードのふちが角丸(影つき)になっているのが見えますかね。。。

丸みは cardCornerRadius で指定するようです。

 

サンプルリソース

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
    android:padding="8dp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.CardView
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:layout_gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardCornerRadius="8dp">

        <ImageView
            android:id="@+id/image"
            android:layout_width="match_parent"
            android:layout_height="280dp"
            android:scaleType="centerCrop" />

    </android.support.v7.widget.CardView>

</LinearLayout>

 

あれっ???

角が丸いだけの View ???

 

(そうだ、yanzm 先生の Android Pattern Cookbook に載ってたカードビューをみてみよう)

 

Android Pattern Cookbook に載ってたカードビューのリソース

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="16dp" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/card_item_bg2"
        android:orientation="vertical" >

        <ImageView
            android:id="@+id/image"
            android:layout_width="match_parent"
            android:layout_height="@dimen/thumbnail_height"
            android:contentDescription="@string/thumbnail"
            android:scaleType="centerCrop" />

    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="4dp"
        android:background="@drawable/card_shadow" />

</LinearLayout>

あれっ???

yanzm せんせいのとコード量、ほとんど変わらないw

CardView さん...

きっと、ボクが何か勘違いしているに違いない...

 

あわせて読みたい

Android Support Library, revision 19.1.0SwipeRefreshLayout が追加されました。

SwipeRefreshLayout とは、下方向のスワイプジェスチャーを検出してプログレスを表示するためのもので、

アプリの情報更新手段として使用できます。

類似の機能は、これまでは各アプリ独自に実装していたと思いますが、Support Library を使用することで、簡単に使用できます。

※2014/3/29追記:Effective Android

 「第4章 引っ張って更新するPullToRefresh UIを使ったListViewを作る」の方がおすすめだよ(みんなには内緒だよ!)

 

SwipeRefreshLayout

 

使い方

1. SwipeRefreshLayout#setOnRefreshListener() を呼び出し、スワイプ時のリスナを登録する

2. スワイプ時に呼び出されるリスナ SwipeRefreshLayout.OnRefreshListener#onRefresh() で更新中の処理を行う

3. 更新後、SwipeRefreshLayout#setRefreshing(false) でプログレスを終了させる

 

※a. SwipeRefreshLayout#setRefreshing(true) を呼び出す事で、メニューアイテム押下時など、

 スワイプ以外の任意のトリガでもプログレスを開始できる

※b. SwipeRefreshLayout#setColorScheme() を呼び出す事で、プログレスの色もカスタマイズできます

 

サンプル

サンプルアプリは、sdk / extras / android / support / samples / Support4Demos にありました(上記動画)。

以下、Support4Demos 内のソースコード(Apache License, Version 2.0)の抜粋です。

 

サンプルリソース(swipe_refresh_widget_sample.xml)

<android.support.v4.widget.SwipeRefreshLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swipe_refresh_widget"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/content"/>
</android.support.v4.widget.SwipeRefreshLayout>

 

サンプルソースコード(SwipeRefreshLayoutActivity.java)

public class SwipeRefreshLayoutActivity extends Activity implements OnRefreshListener {
    public static final String[] TITLES =
    {
            "Henry IV (1)",
            "Henry V",
・・・省略・・・
             "Othello",
            "King Lear"
    };
    private SwipeRefreshLayout mSwipeRefreshWidget;
    private ListView mList;
    private Handler mHandler = new Handler();
    private final Runnable mRefreshDone = new Runnable() {

        @Override
        public void run() {
            // 3. プログレスを終了させる
            mSwipeRefreshWidget.setRefreshing(false);
        }

    };
    @Override
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(R.layout.swipe_refresh_widget_sample);
        mSwipeRefreshWidget = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_widget);
        mSwipeRefreshWidget.setColorScheme(R.color.color1, R.color.color2, R.color.color3,
                R.color.color4);
        mList = (ListView) findViewById(R.id.content);
        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, android.R.id.text1, TITLES);
        mList.setAdapter(arrayAdapter);
        // 1. スワイプ時のリスナを登録する(implements OnRefreshListener)
        mSwipeRefreshWidget.setOnRefreshListener(this);
    }

    @Override
    public void onRefresh() {
        // 2. スワイプ時に呼び出される、ここで更新中の処理を行う
        refresh();
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.swipe_refresh_menu, menu);
        return true;
    }

    /**
     * Click handler for the menu item to force a refresh.
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        final int id = item.getItemId();
        switch(id) {
            case R.id.force_refresh:
                // a. メニューアイテム押下時など、スワイプ以外の任意のトリガでもプログレスを開始できる
                mSwipeRefreshWidget.setRefreshing(true);
                refresh();
                return true;
        }
        return false;
    }

    private void refresh() {
        // このアプリでは更新処理のダミーとして1秒後に更新終了処理を呼び出している
        mHandler.removeCallbacks(mRefreshDone);
        mHandler.postDelayed(mRefreshDone, 1000); // 3000にすると3秒間プログレスが表示されるよ
    }
}

 

あわせて読みたい

cookbooks.JPG

インプレスジャパン様から『Android Pattern Cookbook』をちょうだいいたしましたので紹介させていただきます。

 

Android Cookbook シリーズ第三弾となる今回は、待望のフルカラー、288ページで、本日 2014年3月20日(木)発売!

Navigation Drawer から カスタムビュー まで、いま流行の UI を実装する方法が解説されています。

1.2-2s.PNG 1.5-2s.PNG 2.6s.PNG 7.1s.PNG

特に第7章は、

  • カードUI
  • ショーケース
  • スワイプオフできるリスト
  • ヘッダーが残るリスト
  • スクロール位置に応じて透明度が変わるActionBar
  • FastScroll + インデックスラベル
  • ログイン画面
  • オリジナルのプログレス
  • パターンロック
  • ドラッグ&ドロップコンテキストメニュー
  • 下にスワイプして更新

など、他では読む事ができない貴重な情報が詰まっており、非常にお買い得な内容になっていると思います。

この本を読めば、長らく Android 開発とはご無沙汰状態なワタクシでも、

最近の Google+、Facebook、Feedly、Gmail のような UI を持つアプリを実装できそうです\^^/

 

サンプルアプリ

AndroidPatternCookbook.PNG

本書のデモアプリは、以下のリンク先 (Google Play)で公開されています。

https://play.google.com/store/apps/details?id=yanzm.products.patterncookbook

以下にデモアプリ「第4章 ViewPager」の一部を録画しましたので、参考にしてみてください^^

 

サンプルソースコード

本書で解説されているソースコードは、(近いうちに?)インプレスさんサイトからダウンロードできます。

以下は、ActionBar の背景を(コードで)グラデーションするリソースの抜粋。

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <gradient
        android:angle="270"
        android:endColor="@color/main_color_dark"
        android:startColor="@color/main_color_dark"
        android:type="linear" />
</shape>
action_grad.png

 

おわりに

やんざむさん曰く、Android Pattern Cookbook は、書籍制作にあたり、Re:VIEW を使用されているそうです。

Re:VIEW による美しい仕上がり、、、凄い!凄いよ、ほむらちゃん!

また、2014年3月21日(金)には、Android Bazaar and Conference 2014 Spring が開催されます。

当日は、『Android Pattern Cookbook』だけでなく、てくぶの『Effective Android』、

のぶさとさん監修の『Smashing Android UI』など、Android関連書籍の販売会もあるようですので、

当日参加される方は、バザールのインプレスさんブース(え3)で書籍を手に取って確認してみてください!

(うわさでは、サイン会もある!?かも。。。裏山。。。)

 

あわせて読みたい

 

※本エントリの内容は、2014年3月20日時点のものです

cover.png

Android 界の(元)Flappy Bird 王の adakoda です。

Android 界の シゲちゃんこと「しげむら こうじ」さんから、

Android SDK ポケットリファレンス』本をちょうだいいたしましたので紹介させていただきます。

 

書籍の情報(技術評論社)

  • 書籍案内・・・概要/目次
  • サポートページ・・・サンプルコードダウンロード(Gitがわからない初心者の方でもzipで一括DL可)
  • サンプルソース・・・GitHubソースコード
  • 対象読者=初級者〜中級者(中級向けの内容は ActionBar 、support.v○ 系、Google Map v2など)
  • ページ数・・・約500ページくらい

 

書籍の内容

技術評論社のポケットリファレンス本ということで、

やりたいこと(目的)から APIの使い方を見つけられる「逆引き系」の書籍となっています。

各目的毎の解説については、1〜数ページで構成されているため、必要なページ/興味あるページから読み進めることができます。

ソースコードの説明については、紙面の都合上、必要な部分の抜粋となっていますが、

すべてのソースコードは、インポート可能なプロジェクト形式で公開されているため、自分で実行して試すことができます。

 

本書を読むべき方

書籍の内容を読んでみた印象としては、「これから Android アプリの開発を初めてみたい!」

という方が一番フィットするのではないか思いました(※個人の感想です)。

使い方としては、以下の流れが良いと思います。

1. 気になる機能の解説に該当するページを斜め読みする

2. 該当のソースコードをダウンロード、実行し、動作を確認する

3. もういちど解説ページの詳細を読んで、値を変更するなど、ソースコードを変更し、変化を確認する

 

サンプルソース紹介

例えば、オーバースクロール時に何かをしたい時(ひっぱって情報の更新とか)のソースコードは、

shige0501 / android-sdk-pokeri / Chapter03 / ui_OverScroll

にあります。

※以下のソースコードは、公開されているソースコードの引用です

package net.buildbox.pokeri.ui_overscroll;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.ListView;

public class OverScrollView extends ListView {
	private static final String TAG = "OverScrollView";
	
	public OverScrollView(Context context) {
		super(context);
		
		// オーバースクロールモードの設定
		setOverScrollMode(OVER_SCROLL_ALWAYS);
	}
	
	public OverScrollView(Context context, AttributeSet attrs) {
		super(context, attrs);

		// オーバースクロールモードの設定
		setOverScrollMode(OVER_SCROLL_ALWAYS);
	}
	
	// オーバースクロールとしてonOverScrolledを呼び出す条件を指定
	@Override
	protected boolean overScrollBy(int deltaX, int deltaY, int scrollX,
			int scrollY, int scrollRangeX, int scrollRangeY,
			int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
		return super.overScrollBy(0, deltaY, 0, scrollY, 0,
				scrollRangeY, 0, 100, isTouchEvent);
	}
	
	// オーバースクロール時の処理
	@Override
	protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
			boolean clampedY) {
		// オーバースクロールの情報のデバッグ出力
		Log.d(TAG, "call onOverScrolled(): "
			+ "scrollX=" + scrollX
			+ ", scrollY=" + scrollY
			+ ", clampedX=" + clampedX
			+ ", clampedY=" + clampedY);
		super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
	}
}

 

おわりに

単著って大変ですよね。。。500ページとかレビューアも死ぬと思います。。。

Fragment のあたりは、さすがに少ないページ量だとちょっと説明が少なかったかも^^;。。。

ということで、次回は上級者向けお願いします^^;

ブログが「かけなび化」してるにも関わらず本を送っていただきありがとうございましたm_ _m

 

あわせて読みたい

1234567891011

2015年8月

            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31