基調講演

 

Android

 

Android Studio

 

Google Glass

 

Google Play Store

 

Google Play Game

 

Google Play Music

 

Goolge Play Books

 

Google Map

 

Google Compute Engine

 

Google App Engine

 

Google+ Hangout

 

Google Wallet

 

Chrome Packaged Apps

 

Search

 

YouTube

 

Dart

 

その他

 

※リンク切れ、間違い、重複等あれば @adakoda までご連絡くだされ(I/O報告会まではメンテするかも)

※2013/5/21, 5/22, 5/23, 5/26更新

※同カテゴリ内で新しいエントリを下に追加しています

Ubuntu Touch Developer Preview and SDK Alpha を使用してサンプルアプリをビルドし、

Galaxy Nexus 上のデバイスで動作させてみました。

ubuntu_phone_sdk.png

 

動作確認環境

  • Galaxy Nexus + Ubuntu 12.10 Touch Preview
  • Ubuntu 12.10 desktop 64bit + Ubuntu SDK preview

 

以下、試した手順です。

 

Galaxy Nexus (デバイス)上に Ubuntu 12.10 Touch Preview をインストールする

1. 以下の公式サイトから対象デバイスのプレインストールイメージをダウンロードする。

※Galaxy Nexus 向けは、以下の2ファイル

    • quantal-preinstalled-armel+maguro.zip (48MB)
    • quantal-preinstalled-phablet-armhf.zip (481MB)

2. 任意の方法でデバイス(Galaxy Nexusなど)にイメージを書き込む。

※ワタクシはrecovery-clockwork-5.5.0.2-maguro.img を使用して上記2ファイルを書き込みました

 

Ubuntu (PC) 上に Ubuntu SDK Preview をインストールする

1. 以下の公式サイトの (1) Get the Ubuntu SDK preview の手順を実行し、必要なSDKをインストールする 。

※以下、インストール手順の抜粋(表示幅の都合上コマンドを分割して記載しています)

sudo add-apt-repository ppa:canonical-qt5-edgers/qt5-proper

sudo add-apt-repository ppa:ubuntu-sdk-team/ppa

sudo apt-get update

sudo apt-get install ubuntu-sdk notepad-qml

2. インストール後、Qt Creator がエラー画面なしに起動できることを確認する。

※ワタクシの環境では起動時に エラー画面 が表示されたため、以下もインストールしました

sudo apt-get install libbotan1.10-dev

 

Ubuntu UI サンプルアプリを作成する

1. Qt Creator を起動する。

2. Ctrl + N > Ubuntu > Ubuntu UI - Simple で新規プロジェクトを作成する。

3. Ctrl + R を押下し、PC上でアプリが起動することを確認する。

desktop_app.png

 

Ubuntu (PC) から Galaxy Nexus (デバイス)上へアプリを転送・実行する

1. Ubuntu Phone (デバイス)のネットワークを有効にする。

2. Qt Creator  の [Tools] メニュー > [Ubuntu] > [Device]  > [Enable] > [Developer Connection]を選択し、

 デバイス上で追加のパッケージをインストールさせる。

3. Qt Creator の左側のメニューボタンで Devices を選択し、

 (画面右上の方にある)Detect Device を押下し、デバイスを認識させる。

4. Ctrl + F12 を押下し、アプリをデバイスに転送&実行する。

ubuntu_phone_sample_app.png

 

以上です。

 

あわせて読みたい

Firefox OS 端末の「電源」ボタンと「ホーム」ボタンを同時に押すと、画面キャプチャを取得することができます。

取得した画面キャプチャは、端末の /sdcard/screenshots 下に png ファイルが作成されます。

※Firefox OS Alpha2 で確認

 

Firefox OS では、(今のところ?)adb が動作するようなので、Android Screen Monitor (ASM) も対応させました。

ASM の新しいバージョン(2.40)は、以下のリンク先のページ内からダウンロードできます。

http://www.adakoda.com/adakoda/android/asm/

 

起動後、デバイスを接続した状態でデバイスを選択し、

ASM の画面上でコンテキストメニューを表示させ(画面を右クリック)、

「FrameBuffer」 > 「XRGB」 を選択すると、フレームバッファの色を読み替えて動作します。

 

画面キャプチャ

  • adb、または、ASM 2.30 で取得した画像(赤と青が逆転している)

 

  • ASM 2.40 で取得した画像

after2.png

 

あわせて読みたい

Android Advent Calendar 2012 12月12日(表)担当の adakoda です。

本エントリでは、Android SDK Tools Rev.21 でサポートされた新しい UI テストフレームワークである uiautomator を使用し、

Android アプリケーションの UI テストケースを作成、実行する方法について解説し、

サンプルテストコードとして 「Android 標準の電卓アプリ上で足し算の結果を確認する」 テストケースを作成します。

※本エントリの内容は、「Android SDK Tools, Revision 21(21.0.1 rc1)」  「Android SDK Platform, API 17 (Android 4.2)」を対象とした内容となっています

 

uiautomator ツール

◆ テストプロジェクトを作成する

Eclipse 上で新規に「Java Project」を作成し、JUnit3、uiautomator.jar、android.jar ライブラリを追加することで作成できます。

【手順】

1. Eclipse を起動し、[File] > [New] > [Java Project] をクリックする。

2. [Create a Java Project] 画面で、[Project name] に作成したいプロジェクト名を入力し、[Next] をクリック。

3. [Java Settings] 画面、[Libraries] タブで [Add Library...] をクリック、

    [JUnit] が選択された状態で [Next] をクリック、[JUnit3] が選択された状態で [Finish] をクリック。

4. 同 [Java Settings] 画面、[Libraries] タブで [Add External JARs...] をクリック、

    インストールした Android SDK ディレクトリの platforms\android-17 下にある android.jar、uiautomator.jar を選択。

5. [Finish] をクリックすると、作成完了。

 

◆ テストクラスを作成する

UiAutomatorTestCase クラスを継承したクラスを作成し、テストメソッドを作成します。

※UiAutomatorTestCase クラスは、junit.framework.TestCase を継承したクラス

※具体的なテストクラスは、後述するサンプルテストコードで解説します

 

◆ ant を使用してビルドする

作成したプロジェクトを ant を使用してビルドするには、android コマンドを使用し、ant ビルドに必要なファイルを作成し、ビルドします。

【手順】

1. コマンドプロンプト/ターミナル上で 「android create uitest-project -n <name> -t <target> -p <path>」を実行する。

  ※<name> には、プロジェクト名 を指定する

  ※<target> には、「android list target」で表示される対象 API の Target ID を指定する

  ※<path> には、対象プロジェクトのパス を指定する

  例) 「android create uitest-project -n UIAutomatorSample -t 20 -p .」

2. プロジェクト下に「build.xml」ファイルが生成されていることを確認し、同ディレクトリで「ant build」を実行する。

 

◆ ビルドしたファイルを使用してテストを実行する

ビルドしてできた jar ファイルを adb コマンドで Android 端末に転送し、uiautomator ツールを使用してテストを実行します。

【手順】

1. 「adb push <ビルドして生成されたjarファイル> <転送先ディレクトリ>」を実行し、jar ファイルを Android 端末に転送します。

  例) 「adb push .\bin\UIAutomatorSample.jar /data/local/tmp」

2. 「adb shell uiautomator runtest <ビルドして生成されたjarファイル> -c <テストクラス>」を実行し、テストを実行する。

  例) 「adb shell uiautomator runtest UIAutomatorSample.jar  -c com.adakoda.android.uiautomatorsample.CalculatorTest」

 

◆ サンプルテストケース

ここでは、Android 標準の電卓アプリ上で足し算の結果を確認するテストケースを作成します。

テストのシナリオは、以下のとおりです。

【テストのシナリオ】

1. ホームボタンを押してアプリケーションランチャーを表示する。

2. アプリケーションランチャーから電卓アプリを起動する。

3. 電卓アプリの消去ボタンを長押しして、結果をクリアする。

4. 電卓アプリで「 1 」「 + 」「 2 」「 = 」ボタンをクリックし、結果が「 3 」であることを確認する。

 

◆ サンプルテストコード

package com.adakoda.android.uiautomatorsample;

import android.widget.TextView;

import com.android.uiautomator.core.UiObject;
import com.android.uiautomator.core.UiObjectNotFoundException;
import com.android.uiautomator.core.UiScrollable;
import com.android.uiautomator.core.UiSelector;
import com.android.uiautomator.testrunner.UiAutomatorTestCase;

public class CalculatorTest extends UiAutomatorTestCase {

    public void testAddition() throws UiObjectNotFoundException {
        // 1. ホームボタンを押してアプリケーションランチャーを表示する。
        getUiDevice().pressHome();

        // 2. アプリケーションランチャーから電卓アプリを起動する。
        UiObject allAppsButton = new UiObject(new UiSelector().description("アプリ"));
        allAppsButton.clickAndWaitForNewWindow();
        UiObject appsTab = new UiObject(new UiSelector().text("アプリ"));
        appsTab.click();

        // スワイプしながらアイテムを検索できるUiScrollableインスタンスを作成
        UiScrollable appViews = new UiScrollable(new UiSelector().scrollable(true));
        // スワイプ時のスクロール方向を水平方向に設定
        appViews.setAsHorizontalList();

        // アプリランチャーの子ビューから以下の条件を満たすUIオブジェクトを取得
        // 「クラス名が"android.widget.TextView"」、かつ、「テキストが"電卓"」
        UiObject targetApp = appViews.getChildByText(
                new UiSelector().className(TextView.class.getName()),
                "電卓");
        // 見つかったUIオブジェクト(ここでは電卓アプリアイコン)をクリックする
        targetApp.clickAndWaitForNewWindow();

        // 指定したパッケージ名のアプリの存在を確認する
        UiObject appValidation = new UiObject(
                new UiSelector().packageName("com.android.calculator2"));
        assertTrue(appValidation.exists());

        // 3.電卓アプリの"削除"ボタンを長押しして、結果をクリアする。
        UiObject buttonClear = new UiObject(new UiSelector().text("削除"));
        buttonClear.longClick(); // 長押し

        // 4. 電卓アプリで「 1 」「 + 」「 2 」「 = 」ボタンをクリックし、結果が「 3 」であることを確認する。

        // "1"ボタンを押す
        UiObject button1 = new UiObject(new UiSelector().text("1"));
        button1.click();
        // "+"ボタンを押す
        UiObject buttonPlus = new UiObject(new UiSelector().text("+"));
        buttonPlus.click();
        // "2"ボタンを押す
        UiObject button2 = new UiObject(new UiSelector().text("2"));
        button2.click();
        // "="ボタンを押す
        UiObject buttonEqual = new UiObject(new UiSelector().text("="));
        buttonEqual.click();

        // エディットテキストの値を取得する
        UiObject editText = new UiObject(new UiSelector().className("android.widget.EditText"));
        String textEditText = editText.getText();

        // 結果を確認する
        assertEquals(String.valueOf("3"), textEditText);
    }

}

 

◆ テスト実行結果(画面遷移)

上記テストコードを実行すると、以下の画面が自動的に遷移します。

home.png

0. テストを実行する前のホーム画面。

 

app1.png

1. ホームボタンを押し、アプリケーションランチャーが起動した画面。

 

app2.png

2. 自動的に水平スワイプが実行され、"電卓"というテキストのビューを探している画面。

 

calc1.png

3. 電卓アプリを起動し、削除ボタンを長押ししている画面。

 

calc2.pngcalc3.png

4. 「1」「+」「2」「=」ボタンを押して、「3」という結果となっていることを確認している画面。

 

uiautomatorviewer ツール

◆ UI コンポーネントのスキャンと解析を行う

uiautomator では、UiSelector クラスを使用して操作対象の UI を特定するのですが、

このための UI 解析ツールとして、uiautomatorviewer ツールが提供されています。

ツールを起動し、画面キャプチャ用のアイコン(左から2番目)を押すと、解析結果が表示され、

任意のビューの情報を表示させることができます。

 ※Windows 版は、Rev21.の bat ファイルに不具合があるため、21.0.1 rc1 でないと正常に動作しません

uiautomatorviewer.png

 

おわりに

uiautomator テストフレームワークは、まだまだ開発中のツールと思われるため、

本エントリで紹介した内容は、将来変更される可能性があります。

特に、ant ビルドの部分は、uibuild.xml ファイル内に <!-- todo: fix this --> と書かれているとおり、

ant test コマンドなどでもっと簡単にテストを実行できるようになると思われます^^;

 

◆ 参考リソース

 

◆ あわせて読みたい(Android Advent Calendar 2012 のテスト関連のエントリ)

WindowsPhone8Dev.png

ようやく Windows Phone 8 アプリの開発環境を構築しました^^;

 

Windows Phone 8 アプリの開発環境としては、以下のシステム要件を満たす必要があります((((;゜Д゜)))

 

システム要件

  • Windows 8 64 ビット (x64) クライアント バージョン
  • ハードウェア:
    • 4 GB のハード ディスク空き容量
    • 4 GB の RAM
    • 64 ビット (x64) CPU
  • Windows Phone 8 Emulator:
    • Windows 8 Pro エディション以上
    • Second Level Address Translation (SLAT) をサポートするプロセッサが必要

 

開発環境の構築自体は、とても簡単です。

Windows Phone SDK 8.0 をダウンロードし、インストールするだけです。

 

Windows Phone SDK 8.0

※赤いダウンロードボタンを押すとネットワークインストールが開始されますが、
 ページ中央より下にある .ISO 版のリンクで丸ごとダウンロードするのがお勧め

 

インストールが完了すると、統合開発環境である Visual Studio 2012 や Emulaotr などがインストールされます。

 

アプリケーションを作成する

SDK のインストールが完了したら、IDE である VC 2012 Express を起動し、

「ファイル」 メニューから、「新しいプロジェクト」 「Windowss Phone アプリ」を選択すると、

ひな形のソリューションが作成されます。

 

エミュレータ上でアプリを実行する

続けて、「Emulator WVGA 512MB(JA)」などのボタンを押すと、

作成したアプリがエミュレータ上で実行されます。

 

もしも、仮想環境上でエミュレータの実行が上手くいかなった場合には、以下のブログを参考にしてみてください。

ワタクシの環境(VMWare Player 5.0.0(ホスト:Windows 7 Pro 64ビット、ゲスト:Windows 8 Pro 64ビット))でも同じ対処方法で動作しました。

 

あわせて読みたい

先週くらいに、Twitter の TL 上で見たこともない TOEIC の点数が流れていましたので^^;

西野さんから献本していただいた書籍をご紹介。

 

『アプリケーションをつくる英語』

  • 発売日:2012/09/21発売
  • ページ数:328P
  • サイズ・判型:B5変型判
  • 著者:西野 竜太郎 著
  • ISBNコード:978-4-8443-3284-8
  • 定価:2,400円+税
  • URL:http://www.impressjapan.jp/books/3284

 

内容

  • 第1部 よく使う単語

辞書形式で単語の意味や例文、解説が書かれています。

通常の辞書と違って、エラーやヘルプなど、アプリ開発で必ず使う単語が、

分類別(1章から16章)にまとめられているので、効率的に探すことができます。

 

  • 第2部 構文パターン

「できます(可能)」「できません(不可能)」など、よく使う構文(※)の例文、解説が書かれています。

※「可能」「不可能」「可能性」「完了」「未完了」「成功・失敗」「現在」「予告」
 「指示」「推奨」「禁止」「質問・確認」「条件」「目的」「理由「」有無」「提示」

アプリケーションは、必ず何かしらのメッセージを表示することになるのですが、

ほとんどのものは、ここに書かれている構文で表現できるんじゃないかと思います^^

 

  • 第3部 英語ライティングとグローバリゼーション

基本的な英文法の解説から始まり、

「エリプシス」「キャプタリゼーション」などのUIメッセージの書き方、

アプリケーションを国際化する際の注意点、UI関連のガイドラインへのリンクなど、役に立つ資料が書かれています。

アプリケーション開発者には是非読んでほしい内容だと思います。

 

電子書籍版

電子書籍版は、達人出版会から購入できます。

http://tatsu-zine.com/books/english4app 

 「まず電書で、次に紙で」 という流れはいいですね。

まさに希望の光。

 

最後に

最近では、様々なプラットフォーム上で、開発したアプリケーションを簡単に全世界に公開できるようになっており、

英語対応は必須になっています。

本書は、まさに、そんな私たち開発者にとって必須の本だと思います。

開発者の時給を考えればなおさら。。。

 

 ところで、この値段で、ここまでノウハウを出して大丈夫なの^^;

日本最大級の開発コンテスト「MA8」の連動イベント、信州MashupCaravan&Meetup with GDG信州でライブコーディングしてきました。

 

内容としては、持ち時間15分内に、協賛企業の(REST)APIを使用して制限時間内にAndroidアプリを作るというもの。

 

結果は・・・

 

アプリは何とか完成したものの、持ち時間3~5分オーバー、

途中で時間が足りないことに気づいていたので、少し機能も減らしました。。。><

 

想定外だったのが、立ってタイピングしたせいか、両腕が安定しなくて、タイポを連発しまくったこと。

緊張はしてなかったのですが、手元は正直で、まー、とにかくタイピングのぎこちないことwww

(全体の5分の2くらいで、投げ出して、3分間クッキングみたいに完成品を出そうかと思ったほど。)

 

今回自分でやってみて、あらためてライブコーディングすらすらできる人、すごいなぁ、と感じました。。。

それでも、前日に練習した時に1時間かかったものが、その3分の1くらいでできたので良かったのと、

最後、無事、端末上で1回で動作した時、暖かい拍手を頂けたのが本当に嬉しかったですm_ _m

(来てくれた方々がいい人で良かったわー)

(来てくれた方々がいい人で良かったわー)

しろめ

 

教訓

  • ライブコーディングする場合は、立って練習せよ
  • プロジェクタの関係で画面の作業エリアが狭くなるので、1024x768くらいで練習せよ
  • トラぶった時に、気の利いたトークができるように、トークも磨け
  • 前日に InkScape 起動して萌絵(↓)描いて現実逃避する暇があったら練習せよ

 

jcromtan2.png

ActionScript 用の有名なアニメーションライブラリである Tweener を Android で使用する方法について説明します。

 

Tweener を使用すると、

Tweener.to(imageView, 3000, "alpha", 0);

のような感じでプロパティアニメーションさせたり、

 

Tweener.to(imageView, // アニメーション対象
    3000, // アニメーション期間(単位はミリ秒)
    "translationX", 200.0f, // 変化させたいプロパティ名, 値
    "translationY", 100.0f, // 変化させたいプロパティ名, 値
    "ease", Ease.Quad.easeInOut, // 補間関数
    "onComplete", new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            // 必要であれば、アニメーション終了時のコードを書きます
        }
    });

のような感じで、複雑なアニメーション(複数プロパティ、補完関数変更、終了イベント補足)も、短いコードで書くこともできます。

 

Android 3.0 以降であれば、Android でもプロパティアニメーションがサポートされているため、

新たに Tweener を使用するまでもないのですが、Tweener の方が馴染みがある場合には、役に立つかもしれません。

ちなみにプロパティアニメーションについては『Androidプログラミング上達読本』にも解説があるので、興味を持った方は読んでみてください^^;

 

使い方

1. AOSP から Android 4.0 以降のソースコードをダウンロードする。

 ※必要なコードは frameworks 下の一部のコードだけなので repo ではなく、git で直接取得してもよいです

git clone https://android.googlesource.com/platform/frameworks/base

 

2. 取得したコードの中から以下のクラスをコピーし、任意のパッケージ名に変更する。

  • frameworks\base\core\java\com\android\internal\widget\multiwaveview\Tweener.java
  • frameworks\base\core\java\com\android\internal\widget\multiwaveview\Ease.java

 ※Tweener クラスはパッケージスコープになっているので必要に応じてアクセスレベルを変更してください

 ※パッケージ名を変更しないと、4.0(ICS)―4.1(JB)間で内部クラスが変更されているので上手く動作しない可能性があります

 

3. Tweener.to() を呼び出し、Tweener アニメーションコードを書く。

 ※変化させるプロパティ名は、ActionScript でなく、Adnroid で使用できるプロパティ名を書きます

 ※Tweener.from() などは移植されていないようです

 

Tweener

ActionScript は言語レベルでプロパティをサポートされているので、Tweener の実装は非常にシンプルなコードになっています。

 

Android 4.0 フレームワークの内部クラスで実装されている Tweener のコード

  • Twneer.java

Android のプロパティアニメーションAPI(PropertyValuesHolder、ObjectAnimator など)を使用して移植されています。

/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.widget.multiwaveview;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;

import android.animation.Animator.AnimatorListener;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.util.Log;

class Tweener {
    private static final String TAG = "Tweener";
    private static final boolean DEBUG = false;

    ObjectAnimator animator;
    private static HashMap<Object, Tweener> sTweens = new HashMap<Object, Tweener>();

    public Tweener(ObjectAnimator anim) {
        animator = anim;
    }

    private static void remove(Animator animator) {
        Iterator<Entry<Object, Tweener>> iter = sTweens.entrySet().iterator();
        while (iter.hasNext()) {
            Entry<Object, Tweener> entry = iter.next();
            if (entry.getValue().animator == animator) {
                if (DEBUG) Log.v(TAG, "Removing tweener " + sTweens.get(entry.getKey())
                        + " sTweens.size() = " + sTweens.size());
                iter.remove();
                break; // an animator can only be attached to one object
            }
        }
    }

    public static Tweener to(Object object, long duration, Object... vars) {
        long delay = 0;
        AnimatorUpdateListener updateListener = null;
        AnimatorListener listener = null;
        TimeInterpolator interpolator = null;

        // Iterate through arguments and discover properties to animate
        ArrayList<PropertyValuesHolder> props = new ArrayList<PropertyValuesHolder>(vars.length/2);
        for (int i = 0; i < vars.length; i+=2) {
            if (!(vars[i] instanceof String)) {
                throw new IllegalArgumentException("Key must be a string: " + vars[i]);
            }
            String key = (String) vars[i];
            Object value = vars[i+1];
            if ("simultaneousTween".equals(key)) {
                // TODO
            } else if ("ease".equals(key)) {
                interpolator = (TimeInterpolator) value; // TODO: multiple interpolators?
            } else if ("onUpdate".equals(key) || "onUpdateListener".equals(key)) {
                updateListener = (AnimatorUpdateListener) value;
            } else if ("onComplete".equals(key) || "onCompleteListener".equals(key)) {
                listener = (AnimatorListener) value;
            } else if ("delay".equals(key)) {
                delay = ((Number) value).longValue();
            } else if ("syncWith".equals(key)) {
                // TODO
            } else if (value instanceof float[]) {
                props.add(PropertyValuesHolder.ofFloat(key,
                        ((float[])value)[0], ((float[])value)[1]));
            } else if (value instanceof Number) {
                float floatValue = ((Number)value).floatValue();
                props.add(PropertyValuesHolder.ofFloat(key, floatValue));
            } else {
                throw new IllegalArgumentException(
                        "Bad argument for key \"" + key + "\" with value " + value.getClass());
            }
        }

        // Re-use existing tween, if present
        Tweener tween = sTweens.get(object);
        ObjectAnimator anim = null;
        if (tween == null) {
            anim = ObjectAnimator.ofPropertyValuesHolder(object,
                    props.toArray(new PropertyValuesHolder[props.size()]));
            tween = new Tweener(anim);
            sTweens.put(object, tween);
            if (DEBUG) Log.v(TAG, "Added new Tweener " + tween);
        } else {
            anim = sTweens.get(object).animator;
            replace(props, object); // Cancel all animators for given object
        }

        if (interpolator != null) {
            anim.setInterpolator(interpolator);
        }

        // Update animation with properties discovered in loop above
        anim.setStartDelay(delay);
        anim.setDuration(duration);
        if (updateListener != null) {
            anim.removeAllUpdateListeners(); // There should be only one
            anim.addUpdateListener(updateListener);
        }
        if (listener != null) {
            anim.removeAllListeners(); // There should be only one.
            anim.addListener(listener);
        }
        anim.addListener(mCleanupListener);
        anim.start();

        return tween;
    }

    Tweener from(Object object, long duration, Object... vars) {
        // TODO:  for v of vars
        //            toVars[v] = object[v]
        //            object[v] = vars[v]
        return Tweener.to(object, duration, vars);
    }

    // Listener to watch for completed animations and remove them.
    private static AnimatorListener mCleanupListener = new AnimatorListenerAdapter() {

        @Override
        public void onAnimationEnd(Animator animation) {
            remove(animation);
        }

        @Override
        public void onAnimationCancel(Animator animation) {
            remove(animation);
        }
    };

    public static void reset() {
        if (DEBUG) {
            Log.v(TAG, "Reset()");
            if (sTweens.size() > 0) {
                Log.v(TAG, "Cleaning up " + sTweens.size() + " animations");
            }
        }
        sTweens.clear();
    }

    private static void replace(ArrayList<PropertyValuesHolder> props, Object... args) {
        for (final Object killobject : args) {
            Tweener tween = sTweens.get(killobject);
            if (tween != null) {
                tween.animator.cancel();
                if (props != null) {
                    tween.animator.setValues(
                            props.toArray(new PropertyValuesHolder[props.size()]));
                } else {
                    sTweens.remove(tween);
                }
            }
        }
    }
}

 

  • Ease.java

Tweener で定義されているイージングを Android のプロパティアニメーションAPI を使用して移植してあります。

TimeInterpolator を継承したクラスなので、ここだけでも使えますね!

/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.widget.multiwaveview;

import android.animation.TimeInterpolator;

class Ease {
    private static final float DOMAIN = 1.0f;
    private static final float DURATION = 1.0f;
    private static final float START = 0.0f;

    static class Linear {
        public static final TimeInterpolator easeNone = new TimeInterpolator() {
            public float getInterpolation(float input) {
                return input;
            }
        };
    }

    static class Cubic {
        public static final TimeInterpolator easeIn = new TimeInterpolator() {
            public float getInterpolation(float input) {
                return DOMAIN*(input/=DURATION)*input*input + START;
            }
        };
        public static final TimeInterpolator easeOut = new TimeInterpolator() {
            public float getInterpolation(float input) {
                return DOMAIN*((input=input/DURATION-1)*input*input + 1) + START;
            }
        };
        public static final TimeInterpolator easeInOut = new TimeInterpolator() {
            public float getInterpolation(float input) {
                return ((input/=DURATION/2) < 1.0f) ?
                        (DOMAIN/2*input*input*input + START)
                            : (DOMAIN/2*((input-=2)*input*input + 2) + START);
            }
        };
    }

    static class Quad {
        public static final TimeInterpolator easeIn = new TimeInterpolator() {
            public float getInterpolation (float input) {
                return DOMAIN*(input/=DURATION)*input + START;
            }
        };
        public static final TimeInterpolator easeOut = new TimeInterpolator() {
            public float getInterpolation(float input) {
                return -DOMAIN *(input/=DURATION)*(input-2) + START;
            }
        };
        public static final TimeInterpolator easeInOut = new TimeInterpolator() {
            public float getInterpolation(float input) {
                return ((input/=DURATION/2) < 1) ?
                        (DOMAIN/2*input*input + START)
                            : (-DOMAIN/2 * ((--input)*(input-2) - 1) + START);
            }
        };
    }

    static class Quart {
        public static final TimeInterpolator easeIn = new TimeInterpolator() {
            public float getInterpolation(float input) {
                return DOMAIN*(input/=DURATION)*input*input*input + START;
            }
        };
        public static final TimeInterpolator easeOut = new TimeInterpolator() {
            public float getInterpolation(float input) {
                return -DOMAIN * ((input=input/DURATION-1)*input*input*input - 1) + START;
            }
        };
        public static final TimeInterpolator easeInOut = new TimeInterpolator() {
            public float getInterpolation(float input) {
                return ((input/=DURATION/2) < 1) ?
                        (DOMAIN/2*input*input*input*input + START)
                            : (-DOMAIN/2 * ((input-=2)*input*input*input - 2) + START);
            }
        };
    }

    static class Quint {
        public static final TimeInterpolator easeIn = new TimeInterpolator() {
            public float getInterpolation(float input) {
                return DOMAIN*(input/=DURATION)*input*input*input*input + START;
            }
        };
        public static final TimeInterpolator easeOut = new TimeInterpolator() {
            public float getInterpolation(float input) {
                return DOMAIN*((input=input/DURATION-1)*input*input*input*input + 1) + START;
            }
        };
        public static final TimeInterpolator easeInOut = new TimeInterpolator() {
            public float getInterpolation(float input) {
                return ((input/=DURATION/2) < 1) ?
                        (DOMAIN/2*input*input*input*input*input + START)
                            : (DOMAIN/2*((input-=2)*input*input*input*input + 2) + START);
            }
        };
    }

    static class Sine {
        public static final TimeInterpolator easeIn = new TimeInterpolator() {
            public float getInterpolation(float input) {
                return -DOMAIN * (float) Math.cos(input/DURATION * (Math.PI/2)) + DOMAIN + START;
            }
        };
        public static final TimeInterpolator easeOut = new TimeInterpolator() {
            public float getInterpolation(float input) {
                return DOMAIN * (float) Math.sin(input/DURATION * (Math.PI/2)) + START;
            }
        };
        public static final TimeInterpolator easeInOut = new TimeInterpolator() {
            public float getInterpolation(float input) {
                return -DOMAIN/2 * ((float)Math.cos(Math.PI*input/DURATION) - 1.0f) + START;
            }
        };
    }

}

 

あわせて読みたい

android1.6_2.png android1.6.png  

Android 3.0 以降で追加されたプロパティアニメーション機能の一部(ここ重要)をバックポートしたライブラリを作成しました。

 

このライブラリを使用すると、なんと! Android 1.6 環境でも、

独自に作成したオブジェクトのカスタムプロパティ(※)に限られますが、プロパティアニメーションをさせることができます。

(※Android 3.0 以降にしか存在しない View の組み込みプロパティ(x, y, alpha...など)は使用できない)

 

冒頭の画面キャプチャは、あの HT-03A(Android 1.6)上で Android 4.0 の API Demos に含まれる

プロパティアニメーションのサンプルが動作している様子です。

HT-03A(Android 1.6)たん可愛いよ。

IS01(Android 1.6)たん可愛いよ。

 

ソースコード

※Android 4.0 の API Demos のプロパティアニメーションのコードのうち、
 Android 1.6 + 本ライブラリで使用できる 6 つのサンプルコードも収録しています。

 

サポートしているクラス(制限事項あり)

  • Animator
  • AnimatorListenerAdapter
  • AnimatorSet
  • ArgbEvaluator
  • FloatEvaluator
  • FloatKeyframeSet
  • IntEvaluator
  • IntKeyframeSet
  • Keyframe
  • KeyframeSet
  • ObjectAnimator
  • PropertyValuesHolder
  • TimeAnimator
  • TypeEvaluator
  • ValueAnimator

※制限事項

変化させる対象のプロパティは独自に定義したものに限られます。
例えば、View の移動、回転、拡大縮小、アルファなどの組み込みプロパティは、
Android 3.0 以降のフレームワークに組み込まれているプロパティのため使用できません><

 

非サポートクラス

  • AnimatorInflater → リソースに定義したアニメーションからのインフレート
  • LayoutTransition → レイアウト変更時に適用するアニメーション
  • TimeInterpolator → 従来の android.view.Interpolator
  • ViewPropertyAnimator → View の組み込みプロパティに対する最適化されたアニメーション

 

バックポート手順

詳細なバックポート手順については下記リンク先(Google Drive(ログイン不要))に上げました。

https://docs.google.com/document/d/1fu9p3hfG8GDJOg6H6rIuWhhiGhsAQ4msMPb_pSiRI8g/edit

Google Code 内のリポジトリのコミットログにも細かくコメントをつけてますので、そちらを参照してもわかるかと思います。

コードを読むと、言語レベルでプロパティをサポートしていない Android において、

いかにしてプロパティを実現しているか理解できて面白いと思います。

一部、NDKを使用してネイティブコードも使用しています。

 

Androidプログラミング上達読本

あっ、そうそう、大事なこと言い忘れてました。

このたび、リックテレコムさんから出版される「Androidプログラミング上達読本」の Section 4 の執筆を担当しました。

同 Section にてプロパティアニメーションの API を解説していますので、あわせて読んでいただければ有難いです。

この本は、他にも、タオガクさんや、やんざむさんなど著名な方々の記事で構成されています。

2012年8月4日 ~10日 くらいには発売されますので、見かけた際には、是非手に取って確認してみてください。

book902.png

http://www.ric.co.jp/book/contents/book_902.html

 

私が書いた記事の構成としては、4-2  で API の解説をしていますが、

どちらかというと、4-2 は、困ったときに逆引き的な読めるように書いた/レイアウトしたので、

時間のない方は、プロパティアニメーションの魅力を理解するために

4-3、4-4 を斜め読みするのが良いかもしれません。

また、4-3 では、あの伝説の、、、「第4回 SHARPハッカソン」で生まれたスタ★ミやモグタソも登場します。

最後の 4-4 は、グラフ(カスタムビューのカスタムプロパティ)のアニメーションで、

Android 3.0 以降向けに作成したものですが、本エントリのバックポートライブラリを使用すると、

なんと Android 1.6 でも動作してしまいますー。

ちなみに、Google Drive に上げたバックポート手順は、紙面の都合上カットした没記事です^^;

30ページの枠ですが、勢い余って50ページ書いてました。。。

ということで、本エントリ以外にも、まだ隠し玉があったりします///

 

個人的には、プロパティアニメーションを使用したアプリが増えてくるのは・・・

2013年以降かな。。。

と思ってますので、末永く手元に置いていただければ・・・と思います^^;

 

あわせて読みたい

customLogo.png 

2012年7月8日、東京・京都・名古屋・中国・四国・九州・信州・米国 で同時開催された

DevFestX Japan 2012 Summer に参加してきました。

 

公式サイト

 

講演スライド(直リンク)

 

各会場からアップされた画像

最近 Google+ に追加された「イベント」機能のパーティーモードを使った写真

 

Togetter

@roishi2j2 さんによるまとめ

@tamacjp さんによるまとめ

 

お土産

「android ロボットのUSBメモリ」 や 「Google のビーチサンダル」 を頂きました。

usb.jpg

 

信州会場

Google Maps API Expert の 石丸(@kehi)さん、古籏(@openspc)さんの働きかけにより、

信州会場(パブリックビューイング)として開催していただけました。

「GTUG がない → DevFestX 行けない → しょぼーん(´・ω・`) 」 ではなく、

「ないなら創る → DevFestX やる → イヤッホゥ(゜∀゜)!!」 という流れです。

 

パブリックビューイングということでしたが、実際には他の会場と同様に Google+ Hangout で接続され、

地方にいながら、皆さんと同じ体験をすることができました。これも、

当日ぎりぎりまで準備してくださった Google Expert の方々、素晴らしい会場を提供してくださった S.I.P の方々、

裏方で運営をサポートしてくださった方々、満席になるほど参加して盛り上げてくださった皆様のおかげですね。

大変有難いことですm_ _m

 

@lychee さんのツイートにもありましたが、まさにそのとおりだと感じました。

221873380897460224.png

 

まとめると・・・

 

221834100988846080.png

 

九州会場凄い!ハングアウト凄い!

<<前のページへ 1234567891011

2016年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