ウオッチフェイスの デザイン例 にある「Applying an aesthetic change」の動きをする対話型ウォッチフェイスを作ってみました。
簡単に言うと、タップする毎に背景が切り替わるものです。

watchface_anim.gif

 

この例では、タップ時に、プロパティーアニメーションを使用して、滑らかに色を変化させながら背景を切り替えています。
サンプルとして単純化するために、単色で塗りつぶしているだけですが、
タップする毎に背景画像が切り替わる対話型ウォッチフェイスを作ることができるとも言えます。

 

独自のプロパティーをアニメーションさせる

ObjectAnimator は、ビューの  "x" や "y"  などの組み込みプロパティーをターゲットにして使用しますが、
今回のサンプルコードのように View 以外の任意のクラスに対しても適用できます。
この例では、独自のプロパティーとして、"BGColor" を追加しました。

独自のプロパティーを追加させるには、プロパティー名に m をつけた変数と、
その変数への Get() / Set() のアクセッサを実装します。

 

色をアニメーションさせる

色と色を滑らかに変化させるには ArgbEvaluator クラスを使用します。

 

サンプルコード

private class Engine extends CanvasWatchFaceService.Engine {
    
    // 背景色の配列
    final int[] BG_COLORS = {
            Color.rgb(0x30, 0xa0, 0x40),    // グリーン
            Color.rgb(0xf0, 0x70, 0xa0),    // ピンク
            Color.rgb(0xe0, 0x20, 0x20),    // レッド
            Color.rgb(0xf0, 0xe0, 0x00),    // イエロー
            Color.rgb(0x70, 0x20, 0x70),    // パープル
    };

    // BG_COLORS配列内の現在のインデックス
    int mBGColorIndex = 0;

    // 独自のプロパティー ----->

    // 背景色プロパティー
    private int mBGColor = BG_COLORS[mBGColorIndex];

    // 背景色プロパティー取得
    public int getBGColor() {
        return mBGColor;
    }

    // 背景色プロパティー設定
    public void setBGColor(int bgColor) {
        mBGColor = bgColor;
        // 再描画
        invalidate();
    }

    // 独自のプロパティー -----<

    // アニメーター
    ObjectAnimator mAnimator;

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

        // タップ押下時のみ処理
        if (tapType == TAP_TYPE_TOUCH) {
            if (mAnimator != null) {
                // アニメーションが動作中であればキャンセル
                if (mAnimator.isRunning()) {
                    mAnimator.cancel();
                }
                mAnimator = null;
            }
            if (mAnimator == null) {
                // 次の背景色用のインデックス計算
                int nextBgColorIndex = (mBGColorIndex + 1) % BG_COLORS.length;
                // アニメーターオブジェクト生成
                mAnimator = ObjectAnimator.ofObject(this,
                        "BGColor", // 独自のプロパティー
                        new ArgbEvaluator(),
                        BG_COLORS[mBGColorIndex],
                        BG_COLORS[nextBgColorIndex]);
                // アニメーション期間(ミリ秒)設定
                mAnimator.setDuration(500).start();
                mBGColorIndex = nextBgColorIndex;
            }
            // 再描画
            invalidate();
        }
    }

    @Override
    public void onDraw(Canvas canvas, Rect bounds) {
        // 背景色描画
        canvas.drawColor(mBGColor);
        
        // ...省略
    }

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 Wear でも screenrecord コマンドが使えました。

Google Play など、プロモーション動画としても使えそうですね。

 

動画キャプチャー方法
adb shell screenrecord --time-limit 30 --o raw-frames --verbose /sdcard/test.raw

adb pull /sdcard/test.raw test.raw

ffmpeg -f rawvideo -vcodec rawvideo -s 320x320 \
-pix_fmt rgb24 -r 10 -i test.raw  -an -c:v libx264 -pix_fmt yuv420p test.mp4

 

試しに いくつかの Watch Face をキャプチャしてみました。

 

参考

screenshot_320.png

Chromeの「プロ生ちゃんのテーマ」をつくったよ。

ストアへの登録に 5$ とられたよ。

でも、テーマは無料だよ。

Macでしか確認してないよ。

Windows環境だと、ちょっと変な色でるらしいよ(汗。 →→→ 1.0.1 で修正しました。

 

ダウンロード

Pronama chan santa - Chromeウェブストア

 

テーマを戻すには

テーマを戻すには、Chrome menu (右上の三本線アイコン)をクリックして、

[設定] > [デザイン] > [デフォルトのテーマに戻す] から行ってください。

 

アップデートをするには

古いバージョンがインストールされた状態で、新しいバージョンにアップデートする場合は、

一度、上記手順でデフォルトテーマに戻し、再度インストールしてください。

 

不具合報告/改善要望

不具合報告、改善要望は Twitter アカウントまで頂けると有り難いです m_ _m

※ディスプレイによっては、ブックマークバーの彩度が高すぎて見づらいかもしれません><

 

更新履歴

- 1.0.2 : 自動更新に対応(したかも?、1.0.2をインストール後、1.0.3以降でわかるので (^^;)

- 1.0.1 : Windowsでフレームとブックマークバーの配色がデフォルトになる不具合を修正

- 1.0.0 : 新規作成

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 さん...

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

 

あわせて読みたい

1234567891011

2015年9月

    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