ページ

ラベル UI の投稿を表示しています。 すべての投稿を表示
ラベル UI の投稿を表示しています。 すべての投稿を表示

2013/04/24

Android 単一項目を選択させるListViewの設定

|
今さら書くものでもないですが、AndroidのListViewにおけるonClickやonItemClick、さらに項目にラジオボタンや チェックボックスがついていた時など毎回混乱するので「この仕様ならこれで動く」というパターンをメモします。
以下は
  • ListViewにラジオボタンつき項目を表示
  • 項目は1つだけ選択可能
  • 項目が選択されたら(タップされたら)何かする
というパターンです。(以下のイメージ)



リスナーの設定はsetOnItemSelectedListener()ではなくsetOnItemClickListener()であること、 項目を初期選択させるのはsetSelection()ではなくsetItemChecked()であることが注意する点でしょうか。

2013/04/06

Android XMLだけでできたボタン

|
画像でなくXMLのみで作ったボタンを集めたAndroid用のライブラリをGitHubに公開しました。
RichButtons
使い方は、RichButtons/libraryをプロジェクトとして取り込むか、中身にある実体のXMLを直接プロジェクトへコピーしていただき、 styleで定義済みのスタイルをButtonに適用します。

ライブラリ、とは言ってもグラデーションなどをlayerで重ねただけXMLを集めただけなのですが、、、 1から作ると意外に時間がかかると思うのでよろしければご利用ください。
通常の状態と「pressed」の状態の2種類が定義されているので、タップするとちゃんと見栄えは変わります。
ちなみに端末画像つきのスクリーンショットは以下で作成したものです。
http://developer.android.com/distribute/promote/device-art.html

2013/01/11

Android Drawableのattrを定義

|
ライブラリなどを作る際、styleを使ってDrawableをカスタマイズ可能にするときの方法です。 ライブラリ側のattrs.xmlに属性を定義します。formatはreferenceで良いです。 styleableも定義します。 ライブラリ側のアイコンをロードする部分です。 利用アプリ側で、独自の画像を指定するスタイルを定義します。(styles.xmlなど) 利用アプリ側で、スタイルを適用します。(AndroidManifest.xml) 以上です。 注意しなければいけないのは、アイコンをロードする部分で mContextと書いているContextはActivityのテーマが適用されているものでなければならない点です。 Activityそのものなら有効ですが、Activity#getBaseContext()をContextとして使ってしまうと、適用されているテーマが取得できません。 (他にも有効なものがあるかもしれません。)

2012/08/19

Android TextViewのURLをリンクにしつつURL以外もクリック可能にする

|
TextViewに含まれるURLをリンクにして、クリックするとブラウザでリンク先を表示するケースはよくあると思います。
それに加えて、URL以外の場所をクリックした場合はそのイベントを拾いたかったのですが、なかなかその方法が見つからず苦労したので、ここに整理したものを公開します。
同じことをされたい方がいらっしゃいましたらどうぞご利用ください。(LinkUtils.java)
※もう一つのActivityは使用例です。
https://gist.github.com/3394338
上記の抜粋ですが、使い方は以下のようになります。
        String testStr = "URLの形式のみブラウザで開きます。http://www.yahoo.co.jp/とhttp://d.hatena.ne.jp/をリンク表示にしてクリック可能にしました。";

        TextView textView = new TextView(this);
        textView.setText(testStr);
        LinkUtils.autoLink(textView, new LinkUtils.OnClickListener() {
            @Override
            public void onLinkClicked(final String link) {
                Log.i("SensibleUrlSpan", "リンククリック:" + link);
            }

            @Override
            public void onClicked() {
                Log.i("SensibleUrlSpan", "ビュークリック");
            }
        });

        setContentView(textView);
文字列を設定したTextViewを渡して、LinkUtils.autoLink()を呼び出します。 単にURLをリンクにするのであればこれだけで動きます。
URLがクリックされたときと、その他の場所がクリックされたときをハンドリングする場合は、第2引数に専用のリスナーを設定します。
URLとして認識される文字列はLinkUtilsの中に正規表現で持っていますが、autoLink()の第3引数で変更可能です。
内容としては、内部でURLSpanとLinkMovementMethodを継承したクラスを作り、リンクがクリックされたかどうかを検知できるようにして実現しています。

以下を参考にさせて頂きました。ありがとうございます。

http://www.techmaru.net/wordpress/20101015/textviewlink/

2012/08/03

Android ViewPagerを使った円形のインジケータ

|
左右にスワイプしてページを切り替えるViewPagerというものがありますが、円形のインジケータを使用したサンプルを作成しました。
viewpagersample

既にライブラリとしてViewPagerIndicatorというすばらしいものがあります。が、別のAndroidプロジェクトをいくつも組み込むと環境構築手順やビルド手順も煩雑になりますし、単に円形タイプのインジケータを組み込みたいだけなのでこちらは使いたくない…という方がもしいらしたら、参考にしてみてください。
小さいクラスなので中身もすぐに理解できると思います。

2012/06/12

Android Theme.Lightでのダイアログの文字色

|
テーマに@android:style/Theme.Lightを使用すると、
背景が白の明るい配色になり、文字色は黒系の色になります。

@android:style/Themeの場合は逆です。こちらの場合は問題ないのですが、 @android:style/Theme.Lightを使うときにダイアログを独自のレイアウトで組み立てると、 背景色は黒なのに、文字色も黒系(グレー)になってしまいます。
つまり、各TextViewに文字色を都度設定しなければならないようです。

これを避けるために、独自レイアウトの背景色がTheme.Lightと同じになるよう
テーマをカスタマイズしよう、という案を思いつきますが ダイアログの背景色を変えると、
今度は標準的なAlertDialogに影響が出ます。
AlertDialog.Builder#setView()でレイアウトを変えない普通のAlertDialogの場合、 Theme.Lightを適用していても文字色が白系のまま変わらないためです。
背景白・文字白系で非常に見にくくなります。
*AlertDialogの色を変えられるのは背景色のみのようです。

背景・文字色ともに変更ができるのは
レイアウトを指定しないAlertDialogではなく
レイアウトを指定したAlertDialogなので
レイアウトファイルの中で背景色と文字色両方を指定するのが正解なのだと思います。

ちなみに上記はAndroid 2.3.3とAndroid 4.0(エミュレータ)で確認しています。

2012/03/31

Android ListViewがクリックできない原因の一例

|

ListViewをカスタマイズして以下のような行のレイアウトを作成しましたが、
クリックできなくなってしまいました。
ListView#setOnItemClickListener()を使っても、クリックのイベントに反応しなくなったのです。
しかしなぜか ListView#setOnItemLongClickListener() は有効でした。

レイアウトは以下です。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:padding="2dp">

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:textSize="18sp" />

    <TextView
        android:id="@+id/updated_at"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true" />

    <TextView
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_below="@id/title"
        android:inputType="text" />

</RelativeLayout>

試行錯誤した挙げ句、原因は最後の TextView に指定している android:inputType だとわかりました。
これが設定されていると、この TextView が focusable になり、
ListViewではクリックイベントを拾えなくなるようです。

対策は、以下のように focusable="false" とすることです。
これで、ListView#setOnItemClickListener() が有効になります。

    <TextView
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_below="@id/title"
        android:inputType="text"
        android:focusable="false"  />

2011/09/23

Android ioschedのDashboardLayoutをタイトルなしのダイアログで使う

|
Google I/Oのアプリioschedに含まれているDashboardLayoutをカスタムダイアログで適用しようとしましたが、タイトルバーを非表示にしたところ、レイアウトが崩れてしまいました。
DashboardLayoutを使っているActivityのレイアウトファイルをそのまま使ったわけではなく、 Fragmentを使わないように書き換えたのですが、その際に誤って削ってしまった「android:layout_weight="1"」が原因でした。
以下、詳細のメモです。

まず、最初にダイアログに使ったレイアウトです。
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minHeight="300dp"
    android:minWidth="300dp"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:background="@drawable/background">
    <TextView
        android:text="@string/title_a"
        android:textColor="@color/accent"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center" />
    <com.google.android.apps.iosched.ui.widget.DashboardLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1">
        <Button
            android:id="@+id/button_a"
            style="@style/DashboardButton"
            android:text="@string/label_button_a"
            android:drawableTop="@drawable/icon" />
        :
    </com.google.android.apps.iosched.ui.widget.DashboardLayout>
</LinearLayout>
これをタイトルバーありの状態で表示すると以下のようになります。

2011/08/24

Android PreferenceScreenのタイトルバーをカスタマイズする

|
Androidの設定画面では、PreferenceScreenをクリックすると次の階層の設定画面に進みます。
これはActivityのように思えますが、実際にはダイアログが表示されています。

このタイトル部分のスタイルを変えようと思ったのですが、最初の画面だけが変更され、PreferenceScreenで遷移した先の画面のタイトルバーを変えられず、苦労したので記録しておきます。
結論から言うと、PreferenceScreenのタイトルバーはカスタマイズできず、同じような見た目・挙動になる別の方法をとりました。

まず該当部分をカスタマイズできるのか、Androidのソースコードを見てみたところ、android.preference.PreferenceScreenに以下のような記述がありました。
private void showDialog(Bundle state) {
    Context context = getContext();
    if (mListView != null) {
        mListView.setAdapter(null);
    }
    mListView = new ListView(context);
    bind(mListView);

    // Set the title bar if title is available, else no title bar
    final CharSequence title = getTitle();
    Dialog dialog = mDialog = new Dialog(context, TextUtils.isEmpty(title)
            ? com.android.internal.R.style.Theme_NoTitleBar
            : com.android.internal.R.style.Theme);
    dialog.setContentView(mListView);
    if (!TextUtils.isEmpty(title)) {
        dialog.setTitle(title);
    }
    dialog.setOnDismissListener(this);
    if (state != null) {
        dialog.onRestoreInstanceState(state);
    }

    // Add the screen to the list of preferences screens opened as dialogs
    getPreferenceManager().addPreferencesScreen(dialog);
    
    dialog.show();
}

2011/05/21

Android Preferenceの内容が他の項目に変わる

|
カスタマイズしたPreferenceを作りました。
しばらく画面内に1つしかそのPreferenceを使っていなかったのですが、
数を増やしたところおかしなことになりました。

増やしたもののうち、Dependencyを設定しているカスタムPreferenceがあるのですが
依存先のチェックボックスを操作したところ
そのカスタムPreferenceと他のカスタムPreferenceが入れ替わる現象が起こりました。
さらに、カスタムPreferenceをタップしてダイアログを開き、
OKで閉じても他の項目と表示が入れ替わってしまいます。
しかし、他の項目と思ってタップすると、開くダイアログは入れ替わり前の内容です。

Preferenceの一覧表示だけがおかしい?と見当をつけて調べたところ、
原因はonBindView()にありました。

2011/05/05

Android 見つけにくいResources$NotFoundExceptionの原因

|
TextViewに文字列や数値を表示する画面を作りました。
正しくレイアウトのXMLを記述して、プロジェクトも再ビルドして、R.javaにIDは存在しているはずなのに
Resources$NotFoundExceptionが出てしまいました…。

しばらく例外メッセージの中の「見つからない」と言われているIDを見ていて、気づきました。

TextViewにはsetText(CharSequence text)だけでなくsetText(int resid)があるのです。

そのため、以下のようなコードを書くとコンパイルエラーは出ません。
int price = 1000;
((TextView) view.findViewById(R.id.price)).setText(price);

これを動かすと、リソースID「1000」のR.stringを探そうとするので上記の例外が発生します。

例えば以下のようにres/values/string.xmlが書いてあったとします。
<resources>
    <string name="app_name">Test</string>
</resources>

この場合は以下のようにすれば「Test」がTextViewに表示されます。
((TextView) view.findViewById(R.id.price)).setText(R.string.app_name);

最初の例外が出る例は、以下のように文字列に変換すれば動きます。
int price = 1000;
((TextView) view.findViewById(R.id.price)).setText(Integer.toString(price));

数値はCharSequenceではないので当たり前の動作なのですが、
コンパイルエラーが出ないので注意した方が良さそうです。
本当は、R.javaの定数がintなどのプリミティブ型でなく列挙型等になっていると、
間違ってint型を指定してもコンパイル時に検出できて良いのですけどね。

2011/05/04

Android (ソースコード解説) SlidingDrawerでwrap_contentを効かせつつ表示/非表示での高さを切り替える

|
SlidingDrawerを使って、「Gmailアプリでチェックボックスにチェックを入れたときに表示されるボタン群」のようなものをきれいに実現する方法です。

ソースコードのポイントを解説します。

※完成イメージと、それに至るまでの問題は以下で説明しました。
Android SlidingDrawerでwrap_contentを効かせつつ表示/非表示での高さを切り替える

※ソースコードは以下に掲載しました。
Android (ソースコード掲載) SlidingDrawerでwrap_contentを効かせつつ表示/非表示での高さを切り替える

Android (ソースコード掲載) SlidingDrawerでwrap_contentを効かせつつ表示/非表示での高さを切り替える

|
SlidingDrawerを使って、「Gmailアプリでチェックボックスにチェックを入れたときに表示されるボタン群」のようなものをきれいに実現する方法です。

完成イメージと、それに至るまでの問題は以下で説明しました。
Android SlidingDrawerでwrap_contentを効かせつつ表示/非表示での高さを切り替える

ソースコードをとりあえず載せます。
また長いので、解説は改めて投稿します…。

まずはレイアウトです。

Android SlidingDrawerでwrap_contentを効かせつつ表示/非表示での高さを切り替える

|
他の部品を隠さずにSlidingDrawerを使い、コンテンツの大きさに合わせてSlidingDrawerをレイアウトする方法についてです。この説明もタイトルも、一言でうまく伝えられないので、まずは完成イメージをまず載せます。
ソースコードは改めて投稿します。

完成イメージ

1. 画面の下方にスクロールする部品があります。(TextView)
2. ドロワーを開くと、スクロールするビューのスクロールバーがずれています。つまり、ドロワーで隠れてしまった部分を下方にスクロールすることで表示できます。
3. 実際、最下部までスクロールできます。(「終わり」という部分です。)
4. ドロワーを閉じると、スクロールビューが元のサイズに戻ります。

2011/05/03

Android アノテーションでRadioButtonの値をコード値として取得

|
Androidのラジオボタンは、HTMLのラジオボタンのようなvalueを持っていません。
「どのRadioButtonが選択されたか」と「選択されたRadioButtonのテキスト」くらいしか
そのラジオボタンを特定する情報がありません。

そのため、ラジオボタンの値をDBへ登録しようなどと考えていると
リソースIDとコード値の割り当てをハードコーディングすることになってしまいます。

例えば以下のような感じです。
RadioGroup radioGroup = (RadioGroup) activity.findViewById(R.id.radio_group_foo);
int checkedId = radioGroup.getCheckedRadioButtonId();
int value;
switch (checkedId) {
case R.id.radio_button_foo_1:
 value = 1;
 break;
case R.id.radio_button_foo_2:
 value = 2;
 break;
} 

このマッピングを色んなところに散らかすのは嫌です。

AndroidのUIに関するプログラミングである以上、
リソース(R.java)を扱うのは避けられそうにないので、外部の設定ファイルにマッピングを書くのは難しそうです。
そこで、アノテーションを使ってラジオボタンの値を設定するフィールドに情報をまとめることにしました。

ソースコードにマッピングを書くのは変わりませんが、
情報がまとまるのでコードは見やすくなるはずです。
イメージは、SAStrutsのような、アノテーションをpublicフィールドに定義する感じです。

以下、必要なアノテーションです。

Android TabHostで下側にタブを表示するときにビューが隠れないようにする方法

|
# これまであまりに一般的すぎる内容ばかり書いていたので、ある程度調べてもわからなかったこと、つまづいたことを中心に書いていきたいと思います。

AndroidでTabHostによるタブを作成するとき、下側にタブを作成するにはRelativeLayoutを使う必要があるようです。
しかしこのとき、メインのビューを下一杯まで広げると、タブでビューの一部が隠れてしまいました。

RelativeLayoutを使う場合の注意ですが、相対的な配置なので、
他の要素とどのような位置関係にあるのかを指定しなくてならないようです。

上側に表示するFrameLayoutにこれを指定することで解決しました。
android:layout_above="@android:id/tabs"

FrameLayoutはTabWidgetの上にある、という指定です。

以下、全体のレイアウトです。タブのコンテンツは含まれていません。

<?xml version="1.0" encoding="utf-8"?>
<TabHost
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@android:id/tabhost"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent">
 <RelativeLayout
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="vertical">
  <FrameLayout
   android:layout_alignParentTop="true"
   android:layout_above="@android:id/tabs"
   android:id="@android:id/tabcontent"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent" />
  <TabWidget
   android:id="@android:id/tabs"
   android:layout_alignParentBottom="true"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content" />
 </RelativeLayout>
</TabHost>