先日のエントリ「通知アイテムの背景画像を差し替える」は、
ビルド時に画像が固定で埋め込まれているため、画像の差し替えが大変だけでなく、
ビルドできる人しか画像を差し替えることができない、という問題がありました^^;
そこで、外部ストレージに配置した複数の画像を状態付きで設定するコードを作成し、検証してみたところ、
問題なく動作することを確認できました。
※「画像を状態別に設定できる」かつ「1つの View に複数の画像を設定できる」という点がポイントです
この方法を使用することで、カスタム ROM の画像リソースを、外部ファイル化することができそうです^^
※見ろ!先日のエントリのリソースのハードコードがゴミのようだ!
以下、解説です。
外部ストレージから読み込んだ画像を設定するには(Step1)
外部ストレージにある画像を Drawable#createFromPath(String) を使用して作成し、
画像を設定したい View に対し View#setBackgroundDrawable(Drawable) を呼び出します。
ここまでで、外部ストレージにある画像を、任意の View に適用できます。
外部ストレージから読み込んだ画像を状態付きで設定するには(Step2)
Step1 に加え、画像を「通常状態」「押下状態」など、状態付きで設定してみます。
通常、画像を状態付きで設定するには、<selector> を記述した xml リソースファイルを指定しますが、
ここでは、ソースコードから動的に作成してみます。
まず、 StateListDrawable インスタンスを作成し、
次に、追加したい状態を StateListDrawable#addState(int[], Drawable) を使用して追加し、
画像を設定したい View に対し View#setBackgroundDrawable(Drawable) を呼び出します。
ここまでで、外部ストレージにある画像を「状態付き」で、任意の View に適用できます。
外部ストレージから読み込んだ複数の画像を状態付きで設定するには(Step3)
Step2 に加え、複数の画像(画像00、画像01、・・・)を設定してみます。
通常、複数の画像を設定するには、<level-list> を記述した xml リソースファイルを指定しますが、
ここでは、ソースコードから動的に作成してみます。
まず、LevelListDrawable インスタンスを作成し、
次に、追加したい画像を LevelListDrawable#addLevel(int, int, Drawable) を使用して追加し、
画像を設定したい View に対し View#setBackgroundDrawable(Drawable) を呼び出します。
追加した任意の画像に切り替えるには、Drawable#setLevel((int) を呼び出します。
これで、外部ストレージにある「複数」の画像を「状態付き」で、任意の View に適用できます。
以下、ソースコードサンプルです。
※サンプルのためエラー処理などは省略しています
ソースコード
- 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>
