‘tips’ タグのついている投稿

Androidのグラデーションが縞にならないようにする

スプラッシュ画面などで、グラデーションのかかった画像を一枚表示することがあります。

その際、グラデーションにマッハバンドと言われる縞が出ることがあり、グラデーションがきれいに見えない原因になります。

解決方法は簡単で、以下2ステップで解消できます。

1.スプラッシュのActivityでWindowのPixelFormatを指定する

    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        Window window = getWindow();
        window.setFormat(PixelFormat.RGBA_8888);
    }

2.画像を全解像度用に用意する

ldpi,mdpi,hdpi,xhdpiそれぞれに適切に拡大・縮小した画像を用意しておきます。これをやらないと、内部的に自動で拡大縮小されるので、その際にマッハバンドが発生します。


AlertDialogのViewとの隙間を埋める

AndroidのAlertDialogを継承したクラスで、タイトルなし、setViewでダイアログいっぱいにViewを広げようとしたら、上下に隙間が開いてしまいました。

image

解決方法は簡単で、setView(View view)ではなく、setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight, int viewSpacingBottom)を使うだけです。

image

コードのサンプルは以下のような感じです。

public class MyDialog extends AlertDialog {

    public MyDialog(Context context) {
        super(context);
    }

    @Override
    public void show() {

        LayoutInflater inflater = (LayoutInflater) this.getContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        View layout = inflater.inflate(R.layout.my_dialog, null);

        GridView gridView = (GridView) layout.findViewById(R.id.gridView1);

        // this.setView(layout); // <- 上下に余白ができる
        this.setView(layout, 0, 0, 0, 0); // <- ぴったり埋まる

        super.show();

    }

}

ちなみに、AlertControllerの中ではこのように処理されています。

            customPanel = (FrameLayout) mWindow.findViewById(R.id.customPanel);
            FrameLayout custom = (FrameLayout) mWindow.findViewById(R.id.custom);
            custom.addView(mView, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
            if (mViewSpacingSpecified) {
                custom.setPadding(mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
                        mViewSpacingBottom);
            }
            if (mListView != null) {
                ((LinearLayout.LayoutParams) customPanel.getLayoutParams()).weight = 0;
            }

R.id.customはalert_dialog.xmlで定義されています。

    <FrameLayout android:id="@+id/customPanel"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1">
        <FrameLayout android:id="@+android:id/custom"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingTop="5dip"
            android:paddingBottom="5dip" />
    </FrameLayout>

上下5dip開いてますね。


androidでセパレーター画像を横幅いっぱいにする

長さが足りない素材で、セパレーターを画面の幅いっぱいに広げるテクニック。

下の画像は、上側のセパレーターが広げたもので、下のセパレーターが素材のままです。

image

layout.xml

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

    <ImageView
        android:id="@+id/separator"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:contentDescription="@string/separator"
        android:scaleType="fitXY"
        android:src="@drawable/separator_repeat" />

</LinearLayout>

separator_repeat.xml

<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/separator"
    android:dither="true"
    android:tileMode="mirror" />

肝はbitmapのtileModeと、ImageViewのscaleTypeです。

tileMode="mirror"は左右を反転しながら繰り返し表示をしてくれるので、繋ぎ目が目立ちにくくなります。

scaleType="fitXY"は、指定しないとImageViewが画面いっぱいに広がっていても、画像は広がってくれません。指定すれば、タイル表示が有効になります。


OptionsMenuの文字色を変更する

Activityで端末のメニューボタンを押したときに出てくるオプションメニューですが、文字の色が変えられなくて困っていました。

Themeのandroid:panelTextAppearanceで変えられるのかと思っていたのだけど、なぜかうまく行かず。

背景画像はテーマで変更できるのですが。

リフレクションを使ってテキストカラーの変更をしてみました。Android API 8で確認しています。

デフォルト

device-2011-10-22-120919

文字色変更

device-2011-10-22-135742

Activityのコード

public class CustomizedMenuActivity extends Activity {
	public static final int MENU_ID_COFFEE = 1;
	public static final int MENU_ID_LOVE = 2;
	public static final int MENU_ID_RECYCLE = 3;

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

	private TextView getMenuItemView(MenuItem item) {
		try {
			Class<?> c = item.getClass(); // itemはMenuItemImplのインスタンス
			Class<?>[] paramTypesGetItemView = { int.class, ViewGroup.class };
			Method method = c.getDeclaredMethod("getItemView", paramTypesGetItemView);

			// getItemViewはprivateメソッドなのでアクセス可能に変更する
			method.setAccessible(true);

			// IconMenuItemViewを取得できる
			TextView view = (TextView) method.invoke(item, new Object[] { 0, null });

			return view;
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// メニューアイテムの追加
		MenuItem menuItemCoffee = menu.add(Menu.NONE, MENU_ID_COFFEE,
				Menu.NONE, this.getText(R.string.menu_coffee)).setIcon(
				R.drawable.icon_coffee);
		MenuItem menuItemLove = menu.add(Menu.NONE, MENU_ID_LOVE, Menu.NONE,
				this.getText(R.string.menu_love)).setIcon(R.drawable.icon_love);
		MenuItem menuItemRecycle = menu.add(Menu.NONE, MENU_ID_RECYCLE,
				Menu.NONE, this.getText(R.string.menu_recycle)).setIcon(
				R.drawable.icon_recycle);

		try {
			TextView viewCoffee = getMenuItemView(menuItemCoffee);
			TextView viewLove = getMenuItemView(menuItemLove);
			TextView viewRecycle = getMenuItemView(menuItemRecycle);

			// テキストの色を変える
			viewCoffee.setTextColor(0xFFB5985A);
			viewCoffee.setTextSize(14);
			viewCoffee.setTextScaleX(0.8f);

			viewLove.setTextColor(0xFFCF7D5B);
			viewLove.setTextSize(22);
			viewLove.setTypeface(Typeface.DEFAULT_BOLD);

			viewRecycle.setTextColor(0xFF8A6381);
			viewRecycle.setTextSize(16);
			viewRecycle.setTypeface(Typeface.create(Typeface.SERIF,
					Typeface.BOLD_ITALIC));

		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		}

		return super.onCreateOptionsMenu(menu);
	}
}

MenuItemの実体はMenuItemImplクラスで、getItemViewメソッドを使ってIconMenuItemViewクラスを取り出しています。

IconMenuItemViewクラスはTextViewを継承しているので、キャストすればテキストカラーやフォントサイズなどの変更ができます。

ちなみに、このあと、メニューのセパレータ(divider)画像の変更とアイコンと文字の配置の変更もやりました。それは次以降のエントリで。

オプションメニューのアイコンと文字を横並びにしたかった : blog.loadlimit – digital matter –


複数の入力欄にまたがるValidationのエラーを特定の入力欄に表示させたい場合

sfFormでSymfony 1.4のお話。

PostValidatorのエラーメッセージをどこに出せばいいんだ?という状況で使用できます。

class SomethingInputForm extends BaseForm
{
    public function configure()
    {
        // ...
        $this->validatorSchema->setPostValidator(
            new sfValidatorCallback(array('callback' => array($this, 'myCallbackFunc')))
        );
    }

    public function myCallbackFunc($validator, $values) {
        // 何かバリデーション
        if (!($values['input1'] == $values['input2'] == $values['input3'])) {
            $error = new sfValidatorError($validator, 'error message ...');
            throw new sfValidatorErrorSchema($validator, array('input1' => $error)); 
        }
        
        return $values;
    }
}

こんな感じで、input1のエラーとして出力できるので、テンプレートのエラー表示が楽になります。


iPhoneな人への地図URLの送り方

iPhone/iPod touchでもPCでも共通で見られるGoogleマップのURLを作りたくなったのでメモ。

というか、iPhone上のSafariから、Googleで場所を検索すると出てくるリンクですね。iPhoneの場合、このURLを開くと地図アプリが開きます。

http://maps.google.co.jp/?q=東京都新宿区西新宿2丁目8−1

http://maps.google.co.jp/?q=248-0006

PCのGoogleマップで地図のURL開くとめちゃくちゃ長くなってしまうので、こういう指定の仕方覚えておくと便利かも。

何がやりたかったかって言うと、twitterで○○の場所どこ?って聞かれて、とっさに住所だけ答えてしまったけど、iPhoneで使える地図のURLにしたほうが便利だったな、って思って。