XMLなしで、ボタンの外観を変える

 ボタンの外観を変えるには、

void setBackgroundColor(int color)
void setBackgroundDrawable(Drawable d)
void setBackgroundResource(int resid)


 なんかを使う。
 ただし、これらで画像や色を指定をした場合、カーソル表示や決定の反応なんかはなくなってしまう。
 それは寂しいし、第一、なにを選択しているのかわからず、ユーザーライクではない…。
 それじゃ、どうするかっていうと、xml指定することになる。

 でも、動的に変えたい場合もあるよね?
 たとえば、ユーザーに色変更させたい、Webからダウンロードしたものにしたい、etc…。
 そんなときはどうするか?
 結論からいうと、Button class をベースに新規class を作るのが早道みたいだ。
 でも一応、調べてみたことを書いておくよ。
 それと、サンプルのソースも。

外観設定の仕組み(xml)

 おおざっぱにいうと、仕組みはこんな感じみたい。

	button.xml (ファイル名任意)
	android:background
		↑
		style.xml (ファイル名任意)
		<item
			android:state_focused="true"
			android:color="@color/color1" />
		<item
			android:state_pressed="true"
			android:state_enabled="false"
			android:color="@color/color2" />
		……

 で、android:background に相当するmethod が、先のsetBackgroundになる。
 focuse やpress を設定するmethod はない。
 それじゃ、style.xml に相当するものを用意し、設定すればよいのだろうか…?

StateListDrawable

 StateListDrawable については、実は確かめてない。
 だからこの項目は、推測のものになるので、読みとばしてくれていいよ。(^_^;

 ざっと見のリファレンスからすると、StateListDrawableは、xmlみたいな設定を作成・編集するものみたいだ。
 DrawableContainer の継承で、大元はDrawable だから、Drawable指定の引数に使用できるハズ。
 きっと、setBackgroundDrawable(Drawable d) に渡せば、xml指定と同じことができると思う。

 StateListDrawable にあるmethod の…

void	 addState(int[] stateSet, Drawable drawable)
	Add a new image/string ID to the set of images.

 の第一引数・stateSet はおそらく、Button 内で定義されている↓なんかだろう。

(Button)
int[]	ENABLED_FOCUSED_SELECTED_STATE_SET	Indicates the view is enabled, focused and selected.

 おそらく、↓みたいな指定をすればいいのかな?

stateListDrawable.addState( 
	Button.ENABLED_FOCUSED_SELECTED_STATE_SET,
	drawable );

 そんで最終的に、mutate()でDrawable として出力するんじゃないかな?

Drawable	 mutate()
	Make this drawable mutable.
(StateListDrawable)

 そのDrawable をsetBackgroundDrawable した後に、

(Button)
void	 refreshDrawableState()
	Call this to force a view to update its drawable state.

 …は実行しなくてもいいのかな…?

 ちなみに、テキストカラーではColorStateList がStateListDrawable に相当するんじゃないかな?

 …と。調べてみたのはココまで。
 ここから先は実証実験ってことになるんだけど…。
 その試行錯誤のコストは、結果に見合わない気がしたんだ。(^_^;
 なぜなら、色々やってみて失敗するより、任意のButton class を作ってしまう方がてっとり早く確実だから。
 iアプリなんかだと、アプリ容量が懸念されるところだけど、Android はそんな心配さらさらないからね。
 StateListDrawable については、ココで打ち切ることにした。

任意のButton class を作る

 結局のところ、xml指定しない場合、この方法がよいみたいだ。
 そんなに難しいことでもないしね。
 classを初めてイジる人には、ちょうどいいお題じゃないかな?

 サンプルのソースはこんな感じで作ってみた。
 素のボタンとそう変わりないから、Buttonを置き換えるだけで使えるよ。

package com.migimaki.android;

import android.widget.Button;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
import android.view.animation.AlphaAnimation;
import android.view.animation.CycleInterpolator;
import android.view.animation.TranslateAnimation;

import android.util.Log;


public class MyBut extends Button {

	public MyBut ( Context c ){
		super( c );
		//--
		// 背景色設定
		setBackgroundColor(Color.argb(128, 0, 0, 255));	// 青
		//--
	}

	// Event : Focus Changed
	public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect){

		if ( isFocused() ){
			// 背景色設定
			setBackgroundColor(Color.argb(128, 0, 255, 0));	// 緑
			
			////
			TranslateAnimation translate = new TranslateAnimation( 0, 100, 0, 0 );
			translate.setDuration(4000);
			translate.setInterpolator(new CycleInterpolator(1));
			startAnimation(translate);
			////--
		} else {
			// 背景色設定
			setBackgroundColor(Color.argb(128, 0, 0, 255));	// 青

			////
			AlphaAnimation alpha = new AlphaAnimation( 1, 0.75f );
			alpha.setDuration(4000);
			alpha.setInterpolator(new CycleInterpolator(1));
			startAnimation(alpha);
			////--
		}
	}
}

 選択されていない状態は青。
 選択されると、緑。

 と、ボタンの背景色が変わる。

 決定された状態は、リスナー側のonClick()内でやることになる。
 class内でやる方が美しいんだけど、イマイチ、その意味を見いだせなかった。
 それと、onTouchEvent なんかでやってみたら、onClickが発生しなくなってしまったんだ。
 色々イジくるより、onClick でやる方がてっとり早い。

 オマケで、Animation のエフェクトをつけてみた。
 フォーカスが外れると、半透明で点滅。
 選択されると、横方向に揺れる。
 縦にボタンを連ねた状態で、カーソル・キーでフォーカスを連続移動させると、階段を登るみたいでちょっとおもしろい。

Comments are closed.