2012年2月アーカイブ

日本Androidの会 2012年2月定例会は、『NFC(Near Field Communication)近距離無線通信』特集でした。

以下、ust 動画リンクと動画中で登場するキーワードのリンクなどです。

 

【AndroidとNFCの何がおもしろい?】

  • 株式会社ブリリアントサービス 代表取締役 杉本礼彦様

  • NFC QUEST ・・・ICカードとスマートフォンでゲームが楽しめるスタンプラリーRPG!
  • Clipper Home 
  • NFCラボ ・・・「NFCの普及を目指し、NFCを利用して個人、法人、社会の幸せを追及していく」

 

【たっちなう】

  • ハヤト・インフォメーション株式会社 技術開発グループ統括マネージャ 佐野誠一様

 

【RFID NFC Real Touch Shop ~NFC秘密基地~】

  • クレスコ・アイディー 営業部部長 大坂泰弘様

 

【FeliCaはNFCによって飛躍、進化する】

  • ソニー株式会社 FeliCa事業部 プロダクト&サービス部 シニア ビジネスクリエイター 鳥居三朗様 

 

その他

Say Hello to the Action Bar !

ということで、アクションバー(Action Bar)を画面下側から表示させてみました。

 

google_reader.pnggmail.png

ご覧のとおり、通常は画面上側から表示されるアクションバーが画面下側から表示されるため、片手で操作しやすくなります^^

※上図左、Google Reader アプリの場合、「お気に入り(スター)」ボタンが(片手持ちの)親指で届くため、操作性が向上!

※上図右、GMail アプリのようにスプリットアクションバーがある場合には、その上に表示されます

 

ちなみに、twicca だと、こんな感じ。

twicca.png

4.7 インチの大画面 Galaxy Nexus であっても片手持ちで、タイトルバーダブルタップ(リストビューの先頭へ移動)できます!

 

変更方法

android-4.0.3_r1 \ frameworks \ base \ core \ res \ res \ layout 下にある全ての screen*.xml に対し、 

アプリのコンテンツ領域である FrameLayout (android:id="@android:id/content")の定義位置を上部に移動するだけ。

以下、1 ファイルだけサンプル。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:fitsSystemWindows="true">
<!-- vvv After vvv -->
    <FrameLayout android:id="@android:id/content"
        android:layout_width="match_parent" 
        android:layout_height="0dip"
        android:layout_weight="1"
        android:foregroundGravity="fill_horizontal|top"
        android:foreground="?android:attr/windowContentOverlay" />
<!-- ^^^ After ^^^ -->

    <!-- Popout bar for action modes -->
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content" />
    <FrameLayout
        android:layout_width="match_parent" 
        android:layout_height="?android:attr/windowTitleSize"
        style="?android:attr/windowTitleBackgroundStyle">
        <TextView android:id="@android:id/title" 
            style="?android:attr/windowTitleStyle"
            android:background="@null"
            android:fadingEdge="horizontal"
            android:gravity="center_vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>
<!-- vvv Brefore vvvv -->
<!--
    <FrameLayout android:id="@android:id/content"
        android:layout_width="match_parent" 
        android:layout_height="0dip"
        android:layout_weight="1"
        android:foregroundGravity="fill_horizontal|top"
        android:foreground="?android:attr/windowContentOverlay" />
-->
<!-- ^^^ Brefore ^^^ -->
</LinearLayout>

 

あわせて読みたい

ステータスバーを引き下げた時に表示される(通知トラッキング)ビューの背景を変更してみました。

※標準の背景に似せて(おもむきのある版画風)グレースケール透過にしていますが、背景画像はフルカラー使用可能です

 

notification_tracking_bg_1.png notification_tracking_bg_2.png

 

解説

ステータスバーを引き下げた時に表示される(通知トラッキング)ビューの背景は、デフォルトでは、色リソースとして定義されています。 

  • android-4.0.3_r1\frameworks\base\packages\SystemUI\res\values\colors.xml
<resources>
    <drawable name="notification_tracking_bg">#d8000000</drawable>
</resources>

したがって、上記の「notification_tracking_bg」の行をコメントアウトし、

「drawable(-hdpi)」フォルダ下に「notification_tracking_bg.png」というファイル名の画像を置くことで、背景を差し替えできます。

 

外部ストレージに配置した画像を読み込んで配置する

カスタマイズしやすいように外部ストレージに配置した画像を読み込んで配置するには、以下のようなコードで実現できます。 

  • android-4.0.3_r1\frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\PhoneStatusBar.java
import android.view.animation.AnimationUtils;
// 追加 ->
import android.widget.FrameLayout;
// 追加 <-
import android.widget.ImageView;

public class PhoneStatusBar extends StatusBar {

    protected View makeStatusBarView() {
// ・・・省略・・・
        TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
        tickerView.mTicker = mTicker;

        mTrackingView = (TrackingView)View.inflate(context, R.layout.status_bar_tracking, null);
// 追加 ->
{
    String IMAGE_FILENAME = "notification_tracking_bg.png";
    FrameLayout f = (FrameLayout) mTrackingView.findViewById(R.id.notification_tracking_bg);
    StringBuilder builder = new StringBuilder();
    builder.append(Environment.getExternalStorageDirectory().toString());
    builder.append(File.separator);
    builder.append(IMAGE_FILENAME);
    String filePath = builder.toString();
    Drawable drawable = Drawable.createFromPath(filePath);
    if (drawable != null) {
        f.setBackgroundDrawable(drawable);
    }
}
// 追加 <-

        mTrackingView.mService = this;
// ・・・省略・・・

※読み込むフォルダ位置(ここではSDカード直下)は、適宜変更してください

 

  • android-4.0.3_r1\frameworks\base\packages\SystemUI\res\layout\status_bar_tracking.xml
<com.android.systemui.statusbar.phone.TrackingView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:visibility="gone"
    android:focusable="true"
    android:descendantFocusability="afterDescendants"
    android:paddingBottom="0px"
    android:paddingLeft="0px"
    android:paddingRight="0px"
    >
<!-- 以下のandroid:idの1行のみを追加 -->
    <FrameLayout

android:id="@+id/notification_tracking_bg"

        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:background="@drawable/notification_tracking_bg"
        >
        <com.android.systemui.statusbar.phone.CarrierLabel
            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            android:layout_gravity="bottom"
            android:gravity="center"
            android:paddingBottom="20dp"
            />
    </FrameLayout>

※変更した部分が強調されるようにインデントをずらしています

 

あわせて読みたい

先日のエントリ「通知アイテムの背景画像を差し替える」は、

ビルド時に画像が固定で埋め込まれているため、画像の差し替えが大変だけでなく、

ビルドできる人しか画像を差し替えることができない、という問題がありました^^;

 

そこで、外部ストレージに配置した複数の画像を状態付きで設定するコードを作成し、検証してみたところ、

問題なく動作することを確認できました。

※「画像を状態別に設定できる」かつ「1つの View に複数の画像を設定できる」という点がポイントです

 

この方法を使用することで、カスタム ROM の画像リソースを、外部ファイル化することができそうです^^

※見ろ!先日のエントリのリソースのハードコードがゴミのようだ!

 

以下、解説です。

 

外部ストレージから読み込んだ画像を設定するには(Step1)

外部ストレージにある画像を Drawable#createFromPath(String) を使用して作成し、

画像を設定したい View に対し View#setBackgroundDrawable(Drawable) を呼び出します。

ここまでで、外部ストレージにある画像を、任意の View に適用できます。

sample1.png

 

外部ストレージから読み込んだ画像を状態付きで設定するには(Step2)

Step1 に加え、画像を「通常状態」「押下状態」など、状態付きで設定してみます。

通常、画像を状態付きで設定するには、<selector> を記述した xml リソースファイルを指定しますが、

ここでは、ソースコードから動的に作成してみます。

まず、 StateListDrawable インスタンスを作成し、

次に、追加したい状態を StateListDrawable#addState(int[], Drawable) を使用して追加し、

画像を設定したい View に対し View#setBackgroundDrawable(Drawable) を呼び出します。

ここまでで、外部ストレージにある画像を「状態付き」で、任意の View に適用できます。

sample2.png

 

外部ストレージから読み込んだ複数の画像を状態付きで設定するには(Step3)

Step2 に加え、複数の画像(画像00、画像01、・・・)を設定してみます。

通常、複数の画像を設定するには、<level-list> を記述した xml リソースファイルを指定しますが、

ここでは、ソースコードから動的に作成してみます。

まず、LevelListDrawable インスタンスを作成し、

次に、追加したい画像を LevelListDrawable#addLevel(int, int, Drawable) を使用して追加し、

画像を設定したい View に対し View#setBackgroundDrawable(Drawable) を呼び出します。

追加した任意の画像に切り替えるには、Drawable#setLevel((int) を呼び出します。

これで、外部ストレージにある「複数」の画像を「状態付き」で、任意の View に適用できます。

sample3.png

 

以下、ソースコードサンプルです。

※サンプルのためエラー処理などは省略しています

 

ソースコード

  • src / StateTestActivity.java
package com.adakoda.android.statetest;

import java.io.File;

import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LevelListDrawable;
import android.graphics.drawable.StateListDrawable;
import android.os.Bundle;
import android.os.Environment;
import android.util.StateSet;
import android.view.View;
import android.widget.ImageView;

import com.adakoda.android.statetest.R.id;

public class StateTestActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        step1Sample();
//        step2Sample();
//        step3Sample();
    }

    // ------------------------------------------------------------------
    // Step1
    // 実行するには、SDカード直下に"image.png"を置いてください
    // ------------------------------------------------------------------

    private static String STEP1_IMAGE_FILENAME = "image.png";

    private void step1Sample() {
        // ファイルパス文字列作成(例:"/mnt/sdcard/image.png")
        StringBuilder builder = new StringBuilder();
        builder.append(Environment.getExternalStorageDirectory().toString());
        builder.append(File.separator);
        builder.append(STEP1_IMAGE_FILENAME);
        String filePath = builder.toString();
        // 指定したパスからDrawableを作成
        Drawable drawable = Drawable.createFromPath(filePath);
        final ImageView imageView = (ImageView) findViewById(id.imageview);
        imageView.setBackgroundDrawable(drawable);
    }

    // ------------------------------------------------------------------
    // Step2
    // 実行するには、SDカード直下に
    // "image_normal.png"、"image_pressed.png"、
    // を置いてください。
    // 画像を押下すると、イメージが切り替わります。
    // ------------------------------------------------------------------

    private static String STEP2_STATE_FILENAME_FORMAT = "image_%s.png";
    private static String STEP2_STATE_FILENAME_SUFFIX_NORMAL = "normal";
    private static String STEP2_STATE_FILENAME_SUFFIX_PRESSED = "pressed";

    private void step2Sample() {
        // ファイルパス文字列のフォーマット文字列作成(例:"/mnt/sdcard/image_%s.png")
        StringBuilder builder = new StringBuilder();
        builder.append(Environment.getExternalStorageDirectory().toString());
        builder.append(File.separator);
        builder.append(STEP2_STATE_FILENAME_FORMAT);
        String filePathFormat = builder.toString();

        // StateListDrawable作成
        StateListDrawable stateListDrawable = new StateListDrawable();
        {
            // 押下状態のDrawable作成
            {
                // 例:"/mnt/sdcard/image_pressed.png"
                String pathPressed = String.format(filePathFormat,
                        STEP2_STATE_FILENAME_SUFFIX_PRESSED);
                Drawable drawablePressed = Drawable.createFromPath(pathPressed);
                stateListDrawable.addState(
                        new int[] { android.R.attr.state_pressed },
                        drawablePressed);
            }
            // 通常の状態のDrawable作成
            {
                // 例:"/mnt/sdcard/image_normal.png"
                String pathNormal = String.format(filePathFormat,
                        STEP2_STATE_FILENAME_SUFFIX_NORMAL);
                Drawable drawableNormal = Drawable.createFromPath(pathNormal);
                stateListDrawable.addState(StateSet.WILD_CARD, drawableNormal);
            }
        }

        // 動的に作成したStateListDrawableを背景画像に設定
        final ImageView imageView = (ImageView) findViewById(id.imageview);
        imageView.setBackgroundDrawable(stateListDrawable);
    }

    // ------------------------------------------------------------------
    // Step3
    // 実行するには、SDカード直下に
    // "image_00_normal.png"、"image_00_pressed.png"、
    // "image_01_normal.png"、"image_01_pressed.png"、
    // を置いてください。
    // 画像を押下すると、イメージが切り替わります。
    // ------------------------------------------------------------------

    private static final int STEP3_FILE_NUM = 2; // とりあえず
    private static String STEP3_STATE_FILENAME_FORMAT = "image_%02d_%s.png";
    private static String STEP3_STATE_FILENAME_SUFFIX_NORMAL = "normal";
    private static String STEP3_STATE_FILENAME_SUFFIX_PRESSED = "pressed";

    private void step3Sample() {
        // ファイルパス文字列のフォーマット文字列作成( 例:"/mnt/sdcard/image_%02d_%s.png")
        StringBuilder builder = new StringBuilder();
        builder.append(Environment.getExternalStorageDirectory().toString());
        builder.append(File.separator);
        builder.append(STEP3_STATE_FILENAME_FORMAT);
        String filePathFormat = builder.toString();

        // LevelListDrawable作成
        final LevelListDrawable levelListDrawable = new LevelListDrawable();
        for (int i = 0; i < STEP3_FILE_NUM; i++) {
            // StateListDrawable作成
            StateListDrawable stateListDrawable = new StateListDrawable();
            {
                // 押下状態のDrawable作成
                {
                    // 例:"/mnt/sdcard/image_00_pressed.png"
                    String pathPressed = String.format(filePathFormat, i,
                            STEP3_STATE_FILENAME_SUFFIX_PRESSED);
                    Drawable drawablePressed = Drawable
                            .createFromPath(pathPressed);
                    stateListDrawable.addState(
                            new int[] { android.R.attr.state_pressed },
                            drawablePressed);
                }
                // 通常の状態のDrawable作成
                {
                    // 例:"/mnt/sdcard/image_00_normal.png"
                    String pathNormal = String.format(filePathFormat, i,
                            STEP3_STATE_FILENAME_SUFFIX_NORMAL);
                    Drawable drawableNormal = Drawable
                            .createFromPath(pathNormal);
                    stateListDrawable.addState(StateSet.WILD_CARD,
                            drawableNormal);
                }
            }
            // LevelListDrawableのn番目に作成したStateListDrawableを追加
            levelListDrawable.addLevel(i, i, stateListDrawable);
        }

        // 動的に作成したLevelListDrawableを背景画像に設定
        final ImageView imageView = (ImageView) findViewById(id.imageview);
        imageView.setBackgroundDrawable(levelListDrawable);
        // 初期状態として0番目のStatListDrawableを設定
        levelListDrawable.setLevel(0);
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 押下時、次のStatListDrawableに切り替える
                int newLevel = (levelListDrawable.getLevel() + 1)
                        % STEP3_FILE_NUM;
                levelListDrawable.setLevel(newLevel);
            }
        });
    }

}

 

リソース

  • res / layout / main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/imageview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:clickable="true"
        android:focusable="true" />

</LinearLayout>

通知アイテムの背景画像を差し替えてみました。

単一画像だと飽きてしまうので、16 種類の画像をランダムで採用するようにしています。

新しい通知が来るたびに幸せになれます。 

galaxy_nexus_imas.png

※タップするとハイライトします(上図では4番目のアイテム)

 

ソースコード

  • android-4.0.3_r1\frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\PhoneStatusBar.java
// ->
static int cNotificationRowBgResIds[] = {
    R.drawable.notification_row_bg_ai,
    R.drawable.notification_row_bg_amimami,
    R.drawable.notification_row_bg_azusa,
    R.drawable.notification_row_bg_chihaya,
    R.drawable.notification_row_bg_eri,
    R.drawable.notification_row_bg_haruka,
    R.drawable.notification_row_bg_hibiki,
    R.drawable.notification_row_bg_iori,
    R.drawable.notification_row_bg_kotori,
    R.drawable.notification_row_bg_makoto,
    R.drawable.notification_row_bg_miki,
    R.drawable.notification_row_bg_ritsuko,
    R.drawable.notification_row_bg_ryo,
    R.drawable.notification_row_bg_takane,
    R.drawable.notification_row_bg_yayoi,
    R.drawable.notification_row_bg_yukiho };
// <-

    void applyLegacyRowBackground(StatusBarNotification sbn, View content) {
        if (sbn.notification.contentView.getLayoutId() !=
                com.android.internal.R.layout.status_bar_latest_event_content) {
            int version = 0;
            try {
                ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.pkg, 0);
                version = info.targetSdkVersion;
            } catch (NameNotFoundException ex) {
                Slog.e(TAG, "Failed looking up ApplicationInfo for " + sbn.pkg, ex);
            }
            if (version > 0 && version < Build.VERSION_CODES.GINGERBREAD) {
//                content.setBackgroundResource(R.drawable.notification_row_legacy_bg);
            } else {
//                content.setBackgroundResource(R.drawable.notification_row_bg);
            }
        }
// -> 別にここじゃなくてもいいけど。。。
int index = (int) (Math.random() * cNotificationRowBgResIds.length);
int resId = cNotificationRowBgResIds[index];
content.setBackgroundResource(resId);
// <-
    }

 

リソース

  • android-4.0.3_r1\frameworks\base\packages\SystemUI\res\drawable に以下のファイルを追加
    • notification_row_bg_ai.xml
    • notification_row_bg_amimami.xml
    • notification_row_bg_azusa.xml
    • notification_row_bg_chihaya.xml
    • notification_row_bg_eri.xml
    • notification_row_bg_haruka.xml
    • notification_row_bg_hibiki.xml
    • notification_row_bg_iori.xml
    • notification_row_bg_kotori.xml
    • notification_row_bg_makoto.xml
    • notification_row_bg_miki.xml
    • notification_row_bg_ritsuko.xml
    • notification_row_bg_ryo.xml
    • notification_row_bg_takane.xml
    • notification_row_bg_yayoi.xml
    • notification_row_bg_yukiho.xml

※notification_row_bg_haruka.xml のサンプルリソース例

<selector xmlns:android="http://schemas.android.com/apk/res/android"
        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
    <item android:state_pressed="true"
            android:drawable="@drawable/notification_item_background_haruka_pressed" />
    <item android:state_pressed="false"
            android:drawable="@drawable/notification_item_background_haruka" />
</selector>
  • android-4.0.3_r1\frameworks\base\packages\SystemUI\res\drawable-hdpi に以下の画像を追加
    • notification_item_background_ai.png
    • notification_item_background_ai_pressed.png
    • notification_item_background_amimami.png
    • notification_item_background_amimami_pressed.png
    • notification_item_background_azusa.png
    • notification_item_background_azusa_pressed.png
    • notification_item_background_chihaya.png
    • notification_item_background_chihaya_pressed.png
    • notification_item_background_eri.png
    • notification_item_background_eri_pressed.png
    • notification_item_background_haruka.png
    • notification_item_background_haruka_pressed.png
    • notification_item_background_hibiki.png
    • notification_item_background_hibiki_pressed.png
    • notification_item_background_iori.png
    • notification_item_background_iori_pressed.png
    • notification_item_background_kotori.png
    • notification_item_background_kotori_pressed.png
    • notification_item_background_makoto.png
    • notification_item_background_makoto_pressed.png
    • notification_item_background_miki.png
    • notification_item_background_miki_pressed.png
    • notification_item_background_ritsuko.png
    • notification_item_background_ritsuko_pressed.png
    • notification_item_background_ryo.png
    • notification_item_background_ryo_pressed.png
    • notification_item_background_takane.png
    • notification_item_background_takane_pressed.png
    • notification_item_background_yayoi.png
    • notification_item_background_yayoi_pressed.png
    • notification_item_background_yukiho.png
    • notification_item_background_yukiho_pressed.png

notification_item_background_imas.png

※本サイトでの画像配布はありませんので、画像は各自で適当なものを準備してください(上記は、あくまでも参考イメージです)

電源オフ時の充電中(charger mode)画像を差し替えてみました。

充電完了(100%)時、幸せになれます。

imas.png

 

差し替えたファイル

  • android-4.0.3_r1\system\core\charger\images\battery_*.png

res_battery.png

※上記キャプチャの一番上の段がAOSPオリジナル画像、二番目の段が差し替えた画像

 

あわせて読みたい

1

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