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

2013年5月22日水曜日

Action Barに検索Viewを設ける

Action Barに検索Viewを設ける

検索を行うViewは、EditTextとButtonを併記すれば自作できる。これは簡単だ。
しかし、Androidシステムとして、検索に関する仕掛けが用意されているので、それを用いた方が、利用者の立場から見て親和性の高いViewを構築できる。

この記事は、検索用のViewだけを表示する仕掛けに関してだけである。検索するコマンドは実装していない。

私の所感
SearchView等を使わず、EditTextとButtonを用いて自作する方がずっと簡単だ!
にも関わらず、小難しいSearchViewを作っちゃったってことは、検索に関してマニアックになっている重要視しているってことだね。私としては、このようなマニアックなことは好きなので設計者の意向を尊重してSearchViewを活用してみることにした。

参考にしたサイト
Search Overview
Action Bar..."Action View"とは術語(専門用語)である。このAction Viewの具体例として、例えば、SearchViewがある。
Android Tutorial: Adding Search to Your Apps

簡単なサンプルプログラムを作ってみました。
Action Barに検索用文字列入力欄が表示され、そこに文字列を入力して実行すると、入力した文字列がログに表示されます。

public class MainActivity extends Activity{
    private final String TAG = "MainActivity";

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

    @Override
    public boolean onCreateOptionsMenu(Menu menu){
        getMenuInflater().inflate(R.menu.main, menu);
        SearchView sV;
        SearchManager sM = (SearchManager)getSystemService(Context.SEARCH_SERVICE);
        ComponentName c = getComponentName();
        SearchableInfo si = sM.getSearchableInfo(c);
        MenuItem mi = menu.findItem(R.id.action_Search);
     
/*
 * getActionView()について
 * menuのxmlにおいて、android:actionViewClassで設定したactionViewを取得する。
 * もし、android:actionViewClassが存在しない場合、getActionView()の戻値はnullである。
 */
        sV = (SearchView)mi.getActionView();
        sV.setSearchableInfo(si);
     
        /*
         * setIconifiedByDefault()について
         * falseは、文字列入力領域が表示される。
         * trueは、文字列入力領域は表示されずに、虫メガネアイコンだけが表示される。
         */
        sV.setIconifiedByDefault(false);
        return true;
    }

    @Override
    protected void onNewIntent(Intent intent) {
    /*
     * getIntent()で得られるIntentは、原初の時点でのIntentのままである。
     * setIntent()を使うことにより、最新のIntentに更新される。
     */
        setIntent(intent);
        if(Intent.ACTION_SEARCH.equals(intent.getAction())==true){
            String q = intent.getStringExtra(SearchManager.QUERY);
            Log.d(TAG, "onNewIntent() : query==" + q);
        }
    }
}

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.searchable"
    android:versionCode="1"
    android:versionName="1.0"
    >
    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="17"
        >
    </uses-sdk>
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme"
        >
        <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.SEARCH" />
            </intent-filter>
            <meta-data
                android:name="android.app.searchable"
                android:resource="@xml/searchable"
                />
        </activity>
    </application>
</manifest>

searchable.xml
<?xml version="1.0" encoding="utf-8"?>
<searchable
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/app_name"
    android:hint="@string/SearchHint"
    >
</searchable>

menu/main.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:id="@+id/action_Search"
        android:actionViewClass="android.widget.SearchView"
        android:showAsAction="always"
        >
    </item>
</menu>

2013年4月23日火曜日

RadioButtonの背景色を変更する方法

RadioButtonの背景色を変更する方法

RadioButtonの背景色を変更すると、RadioButtonの形状が崩れることが指摘されています。
Android RadioButton の画像とテキストの間隔を広げる
私は、形状崩れをなんとかしたいと思います。
なお、この現象は少なくともAndroid 4.2.2では発生しません。2.2及び2.3では発生します。

解決方法 その1
xmlにおいて背景色を設定した場合に、形状崩れが発生するのです。実行時に背景色を設定すれば問題は発生しません。

解決方法 その2
RadioGroupを使用せず、javaのコードにおいて、RadioButtonに対する押し下げをonClickメソッドで補足し、他のRadioButtonをsetChecked(false)してやるのです。なお、個々のRadioButtonの上位に個別にLinearLayout等を配置し、このLinearLayoutに対して背景色を設定します。

2012年5月15日火曜日

SurfaceViewと、それ以外のViewとの混在

SurfaceViewと、それ以外のViewとの混在

SurfaceViewと、それ以外のViewとを混在させる場合、RelativeLayoutを使うのがよろしいでしょう。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#880000ff"
    >
    <TextView
        android:id="@+id/TextTop"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:text="@string/app_name"
        >
    </TextView>
    <TextView
        android:id="@+id/TextBottom"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="@string/app_name"
        >
    </TextView>
    <SurfaceView
        android:id="@+id/SurfaceView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/TextTop"
        android:layout_above="@id/TextBottom"
        >
    </SurfaceView>
</RelativeLayout>

2012年4月27日金曜日

半透明の程度を動的に変更する

半透明の程度を動的に変更する

半透明の程度の値(alpha値)を、何か1個の値で固定したままにするのであれば、その値をxmlファイルに設定すれば良い。
この記事では、利用者の操作等により、alpha値が任意の値に動的に変更・表示できる方法を示す。

この場合、透明のActivityにはconfiguration changeが発生しないという現象があるので注意して頂きたい。

手順概要
1 アプリ(又は任意のActivity)を、完全な透明であるように設定する。
2 任意のタイミングで、任意の半透明の値に設定・表示する。

1 アプリ(又は任意のActivity)を、完全な透明であるように設定する。
処理対象ファイル:AndroidManifest.xml
書き加えるコマンド:android:theme="@android:style/Theme.Translucent"
書き加える場所:アプリ全体を半透明化するのであれば、<application>の属性として書く。任意のActivityだけに施すのであれば、<activity>の属性として書く。
sample code:アプリ全体を半透明化する場合、次のとおりである。
<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="jp.Androyer.Transparent"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk android:minSdkVersion="8" />
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.Translucent"
        >
        <activity
            android:name=".TransparentActivity"
            >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

2 任意のタイミングで、任意の半透明の値に設定・表示する。
Java codeの中で、setBackgroundColorメソッドを使う。
sample codeは次のとおりである。
public class TransparentActivity extends Activity
    implements
    OnClickListener
    {
    Button bu;
    TextView tv;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        bu = (Button)findViewById(R.id.Button);
        bu.setOnClickListener(this);
        
        int i;
        char c = '亜';
        StringBuilder sv;
        
        //適当に文字列を作成・表示する。
        sv = new StringBuilder();
        for(i=0; i<800; i++, c++) sv.append(c);
        tv = (TextView)findViewById(R.id.Text);
        tv.setText(sv);
        //本アプリの後ろの他のアプリが完全に見える状態で表示される。
        //ただし、文字は白色で表示される。
    }
    
    @Override
    public void onClick(View v) {
        if(v==bu){
            //任意の色の背景色にする。ここでは桃色の半透明にする。
            tv.setBackgroundColor(Color.argb(0x66, 0xff, 0, 0xff));
        }
    }
}

参考:上記sample codeで使用したレイアウト用の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" 
    >
    <Button
        android:id="@+id/Button"
        android:text="@string/Button"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        >
    </Button>
    <TextView
        android:id="@+id/Text"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        >
    </TextView>
</LinearLayout>

2012年4月8日日曜日

オプションメニューの作成と変更

オプションメニューの作成と変更

副題:複雑な形状のオプションメニューを作成し、必要に応じ、メニューの一部を削除する。

形状が複雑なオプションメニューはJavaのcodeで書くよりも、xmlで書いた方が視認性が高まる。
sampleは次のとおりである。
resフォルダーの直下にmenuという名前のフォルダーを作成し、menuフォルダーの直下に、このxmlファイルを配置する。このsampleでは、このxmlファイル名は、menu_inputactivity.xmlである。
<?xml version="1.0" encoding="utf-8"?>

<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    >
    <item
        android:id="@+id/Menu_Setting"
        android:title="@string/Menu_Setting"
        >
    </item>
    <item
        android:id="@+id/Menu_Display"
        android:title="@string/Menu_Display"
        >
        <menu>
            <item
                android:id="@+id/Menu_ShowButtons"
                android:title="@string/Menu_ShowButtons"
                >
            </item>
            <item
                android:id="@+id/Menu_HeightSize"
                android:title="@string/Menu_HeightSize"
                >
            </item>
        </menu>
    </item>
    <item
        android:id="@+id/Menu_Quit"
        android:title="@string/Menu_Quit"
        >
    </item>
</menu>

このメニューを任意のActivityに作成するsample codeは次のとおりである。必要に応じ、任意のメニューを削除する。
    @Override
    public boolean onCreateOptionsMenu(Menu menu){
        MenuInflater inf;
        
        inf = getMenuInflater();
        inf.inflate(R.menu.menu_inputactivity, menu);
        if(p!=null && p.iInputMethod==1){//必要に応じ
            MenuItem mi;
            mi = menu.findItem(R.id.Menu_Display);
            if(mi!=null){//サブメニューのメニューを削除する。
                SubMenu sub;
                sub = mi.getSubMenu();
                sub.removeItem(R.id.Menu_ShowButtons);
            }
        }
        return super.onCreateOptionsMenu(menu);
    }

onCreateOptionsMenuメソッドは、Activityの起動時に1回しか呼ばれない。そういう意味で、上記のJava codeだと、「静的に」メニューを作成したことになる。
動的にメニューを変更するのであれば、下記のようにonPrepareOptionsMenuメソッドを組み合わせて使うことになる。
    @Override

    public boolean onCreateOptionsMenu(Menu menu){
        MenuInflater inf;
        inf = getMenuInflater();
        inf.inflate(R.menu.menu_inputactivity, menu);
        return super.onCreateOptionsMenu(menu);
    }
    
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        if(p==null) return super.onPrepareOptionsMenu(menu);
        MenuItem mi;
        SubMenu sub;
        mi = menu.findItem(R.id.Menu_Display);
        sub = mi.getSubMenu();
        mi = sub.findItem(R.id.Menu_ShowButtons);
        if(p.iInputMethod==1){//必要に応じ
            if(mi!=null) sub.removeItem(R.id.Menu_ShowButtons);
        }
        else{
            if(mi==null){
                sub.add(Menu.NONE, R.id.Menu_ShowButtons,
                    Menu.NONE, R.string.Menu_ShowButtons);
            }
        }
        return super.onPrepareOptionsMenu(menu);
    }

2012年1月29日日曜日

xml:文字列の前後の空白文字が表示されない

xml:文字列の前後の空白文字が表示されない

画面構成を整えるため、文字列の前後に空白文字を挿入したい場合がある。
英語の環境で次のxmlを実行させた場合、文字列の前後の空白文字は表示されない。
    <string name="TextVolume"> Volume </string>
空白文字を表示するためには、次のように、前後を括弧で括らねばならない。
    <string name="TextVolume">" Volume "</string>

日本語の環境において、文字列の前後に全角の空白文字を書いた場合、その全角空白文字は表示される。
    <string name="TextVolume"> 音 量 </string>
しかし、半角の空白文字であれば、その空白文字は表示されない。この場合、前後に括弧を施せば表示される。

これらはxmlでの話である。Javaのcodeにおいては、普通に、教科書どおりに書けば良い。

2012年1月27日金曜日

xml:勝手に空白文字が挿入されてしまう

xml:勝手に空白文字が挿入されてしまう

次のxmlがある。この文字列を画面に表示すると、「春」から「夏」にかけて、改行はされる。しかし、その部分に、空白文字が1個挿入されてしまうのである。なので、「夏」が1個右にズレて表示される。

<string name="AboutThis3">
春\n

</string>

これは、「春\n」と「夏」の間に、(エディタ上では見えない)改行文字が挿入されているために発生する現象である。
そして、「春\n」の前、及び「夏」の後にも、各々1個の(エディタ上では見えない)改行文字が挿入されている。
この(エディタ上では見えない)改行文字が空白文字に置き換えられるのである。
なので、この(エディタ上では見えない)改行文字を書かないようにすれば良い。解決策は次のとおりである。

<string name="AboutThis3">春\n夏</string>

(エディタ上では見えない)改行文字が空白文字に変換されてしまう仕様は、英語を記述することを想定しているためのようである。日本語しか使わないのであれば、違和感のある仕様だ。
参考:第10回 落とし穴になる空白文字と改行文字

上記例では短い文字列であったため問題は無い。
しかし、日本語で比較的長い文字列を書く場合、エディタ上で読み難くなる。

<string name="AboutThis3">【動作環境】android 2.2\n  【ソフトウェア種別】フリーソフトウェア\n【制作者名】Androyer\n 読み難いぞ(T_T)</string>

エディタ上で読み難くなるという、ただそれだけであるため、問題は無い。
しかし、やっぱり見易くしたいという御仁もいらっしゃるであろう。その場合、やむを得ないが、括弧で括り、文字列の先頭に半角空白文字を挿入して頭を揃えることになる。

    <string name="AboutThis3">
        " 【動作環境】android 2.2\n"
        "【ソフトウェア種別】フリーソフトウェア\n"
        "【制作者名】Androyer"
    </string>

第1列目が空白文字で表示されてしまうが、ご容赦して頂くことになる。このことについて、文句を言う利用者はいないであろう。

2011年12月18日日曜日

AdMob画像の場所に、代替画像を表示する

AdMob画像の場所に、代替画像を表示する

副題:待ち時間に、愛想笑い微笑みを

アプリの画面の表示と同時に、AdMob画像が表示されるのでは無い。
アプリの画面が表示された後、インターネットから広告のデータをダウンロードしてから、その広告データに基づきAdMob画像が表示されるのである。AdMob画像が物理画面の下端にあるのであれば気にはならない。

しかし、何も考慮せずに、AdMob画像を物理画面の上端に配置した場合、AdMob画像が表示された時、その画像がその位置に「挿入される」ということになるため、その画像の下に配置しているViewが、一斉に下にズレてしまう。このような振る舞いは、利用者に違和感を与える。
if(あなたの感じ方=="別に問題では無い。") return;

この問題への対処の方法として、AdMob画像を表示する位置に、予め別の画像を表示するようにしておいて、そこに後からAdMob画像を表示するようにすれば良い。
この方法を講じるためには、愛想笑い微笑みを描いた横320×縦50の画像を作成し、この画像をres>drawableディレクトリに入れておかねばならない。
if(あなたの画力=="苦笑い程度しか描く画力が無い") return;

下記のxmlで実装する。画像ファイル名はbanner.pngである。FrameLayoutを使うことにより、同一の場所に表示させることができる。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#a00"
    >
    <FrameLayout
        android:id="@+id/FrameLayout"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:gravity="center"
        >
        <ImageView
            android:src="@drawable/banner"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            >
        </ImageView>
        <LinearLayout
            android:id="@+id/AdMob"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            >
        </LinearLayout>
    </FrameLayout>
    <Button
        android:text="@string/Button"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/FrameLayout"
        >
    </Button>
</RelativeLayout>

この方法だと、AdMob画像が表示される前も後もButtonが下にズレることは無い。かつ、AdMob画像が表示されるまでの短い時間ではあるが、愛想笑い微笑みの画像を表示させることができて、利用者を飽きさせない。
if(あなたの感想=="飽きはしないが、好感を持てるということも無い") return;

2011年12月16日金曜日

AdMobを中央に配置する。

AdMobを中央に配置する。

副題:その空間に、愛想笑いを。

何も考慮せずにAdMobのlayoutを作成すると、AdMobの広告は物理画面の左詰めになる。この場合、端末が横位置になった場合、広告の右側に無意味な空白が発生する。
if(あなたの考え=="別に構わないじゃないか") return;

このような無意味な空白はAdMob側で何か措置を施して頂きたいが、現状ではどうにもならないので、プログラマ側でなんとかする。
無意味な空間を、なんとか取りツクロうのである。言わば、気まずい雰囲気の中で行われる、愛想笑いのような仕掛けだ。
if(あなたの表情=="笑い顔には自信が無い") return;

取り敢えず考えられるのは、広告を横方向の中央に配置すれば良い、ということであろう。中央に配置することにより、無意味な空間を左右に分散させるのである。これにより、無意味と感じる程度が低下することが期待できる。
if(あなたの感想=="左側にも無意味な空間が広がるだけだ") return;

背景が画像である場合は、FrameLayoutを使うことになるであろう。そのためのlayout用xmlのsample codeは次のとおりである。
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ViewGroup"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#a00"
    >
    ここに背景用のViewを設置する。
    <LinearLayout
        android:id="@+id/AdView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center_horizontal"
        >
    </LinearLayout>
</FrameLayout>

他のViewがBottunやScrollView等であれば、次のようにRelativeLayoutを用いることになる。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#a00"
    >
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:gravity="center_horizontal"
        >
        <LinearLayout
            android:id="@+id/AdView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            >
        </LinearLayout>
    </LinearLayout>
    ここに他のViewを書いていく。
</RelativeLayout>

上記例では、中央に配置したことを視覚的に明確にするため、背景を赤色で塗ってある。しかし、広告を目立たせるためには、背景を目立たない色にするべきであろう。
if(あなたの趣味=="背景は広告よりも目立つようにすべきだ") return;

javaのcodeは、AdMob Version 4.3.1の実装に書いたとおりである。

後日談は、画面サイズに応じてAdMobのサイズを変えるに書きました。

2011年11月26日土曜日

CheckBoxを右端に配置する


CheckBoxを右端に配置する

下記の例では、TextViewは、CheckBoxの縦幅の中央に配置されます。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    >
    <CheckBox
        android:id="@+id/CheckBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:checked="true"
        android:layout_alignParentRight="true"
        >
    </CheckBox>
    <TextView
        android:text="aaaaaa"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_alignLeft="@id/CheckBox"
        android:layout_alignParentLeft="true"
        android:layout_alignBottom="@id/CheckBox"
        android:gravity="center_vertical"
        >
    </TextView>
</RelativeLayout>

2011年11月21日月曜日

RelativeLayout内に書くViewの順番

RelativeLayout内に書くViewの順番

この記事には、筆者自身がよく分かっていない事柄を、臆面も無く、書いてある。例えば、「宣言」とか「参照」とか「生成」という用語の使い方だ。
しかし、筆者の経験からして、機能面ではほぼ間違いのない事実であるので、ここに書き留めておき備忘録としておきたい。

RelativeLayoutをxmlで書く方法
RelativeLayoutは、それに含まれるViewを相対的な位置に配置するGroupViewである。相互の相対的な位置であるから、配置コマンドの中にViewを指定して書くことになる。
Viewを指定して書く場合、その書く順番に留意をしなければならない。IDを宣言しているコマンドを上に書いて、その下に、そのIDを参照しているコマンドを書くのである。
下記のxmlのsample codeの例では、IDを宣言している箇所を赤色で塗った。そして、それを参照している箇所を青色で塗った。
宣言を先に書き、参照を後に書くのである。

下記sampleでは、BottomViewを、物理画面の下端に配置するため、そのコマンドを、xmlの一番下に書いてしまいそうになる。しかし、そのようにすると、参照ができなくなり、エラーになる。
このエラーを発生させないようにするため、+記号付きの書式で書くこともできるが、「エラーを発生させたくない」ということを動機として+記号付き書式を採用して良いのであろうか。。。。まあ、別に良いですけど。

xml sample code
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    >
    <TextView
        android:id="@+id/TopView"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text="@string/ThisIs"
        android:layout_alignParentTop="true"
        >
    </TextView>
    <TextView
        android:id="@+id/BottomView"
        android:text="@string/ThisIs"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:layout_alignParentBottom="true"
        >
    </TextView>
    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:layout_above="@id/BottomView"
        android:layout_below="@id/TopView"
        >
(以下省略)

IDの書式
以上の事柄を理解するためには、resourceのIDを取り扱うコマンドの書式を理解しておく必要がある。次のような似た書き方がある。
  • "@+id/TopView"
  • "@id/TopView"
@の次に+記号を付けるかどうかの違いである。
筆者は、これらを正確に理解していない。しかし、経験上、次のことが言える。

+記号を付けると、その+記号の右側の文字列のディレクトリがR.javaに生成される。そして、そのディレクトリの下に、/の右側にある文字列をIDとするものが生成される。
例えば、適当なViewの属性に、次のように書いてみる。
<LinearLayout
    android:id="@+OhMyGod/What"
(以下省略)
これをコンパイルして、genディレクトリの下にあるR.javaのRを見ると、OhMyGodがあり、更にその下にWhatがある。
このことから、+記号を付けた書式では、IDが生成されることが分かる。

+記号の無い書式は、お馴染みである。
例えば、"@string/app_name"というのは、string resourceの中のapp_nameというIDを指している。そして、このIDに関連付けられている文字列を参照することができるのである。
つまり、+記号の無い書式は、resourceへの参照機能を有するのである。生成はしない。
xmlは、上の行から下の行に向かって解析される。上の方で、IDの生成を行っておき、少なくとも、その行よりも下で、そのIDへの参照を行わねばならない。生成が無い段階で、そのIDを参照しようとするとエラーになる。

実際の書き方
以上の説明は、書式の趣旨に沿って書く方法であった。
試作品を作る場合や、複雑な形状のRelativeLayoutを作る場合であれば、+記号がある書式を用いることも有り得る。例:"@+id/TopView"
この書式は、既に述べたように、IDへの参照では無く、IDの生成である。

同一のIDをあちこちで生成したとしても、エラーにはならず、生成したIDをお互いに共有する仕掛けになっているようである。このため、上記に書いたような、書く順番を無視して書くことができる。
エラーを発する「参照書式」よりも、エラーを発しない「ID生成書式」が好まれるかもしれない。

もし、エラーを発する書式で書くのであれば、エラーを発生させないように書かざるを得ない。そうすれば、堅牢な作りになると思われる。IDの生成は(複数書くのではなく)1か所だけに留める書き方にするのである。
もし、エラーを発生する仕掛けがあるならば、それを喜んで使えば良い。そして、エラーを発生させないように書けば良い。

参考
android developersのCommon Layout ObjectsのRelativeLayoutに下記の注意書きがあります。
Note that the attributes that refer to relative elements (e.g., layout_toLeft) refer to the ID using the syntax of a relative resource (@id/id).

For example, assigning the parameter toLeft="my_button" to a TextView would place the TextView to the left of the View with the ID my_button (which must be written in the XML before the TextView).

2011年11月19日土曜日

様々なViewの縦幅を揃える

様々なViewの縦幅を揃える

ViewGroupの中で使い道が無いと思われるものの一つとしてFrameLayoutを挙げることができるのではないだろうか。ここでは、このFrameLayoutの活用方法を無理矢理考える。

様々なViewを配置した場合、個々のViewの縦幅が異なるため、見た目が不細工になる。
個々のViewの縦幅を統一する簡便な方法として、FrameLayoutを用いる方法がある。

FrameLayoutの中に、表示したいViewと共に、縦幅が最も高いViewをダミーで挿入するのである。
ダミーViewを挿入するため、無駄なresourceを設けることにはなるが、作成が簡単であるため、試作品の作成には向いている。

ここでは静的な揃え方を書きましたが、動的に揃える書き方は、起動直後にViewのサイズを動的に変更するに書きました。

下記の例では、TextViewとSeekBarとSpinnerの3個のViewの高さを、全て、Spinnerの高さと同じ高さに揃えている。
高さ調整のため、 TextViewとSeekBarに対にしたSpinnerを非表示にしている。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <FrameLayout
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content"
        >
        <TextView  
            android:layout_width="fill_parent" 
            android:layout_height="wrap_content" 
            android:text="@string/hello"
            android:layout_gravity="center_vertical"
            >
        </TextView>
        <Spinner
            android:layout_width="fill_parent" 
            android:layout_height="wrap_content" 
            android:visibility="invisible"
            >
        </Spinner>
    </FrameLayout>
    <FrameLayout
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content"
        >
        <SeekBar
            android:layout_width="fill_parent" 
            android:layout_height="wrap_content" 
            android:layout_gravity="center_vertical"
            >
        </SeekBar>
        <Spinner
            android:layout_width="fill_parent" 
            android:layout_height="wrap_content" 
            android:visibility="invisible"
            >
        </Spinner>
    </FrameLayout>
    <Spinner
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        >
    </Spinner>
</LinearLayout>

2011年9月8日木曜日

ScrollViewに関するメモ

ScrollViewに関するメモ

  1. 多数のViewが存在し、全てのViewが物理画面内に表示できない場合であっても、スクロールバーは自動的には表示されない。そして、物理画面に表示されていないViewは、表示されないままである。これはandroidシステムの仕様である。
  2. もし物理画面内に収まらないViewを表示させたいのであれば、プログラマがScrollViewを実装して、スクロールバーを使えるようにしなければならない。
  3. ScrollViewとは、物理画面に収まらない複数のViewを、利用者が画面をスクロールすることによって、表示できるようにするViewである。
  4. 文字列や画像のデータを一覧にして表示するのであれば、別の技法を用いることを検討した方が良い。例えば、GridView, ListView, Spinner等である。ScrollViewは、データでは無く、Viewを一覧表示させるものである。
  5. ScrollViewは縦方向のスクロールです。横方向にはHorizontalScrollViewを使う。
  6. スクロールさせたいViewを、ScrollViewの子として配置させて使う。ScrollViewが親になるわけです。
  7. 表示すべき子Viewが少量であり、物理画面内に収まる場合には、例えScrollViewを実装していたとしても、スクロールできないようになります。当たり前といえば、当たり前の仕様ですが、私はこれが気になって調べました。
  8. 子Viewが動的に大量になり、物理画面内に収まっていた状態から、収まらない状態へと変化した場合、スクロールできない状態からできる状態へと自動的に変化する。これの逆の状態へと子Viewが変化した場合も、スクロール状態は適切に変化する。
  9. ScrollViewは、FrameLayoutから派生されたクラスです。FrameLayoutでは複数のViewを子供にできますが、ScrollViewは1個の子供しか持てないようになっています。
  10. 普通にScrollViewを使うとすれば、子Viewは、きっとLinearLayoutでしょう。このLinearLayoutに、EditTextとかButtonとかのスクロール表示させたいViewを子供として指定します。
  11. ScrollViewの上(物理画面の上端)又は下(物理画面の下端)に任意のViewを表示することもできます。この場合、ScrollViewの親にRelativeLayoutを設定します。RelativeLayoutからの相対位置として、上端のView、下端のView、そして中段のScrollViewを指定します。
  12. smoothScrollByメソッドやfullScrollメソッド等のスクロールをさせるメソッドは、Viewが作成された後でなければ有効な実行ができません。参考:TextViewを任意の位置にスクロールさせる
  13. smoothScrollToメソッドは、左上端からの絶対座標を指定します。
  14. smoothScrollByメソッドは、現在の位置からの相対値を指定します。このため、Y軸の引数にマイナスの数値を指定すると、上方向にスクロールします。
  15. レイアウト間の間隔を動的に変えるにプロジェクトファイルを添付しておきました。
  16. ScrollViewの配下に(例え2段下であっても)ListViewを配置した場合、そのListViewのスクロールは機能しません。つまり、全然動かないので使い物にならない。
  17. もし、どうしてもListViewのようなViewを実装したいのであれば、LayoutInflaterを使って、同一のViewを複数表示するようにすれば良い。
  18. ScrollViewの配下にScrollViewを配置することは有効です。
  19. Android 4.4.2 (API 19)において、画面を4~5回上下にスクロールさせると、画面が動かなくなる。原因不明。対応措置無し。このため、ListViewで代替することになる。このListViewのAddHeaderViewにView群を挿入してやれば良い。ただし、何かダミーをList要素に入れてやらないと、HeaderViewは表示されない。空っぽのアダプターを作ってsetAdapter()すれば良い。Android Studio: Horizonal Scroll View Freezes While Testing On Emulator

上記知見を得るために使用したxmlを掲載しておきます。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    >
    <TextView
        android:id="@+id/TopView"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text="@string/ThisIs"
        android:layout_alignParentTop="true"
        >
    </TextView>
    <TextView
        android:id="@+id/BottomView"
        android:text="@string/ThisIs"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:layout_alignParentBottom="true"
        >
    </TextView>
    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:layout_above="@id/BottomView"
        android:layout_below="@id/TopView"
        >
        <LinearLayout
            android:id="@+id/LLParent"
            android:orientation="vertical"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" 
            >
            <LinearLayout
                android:id="@+id/LLButtonReportSize"
                android:layout_width="wrap_content" 
                android:layout_height="wrap_content" 
                android:layout_gravity="center_vertical"
                android:tag="padding"
                >
                <Button
                    android:layout_width="wrap_content" 
                    android:layout_height="wrap_content" 
                    android:text="@string/ThisIs"
                    >
                </Button>
            </LinearLayout>
            <Button
                android:id="@+id/ButtonReportSize"
                android:layout_width="wrap_content" 
                android:layout_height="wrap_content" 
                android:text="@string/ThisIs"
                >
            </Button>
            
            <LinearLayout
                android:id="@+id/LLToggleButton"
                android:layout_width="wrap_content" 
                android:layout_height="wrap_content" 
                android:layout_gravity="center_vertical"
                android:tag="padding"
                >
                <ToggleButton
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    >
                </ToggleButton>
            </LinearLayout>
            <ToggleButton
                android:id="@+id/toggleButtonPadding"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                >
            </ToggleButton>
            
            <LinearLayout
                android:id="@+id/LLCheckBox"
                android:layout_width="wrap_content" 
                android:layout_height="wrap_content" 
                android:layout_gravity="center_vertical"
                android:tag="padding"
                >
                <CheckBox
                    android:text="@string/ThisIs"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    >
                </CheckBox>
            </LinearLayout>
            <CheckBox
                android:id="@+id/checkBox"
                android:text="@string/ThisIs"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                >
            </CheckBox>
            
            <SeekBar
                android:id="@+id/seekBar1"
                android:layout_height="wrap_content"
                android:layout_width="fill_parent"
                >
            </SeekBar>
            <RatingBar
                android:id="@+id/ratingBar1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                >
            </RatingBar>
            <RatingBar
                android:id="@+id/ratingBar1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                >
            </RatingBar>
        </LinearLayout>
    </ScrollView>
</RelativeLayout>