6月 28
まずはお詫びから、6/5のエントリで「カスタムダイアログと遷移アニメーション」のソースコードが GoogleCode のレポジトリに登録されていませんでした。なぜかローカルの svn のレポジトリに登録されていました。orz 本日、登録しましたのでよろしくー。

で、今日のお題は「レイアウトを分割しよう!」です。簡単な場合やひとりで開発する場合はさておき複雑な画面(Activity)やひとつの画面を複数人で開発しなければならない場合、レイアウトファイルが大きくなりすぎて見通しが悪くなったり他の人の修正とコンフリクトを起こしひどいめにあったりする場合がよくあると思います。今回はそんなときに使える Tips です。

今日のサンプルアプリ

device-1.png

今日のサンプルアプリの画面はこんな感じです。リセットボタンが1つに数字が3つ。数字はそれぞれ別個のTextViewです。青のビューの数字が1の位、緑のビューの数字が十の位、赤のビューの数字が百の位を表しています。アプリを起動するとカウントアップが開始され999を超えると000に戻ります。またリセットボタンを押すことで000に戻すこともできます。普通に作ればなんということのないアプリです。今回はこれをカスタムビューと複数のレイアウトを使って作りました。

blog-1.png

aaa.xml, bbb.xml, ccc.xml に対応するカスタムビューを Aaa, Bbb, Ccc をそれぞれ用意します。カスタムビューは FrameLayout または LineraLayout のサブクラスにするといいでしょう。ここで示す例では FrameLayout のサブクラスとしています。xml ファイルから読み込まれるコンストラクタで対応するレイアウトファイルを inflate し、それを addView します。

public class Aaa extends FrameLayout {

    public Aaa(Context context, AttributeSet attrs) {
        super(context, attrs);

        LayoutParams params =
            new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
        View child = inflate(context, R.layout.aaa, null);
        addView(child, params);

aaa.xml
bbb.xml
ccc.xml

Aaa
Bbb
Ccc

こうすることでレイアウトファイル Aaa.xml 上のウィジェットの制御は全てカスタムビュー Aaa に集約させることができます。

でもレイアウトファイルをまたぐような連携はどうするの?もちろんレイアウトを分割したとはいえ同じ Activity 上のウィジェットです。普通に findViewById を利用すれば相互にアクセス可能です。でもこれじゃーおもしろくありませんし、なによりモジュールはできるだけ疎に保つべきという定石に反します。こういうときはカスタムモジュールに外部から操作できるメソッドと通知を受け取るリスナーを用意してあげましょう。

DigitListener

blog-2.png

リスナーで受け取ったコールバックの中で UI を操作するとスレッドの例外が発生するので必ず Handler を用いて UI スレッドに処理を post してあげるのを忘れずに。

サンプルコード

サンプルアプリのソースコードは Google Code で公開しています。完全なサンプルコードを参照したい場合はそちらをどうぞ。


Posted by sak+

Tagged with:
6月 20

もう先週の話しになってしまうのですが、2011/06/10-13 の日程で Interop Tokyo 2011 を見るために東京へ行ってきました。6/10 は朝早い便で羽田へ、そしてそのままバスで幕張へ。思っていたより早く11時前には会場の幕張メッセに着いちゃいました。バスは早いですね。羽田から幕張までほんの 45分くらいでした。

実は2年前の Interop では出展側で参加したのですが、そのときと比べると会場がだいぶ小さくなっている感じがしました。これも震災と原発の影響でしょうか?そう言えば外国企業の出展や外国人の見学者をあんまり見かけません。会場は2時間もあれば一回りできる感じでしょうか。

2年前の Interop では Android の出展はリアルでは私の所属していた会社だけ、あと2つほどパネル展示があるだけでしたが、今回は普通に Android 端末を使った展示がありましたね。イベントの性格上 Android という言葉を強く押し出さずスマートフォンという言葉で iPhone や iPad のデバイスと同列に扱っている感じです。Android はもはや特別な存在ではないのでしょうね。

私の前職の会社も2年ぶりにブースを出しているということで遊びに行きました。今回は私の所属していた部隊がメインの展示ということもあり、当時私が関わったアプリも展示されており懐かしい感じがしました。おみやげにノベリティのライト付きドライバーをもらっちゃいました。

IMG_0.png

メールでしかやり取りしていなかったタオソフトウェアのがくさんと初めて会ったのが2年前の Interop でした。今回は会場で合流し一緒に一回り、そのあといろいろ情報交換させていただきました。その際に赤いドロイド君のストラップをいただいちゃいました。Xperia の赤いカバーにジャストフィットしてます。

IMG_1.png

札幌にいると技術的な刺激を受ける機会が少ないので、こういったイベントにはたまに参加して刺激をもらわないとね。


Posted by sak+

Tagged with:
6月 05
今日はカスタムダイアログ作成の話しです。標準のダイアログではなく iPhone のような洒落たダイアログを作成するにはどうしたら良いでしょう。そのためには、レイアウトのカスタマイズと画面遷移の際のアニメーションの設定が必要です。この2つをすることで Android でもこんなカスタムダイアログを作成できます。(サンプルの画面はそれほど洒落た画面ではありませんね。 (^^; でも頑張ればちゃんとイカした画面が作成できます!)

device-1.png device-2.png

左のダイアログは中央に表示するカスタムダイアログです。レイアウトはレイアウトファイルで自由に指定できます。右は iPhone のアクションシートのようなカスタムダイアログです。ダイアログ画面が下からせり上がります。

このような画面遷移のアニメーションはアニメーションファイル(res/anim/*.xml)で作成します。これをスタイルファイル(res/value/styles.xml)とテーマファイル(res/value/themas.xml)の設定で作成したカスタムダイアログに関連付けるのです。

今回は Android のソースに含まれている8種類の画面遷移アニメーションを利用しサンプルアプリを作成しました。ダイアログのレイアウトは3種類です。画像とソースだけではアニメーションの動きを伝えるのは難しいので是非サンプルアプリを入手しお試しください。アニメーションの Alpha, translate, scale 等を組み合わせて様々な画面遷移を実現できることを体感していただけると思います。

device-3.png

public class Main extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Button b1 = (Button)findViewById(R.id.button1);
        b1.setOnClickListener(new OnMyClickListener(R.layout.dialog1, R.style.AnimDialog));

        Button b2 = (Button)findViewById(R.id.button2);
        b2.setOnClickListener(new OnMyClickListener(R.layout.dialog1, R.style.AnimToast));

        Button b3 = (Button)findViewById(R.id.button3);
        b3.setOnClickListener(new OnMyClickListener(R.layout.dialog1, R.style.AnimWallPaper));

        Button b4 = (Button)findViewById(R.id.button4);
        b4.setOnClickListener(new OnMyClickListener(R.layout.dialog1, R.style.AnimSubmenu));

        Button b5 = (Button)findViewById(R.id.button5);
        b5.setOnClickListener(new OnMyClickListener(R.layout.dialog1, R.style.AnimInputMethod));

        Button b6 = (Button)findViewById(R.id.button6);
        b6.setOnClickListener(new OnMyClickListener(R.layout.dialog1, R.style.AnimInputMethodFancy));

        Button b7 = (Button)findViewById(R.id.button7);
        b7.setOnClickListener(new OnMyClickListener(R.layout.dialog2, R.style.AnimOptionsPanel));

        Button b8 = (Button)findViewById(R.id.button8);
        b8.setOnClickListener(new OnMyClickListener(R.layout.dialog3, R.style.AnimStatusBar));
    }

    private class OnMyClickListener implements OnClickListener {
        private int layout;
        private int style;

        public OnMyClickListener(int layout, int style) {
            this.layout = layout;
            this.style = style;
        }

        @Override
        public void onClick(View arg0) {
            final Dialog dialog = new Dialog(Main.this, style);
            dialog.setContentView(layout);

            // OK ボタン
            Button btnOk = (Button)dialog.findViewById(R.id.ok);
            btnOk.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View arg0) {
                    dialog.dismiss();
                }
            });

            // Cancel ボタン
            Button btnCancel = (Button)dialog.findViewById(R.id.cancel);
            btnCancel.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View arg0) {
                    dialog.cancel();
                }
            });

            dialog.show();
        }
    }
}

res/value/themes.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--
   <style name="Theme.Dialog">
       <item name="android:windowFrame">@null</item>
       <item name="android:windowTitleStyle">@android:style/DialogWindowTitle</item>
       <item name="android:windowBackground">@android:drawable/panel_background</item>
       <item name="android:windowIsFloating">true</item>
       <item name="android:windowContentOverlay">@null</item>
       <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
       <item name="android:windowSoftInputMode">stateUnspecified|adjustPan</item>
   </style>
-->
    <style name="MyDialog" parent="android:style/Theme.Dialog">
        <item name="android:windowNoTitle">true</item>
    </style>

    <style name="AnimSubmenu" parent="MyDialog">
        <item name="android:windowBackground">@drawable/frame</item>
        <item name="android:windowAnimationStyle">@style/AnimSubmenu</item>
    </style>
    <style name="AnimToast" parent="MyDialog">
        <item name="android:windowBackground">@drawable/frame</item>
        <item name="android:windowAnimationStyle">@style/AnimToast</item>
    </style>
    <style name="AnimWallPaper" parent="MyDialog">
        <item name="android:windowBackground">@drawable/frame</item>
        <item name="android:windowAnimationStyle">@style/AnimWallPaper</item>
    </style>
    <style name="AnimDialog" parent="MyDialog">
        <item name="android:windowBackground">@drawable/frame</item>
        <item name="android:windowAnimationStyle">@style/AnimDialog</item>
    </style>
    <style name="AnimInputMethod" parent="MyDialog">
        <item name="android:windowBackground">@drawable/frame</item>
        <item name="android:windowAnimationStyle">@style/AnimInputMethod</item>
    </style>
    <style name="AnimInputMethodFancy" parent="MyDialog">
        <item name="android:windowBackground">@drawable/frame</item>
        <item name="android:windowAnimationStyle">@style/AnimInputMethodFancy</item>
    </style>
    <style name="AnimOptionsPanel" parent="MyDialog">
        <item name="android:windowBackground">@drawable/frame_0</item>
        <item name="android:windowAnimationStyle">@style/AnimOptionsPanel</item>
    </style>
    <style name="AnimStatusBar" parent="MyDialog">
        <item name="android:windowBackground">@drawable/frame_0</item>
        <item name="android:windowAnimationStyle">@style/AnimStatusBar</item>
    </style>
</resources>

res/value/styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="AnimStatusBar" parent="android:Animation.Dialog">
        <item name="android:windowEnterAnimation">@anim/status_bar_enter</item>
        <item name="android:windowExitAnimation">@anim/status_bar_exit</item>
    </style>
    <style name="AnimOptionsPanel" parent="android:Animation.Dialog">
        <item name="android:windowEnterAnimation">@anim/options_panel_enter</item>
        <item name="android:windowExitAnimation">@anim/options_panel_exit</item>
    </style>
    <style name="AnimSubmenu" parent="android:Animation.Dialog">
        <item name="android:windowEnterAnimation">@anim/submenu_enter</item>
        <item name="android:windowExitAnimation">@anim/submenu_exit</item>
    </style>
    <style name="AnimToast" parent="android:Animation.Dialog">
        <item name="android:windowEnterAnimation">@anim/toast_enter</item>
        <item name="android:windowExitAnimation">@anim/toast_exit</item>
    </style>
    <style name="AnimWallPaper" parent="android:Animation.Dialog">
        <item name="android:windowEnterAnimation">@anim/wallpaper_enter</item>
        <item name="android:windowExitAnimation">@anim/wallpaper_exit</item>
    </style>
    <style name="AnimDialog" parent="android:Animation.Dialog">
        <item name="android:windowEnterAnimation">@anim/dialog_enter</item>
        <item name="android:windowExitAnimation">@anim/dialog_exit</item>
    </style>
    <style name="AnimInputMethod" parent="android:Animation.Dialog">
        <item name="android:windowEnterAnimation">@anim/input_method_enter</item>
        <item name="android:windowExitAnimation">@anim/input_method_exit</item>
    </style>
    <style name="AnimInputMethodFancy" parent="android:Animation.Dialog">
        <item name="android:windowEnterAnimation">@anim/input_method_fancy_enter</item>
        <item name="android:windowExitAnimation">@anim/input_method_fancy_exit</item>
    </style>
</resources>

サンプルコード

サンプルアプリのソースコードは Google Code で公開しています。完全なサンプルコードを参照したい場合はそちらをどうぞ。


Posted by sak+

Tagged with:
preload preload preload