2011年9月22日木曜日

画面を持たないActivityを作る

画面を持たないActivityを作る

Activityには画面があります。しかし、今回の目的は、IntentをActivityで受け取る仕掛けを作るだけなので、画面があってはならないのです。この話は、Intentにより起動されるタイミングの続編です。

Intentを受け取るだけの目的なので、onCreateの段階でfinish()すれば良いのですが、この場合、画面が一瞬黒くなります。

なので、そもそも、画面が無いActivityを作りたいのです。しかし、その方法が分かりません。

そこで、画面を透明にする技法にしました。
やり方は、アクティビティ(画面)の背景を透過にするにはに掲載されています。
誰も、私のような目的のために、この技法を使うものとは、思うめい。へっへっへ\(^o^)/
て、いうか、私の目的以外に、どんな目的で全画面を透明にするねん(-_-;)
この技法を紹介しているサイトには、何のためにってのが書いていません。技法しか書いてない。おそらく、私と同じ目的に使おうとしていたんだよねぇ、たぶん。憎いねぇ。さすがだねぇ。ジャ(蛇)の道は蛇(ヘビ)ですな。

xmlではなく、javaでコーディングをすれば、もっと幅広い何かができるものと思いまして、いろいろ試しましたが、結論としては、javaコードで透明にすることはできない、ということです。
参考に、半透明にしようと試みたが半透明にする効果の無いプログラムを掲載しておきます。(何のために?!(-_-;))
public class InvisibleActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        WindowManager.LayoutParams wl;
        Window w;
        w = getWindow();
        wl = w.getAttributes();
        wl.alpha = 0.5f;
        wl.format = PixelFormat.RGBA_8888;
        w.setAttributes(wl);
        ColorDrawable c;
        c = new ColorDrawable(0x88ff0000);
        w.setBackgroundDrawable(c);
     }
}
普通は、このコードだけで半透明になるはずなんだが、ならない。

2011年9月21日水曜日

Explicit IntentとImplicit Intent

Explicit IntentとImplicit Intent

IntentはExplicit IntentとImplicit Intentの2種類に分けられます。
このような分類や議論は設計上での話です。プログラマーの立場から見れば、Intentの引数に与える値が異なる、という話になります。「Explicit」といったコマンドがあるのではありません。

Explicit Intentについて
Explicit Intentは、起動させるアプリを明示するIntentです。プログラムの書き方としては、パッケージ名とクラス名とを引数にします。

これは、自分のアプリ内のActivityやServiceを起動するために使います。

また、他のアプリのパッケージとクラスを指定すれば、(指定したアプリが稼働していない状態であっても)そのアプリのそのクラスを起動できます。

自分のアプリ内のを起動する方法については、他の多くのサイトで紹介されています。Bound Servicesはその例です。
書き方の例を次に示します。この例は自分のアプリ内のLocalServiceというクラスを起動します。第一引数のthisは自分のアプリを指します。第二引数は自分のアプリ内のクラスを指します。

Intent intent = new Intent(this, LocalService.class);

自分以外のアプリ(他のアプリを)起動する書き方の例は次のとおりです。setClassNameメソッドの第一引数はパッケージ名です。第二引数はそのパッケージに含まれるクラスです。
Intent i;
i = new Intent();
i.setClassName(
"jp.Androyer.ReceiveIntent",
"jp.Androyer.ReceiveIntent.ReceiveService");
i.putExtra(Intent.EXTRA_TEXT, "Intent Send");
try{
startService(i);
}
catch(ActivityNotFoundException e){
Toast.makeText(this, "Error", Toast.LENGTH_LONG).show();
}

Implicit Intentについて
Implicit Intentは、起動させるアプリを明示せず、行いたい行動を指定するIntentです。
この用語は、アプリを設計する場面で使う用語です。アプリを利用する場面では、これを「共有」と呼んでいます。

Implicit Intentが発行されると、アンドロイドシステムは、指定された行動を行えるアプリの一覧を作成し、これを利用者に提示します。利用者は、この一覧の中から1個のアプリを選択します。
書き方は次のとおりです。
Intent i, i2;
i = new Intent();
i.setAction(Intent.ACTION_SEND);
i.setType("text/plain");
i.putExtra(Intent.EXTRA_TEXT, "Intent Send");
i2 = Intent.createChooser(i, "選択してください");
try{
startService(i2);
}
catch(ActivityNotFoundException e){
Toast.makeText(this, "Error", Toast.LENGTH_LONG).show();
}

intent-filter
Implicit Intentが行っているのは「指定した行動を行えるアプリを起動せよ」という指示だけであり、具体的にどのアプリを起動させるのかまでは指示しません。Intentの引数にはパッケージ名やクラス名が指定されていないのです。

Implicit Intentは指示を発する側の仕掛けです。この指示を受けて、起動するアプリを探し求める仕掛けが必要になります。
アンドロイドシステムは、指定された行動を行えるアプリがどれだけ端末にインストールされているのかを知っておかなければなりません。また、起動されるアプリ側でも、自分はどのような行動を行えるのかをアンドロイドシステムに通知しておかなければなりません。

自分が行える行動をアンドロイドシステムに通知する仕掛けが、intent-filterです。これはAndroidManifest.xmlの<intent-filter>に書きます。
filterとは、ろ過するという意味ですが、Intentをろ過して、取捨選択して、指示内容に合致したIntentだけを取り扱う、というような仕掛けになっています。

起動について
ここでは話を簡単にするため、一言で「起動」と表現しています。詳しくは、Intentにより起動されるタイミングをご覧ください。

用語の翻訳について
他のサイトでは、Implicitを「暗黙の」と翻訳されています。
私がこの勉強を始めた頃、「暗黙の」と訳された日本語の解説をあちこちで読みましたが意味が理解できませんでした。そのため、英語の原文を読んだのです。このような経緯から、私の記事では、原文のまま、Implicit Intentと表記しています。
Implicit Intentを「(起動するアプリを)明示しないIntent」と翻訳しても、間違ってはいないと思います。
なお、Implicitという言葉とExplicitという言葉は、反意語の関係にあります。Explicitという言葉は、「明白な」、「はっきりした」という意味があります。

2011年9月19日月曜日

むむむ!createChooser()

むむむ!createChooser()

androidのプログラミングを行っていると、変だなと思うことがある。
ただし、変だと思うのは私だけであって、私が変であるという可能性を否定することはできない。

IntentクラスにcreateChooserメソッドがある。
public static Intent createChooser (Intent target, CharSequence title)

これは、共有コマンドを実行する時に、起動できる複数のアプリの中から、お好みの1個のアプリを選択する画面を表示するメソッドである。
必須のメソッドでは無い。

startActivity()には対応しているが、startService()には対応していない。

もし、このメソッドを使わなければ、デフォルトの画面が表示されるだけである。このデフォルト画面には「常にこのアプリを起動する」オプションボタンがある。

このメソッドを使うメリットは、次の2点である。
1 第二引数の文字列を、画面のタイトルに設定できることである。
2 「常にこのアプリを起動する」オプションボタンが表示されなくなる。

次のように書く。
    Intent i, i2;
    i = new Intent();
    i.setAction(Intent.ACTION_SEND);
    i.setType("text/plain");
    i.putExtra(Intent.EXTRA_TEXT, "InfiniteIntent");
    i2 = Intent.createChooser(i, "選択してください");
    try{
        startActivity(i2);
    }
    catch(ActivityNotFoundException e){
    }

仕掛けが大袈裟な割には、タイトル文字列を変更するだけなのか?。などという疑問を持つのは果たして私だけだろうか。

選択してください
1:私も同感である。
2:設計をミスしたように見えるな
3:そんな疑問を持つのはアンタだけや!
4:このような仕掛けにすることが技術的に妥当である。
5:不満があるなら自分で拡張しろ!
6:むむむ!

2011年9月14日水曜日

Adapterとは何か

Adapterとは何か

概要
Adapterは、文字列や画像などの複数のデータと、(AdapterViewから派生した)ListView等との中間に存在し、両者の橋渡しをする役割を担います。

他のViewとの違い
Viewとして良く使うものに、ButtonやTextViewなどがあります。これらは、特に問題なく使えるでしょう。
訳が分からないのがListViewです。ListViewを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="wrap_content" 
    >
    <ListView
        android:id="@+id/ListView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:fastScrollEnabled="true"
        >
    </ListView>
</LinearLayout>

この書き方は、ButtonやTextViewと良く似た書き方です。このため、ButtonやTextViewと同じ振る舞いをするかのような錯覚を持ちます。この錯覚が、理解を困難にしているのだと思います。

ListViewは、上記のように1個しか書いていません。しかし、このListViewにAdapterを結びつけることによって、AdapterのデータがListViewに表示される仕組みになっています。この結びつける作業は、javaで書きます。
1個しか書いていないんだけど、実際には、複数のデータが表示されるのです。

詳細
Adapterは、全てのViewで使えるのではなく、AdapterViewから派生したViewに限って使えるのです。
AdapterViewは、ViewGroupから派生したクラスです。ViewGroupは複数のViewを取り扱うViewです。このことから、(1個ではなく)複数のデータを表示する場合に、Adapterを使うものだと理解できます。
AdapterViewから派生してできたクラスとして、GridView, ListView, Spinner等があります。当然、これらのViewはViewGroupでもあるのです。

参考に、ListViewの階層図を示しておきます。


java.lang.Object
   ↳android.view.View
   ↳android.view.ViewGroup
   ↳android.widget.AdapterView<T extends android.widget.Adapter>
   ↳android.widget.AbsListView
   ↳android.widget.ListView

GridView, ListView, Spinner等を使う場合にAdapterも併せて使うことになります。
ただし、取扱うデータの数が、3~4個程度であり、少なければ、ViewGroupを使わずに、Viewを1個1個単体で作っていけばよろしくて、その場合、Adapterは使えません。

表示するデータの並べ替えや表示順序などの指定も、Adapterが執り行うこととなっています。このことから、ListView等を扱うプログラマーは、Adapterに関する理解が必須になるのです。Adapterを使わねばならなくなった時に、あたふたーしないようにしましょう。

Adapterには次の種類があります。
  1. ListAdapter....1個の行に1個の列を作成します。Listを使ってデータを挿入します。
  2. SimpleAdapter....1個の行に複数の列を作成できます。Mapを使って1行単位のデータを作成します。Listを使ってデータを挿入します。
  3. SimpleCursorAdapter....取扱うデータをデータベースから取得します。

2011年9月10日土曜日

任意の子Viewの属性を一括して変更する

任意の子Viewの属性を一括して変更する

親のViewGroup(例えば、LinearLayout)の下に、様々な種類(1種類でもよいが)のView(例えば、Button、TextView、LinearLayout等)が存在しており、それらのViewの属性を一括して変更する場合に使うsourceを掲載しておきます。

その1:
次のsourceでは、LinearLayoutに含まれる全部の子Viewの縦方向のpaddingの値の調整をしています。この技法を用いたsourceをレイアウト間の間隔を動的に変えるに公開しておきました。

        int i, iCount;
        LinearLayout ll;
        
        ll = (LinearLayout)findViewById(R.id.LLParent);
        iCount = ll.getChildCount();
        
        for(i=0; i<iCount; i++){
            View v;
            v = ll.getChildAt(i);
            v.setPadding(0, Padding, 0, Padding);
        }

その2:
次のsourceでは、LinearLayoutに含まれる子Viewの内、クラス名の末尾がButtonで終わるViewに対してpaddingを行います。
クラス名の末尾がButtonで終わるViewとは、ButtonやToggleButtonです。
この技法を使えば、複数の種類のViewが混在していたとしても、例えば、その中から"Layout"だけを取り出す、ということができます。

        int i, iCount;
        LinearLayout ll;
        
        ll = (LinearLayout)findViewById(R.id.LLParent);
        iCount = ll.getChildCount();
        
        for(i=0; i<iCount; i++){
            View v;
            String s;
            v = ll.getChildAt(i);
            s = v.getClass().getName();
            if(s.endsWith("Button")==true){
                v.setPadding(0, Padding, 0, Padding);
            }
        }

その3:
次のsourceでは、LinearLayoutに含まれる子Viewの内、paddingという名前のtagが付いているViewに対して、paddingを行います。
tagは識別子ですが、同一の値のtagを持つViewが複数存在しても良いです。
この技法を使うためには、事前に、任意のViewにtagを付けておかなければなりません。

        int i, iCount;
        LinearLayout ll;
        
        ll = (LinearLayout)findViewById(R.id.LLParent);
        iCount = ll.getChildCount();
        
        for(i=0; i<iCount; i++){
            View v;
            String s = "padding";
            v = ll.getChildAt(i);
            if(s.equals(v.getTag())==true){
                v.setPadding(0, Padding, 0, Padding);
            }
        }

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>

2011年9月2日金曜日

LayoutInflaterについて勉強する その2

LayoutInflaterについて勉強する その2

Activityが取り扱えるレイアウトはsetContentView()で設定します。この設定によるレイアウト以外のレイアウトを取り扱いたい場合にLayoutInflaterを使います。
LayoutInflaterは、プロジェクト内の任意のXMLファイルの内容(View)を、動的に取り扱えるようにするクラスです。
このクラスにより生成されたViewの在り方について実験しました。結論は次のとおりです。

  1. getLayoutInflater()を何度実行しても、返されるLayoutInflaterオブジェクトは同一のものである。
  2. LayoutInflaterのinflate()が返すViewオブジェクトは、inflate()の実行毎に異なる。このことから、inflate()は、その内部でViewをnewしていると想定される。

この結論を得るために使ったsourceを掲載しておきます。
public class LayoutInflaterActivity extends Activity{
    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        
        LayoutInflater inf1, inf2;
        TextView tv1, tv2, tv3, tv4;
        
        setContentView(R.layout.main);
        
        inf1 = getLayoutInflater();
        tv1 = (TextView)inf1.inflate(R.layout.textview, null);
        tv1.setText("inf1a");
        
        tv2 = (TextView)inf1.inflate(R.layout.textview, null);
        tv2.setText("inf1b");
        
        inf2 = getLayoutInflater();
        tv3 = (TextView)inf2.inflate(R.layout.textview, null);
        tv3.setText("inf2a");
        
        tv4 = (TextView)inf2.inflate(R.layout.textview, null);
        tv4.setText("inf2b");
        
        Log.d("Inf", "tv1:" + tv1.getText());
        Log.d("Inf", "tv2:" + tv2.getText());
        Log.d("Inf", "tv3:" + tv3.getText());
        Log.d("Inf", "tv4:" + tv4.getText());
        if(inf1==inf2){
            Log.d("Inf", "Inf1==Inf2");
        }
        else{
            Log.d("Inf", "Inf1!=Inf2");
        }
        if(tv1==tv2){
            Log.d("Inf", "tv1==tv2");
        }
        else{
            Log.d("Inf", "tv1!=tv2");
        }
        if(tv1==tv3){
            Log.d("Inf", "tv1==tv3");
        }
        else{
            Log.d("Inf", "tv1!=tv3");
        }
    }
}

main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/MainView"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
</LinearLayout>

textview.xml
<?xml version="1.0" encoding="UTF-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    >
</TextView>

ログの出力は次のとおりです。
09-02 01:09:47.375: DEBUG/Inf(960): tv1:inf1a
09-02 01:09:47.375: DEBUG/Inf(960): tv2:inf1b
09-02 01:09:47.385: DEBUG/Inf(960): tv3:inf2a
09-02 01:09:47.385: DEBUG/Inf(960): tv4:inf2b
09-02 01:09:47.385: DEBUG/Inf(960): Inf1==Inf2
09-02 01:09:47.385: DEBUG/Inf(960): tv1!=tv2
09-02 01:09:47.385: DEBUG/Inf(960): tv1!=tv3

2011年9月1日木曜日

LayoutInflaterについて勉強する

LayoutInflaterについて勉強する

この記事は、私が初心者であった時のものです。分かり易く書いたつもりの記事を、LayoutInflaterに掲載しました。(2012/05/05)

LayoutInflaterは、他のxmlリソースのViewを取扱える仕掛けです。
これについて勉強するため、コーディングしましたのでsourceを公開します。

ここに掲載したのは学習・実験を目的にしたものです。まともに動くプログラムは、動的にレイアウトの挿入&削除を繰り返すに掲載してあります。

以下 javaファイル********************
/*
 * LayoutInflaterへの理解を深めるため、ありがちなメソッドfindViewById()と、
 * LayoutInflaterを使った仕掛けとの両方を書きました。
 * LayoutInflaterはxmlファイルを扱えはしますが、そのファイルはリソースにある
 * コンパイルされたファイル(R.something file)を指します。
つまり、物理的に存在するxmlファイルを直接取り扱うのではありません。
例:res>layout>main.xml を設定するのでは無い
R.layout.main を設定します。
 */
public class LayoutInflaterActivity extends Activity{
    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
     
        LinearLayout ll;
        LayoutInflater inf;
        TextView tv;
/*
LayoutInflaterは、getSystemServiceメソッドから取得する。
getSystemServiceという名前から推察できるように、システムのサービスの一環として
LayoutInflaterが提供される仕掛けとなっている。
getSystemService()を使えばWifiやUSBなどハードウェア関連のサービスも使えるようになる。
たかだか、XMLファイルを扱うだけなのに、何故こんな仕掛けが必要なのでしょうか、という疑問が湧いてくる。
 */
        inf = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//こんな大げさな仕掛けには違和感を感じる。
getSystemServiceメソッドに目移りをしてしまい、このメソッドの仕掛けを勉強しなければならないような雰囲気になります。
そんな御仁にお勧めなのが次のメソッドだ。
        inf = getLayoutInflater();//初めから、これを出せ。(-_-;)
     
        //main画面をActivityで表示できるようにする。
        setContentView(R.layout.main);//毎度お馴染みのメソッドです。
        ll = (LinearLayout)findViewById(R.id.MainView);
     
        //main画面の中にあるTextViewを取得する。
        tv = (TextView)findViewById(R.id.MainText);//これも毎度お馴染み
        tv.setText("ccc");//文字列の代入に成功する。当たり前。
        //以上は、ありがちなプログラム処理です。
     
        //別のファイルにあるTextViewを取得するように試みる。
        tv = (TextView)findViewById(R.id.SubText);
        if(tv==null){
            //しかし、nullが返される。
            Log.e("InfAct", "return findViewById() : null");
            //普段よく使うfindViewById()は、自分のActivity画面にしか使えないのだ。
            return;
        }
        //上記エラーのため、以下を実行できない。
        tv.setText("ddd");//実行できない無駄なコード
        ll.addView(tv);//実行できない無駄なコード
     
        //もし、上記エラーが無いとして、ここまで来たとした場合,,,,
        //別のxmlファイルにアクセスする。
        tv = (TextView)inf.inflate(R.layout.textview, null);
        tv.setText("eee");//文字列の代入ができる。おお。すごい。
        ll.addView(tv);//main画面に挿入することだってできる。
    }
}

以上 javaファイル********************

main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/MainView"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView  
        android:id="@+id/MainText"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text="@string/aaa"
        />
</LinearLayout>

textview.xml
<?xml version="1.0" encoding="UTF-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/SubText"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/bbb"
    >
</TextView>

string.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">LayoutInflater</string>
    <string name="aaa">aaa</string>
    <string name="bbb">bbb</string>
</resources>

実験:ViewStubはViewの動的挿入に使えるか

実験:ViewStubはViewの動的挿入に使えるか

結論:「動的挿入」の定義による。
任意のViewを1回だけ挿入するだけなら使える。
挿入・削除を繰り返す目的には使えない。

動作
ViewStubは、実行時に、別のViewを、ViewStubが書かれてあるxml上の位置に挿入するものである。この挿入により、元々あったViewStubコマンドが、挿入するViewにより上書きされてしまうので、1回限りしか実行できない。
なお、挿入したViewを削除するには、setVisibility(View.GONE)を実行すれば良い。つまり、1回だけ挿入&削除はできるのである。

目的
ViewStubの目的は、処理速度の向上にある。
Layout Tricks: Using ViewStubs
Turbo-charge your UI

すまん。「実験」という表題でありながら実験したsourceを掲載していない。私は複数回挿入&削除したかったので、私的にはこの実験は失敗だった。

layout_weightの留意点

layout_weightの留意点

layout_weightは、複数のViewを任意の大きさの割合で配置することができるコマンドであり、とても有益である。このコマンドの留意点は次のとおりである。
  1. 絶対の位置では無く、相対的な位置を設定する。このため、物理画面の大きさの違いに関係無く、全てのサイズの画面に対応できる。
  2. 横方向で使う場合:layout_widthの値を"0dp"にすること。具体的には次のように書く。android:layout_width="0dp"
    この値を"fill_parent"にした場合、layout_weightの数値が低いViewが大きく配置される。物好きな人はお試しください。
    この値を"wrap_content"にした場合、各Viewの配置割合は不正確になる。
  3. 縦方向で使う場合には、(layout_widthを0dpにするのではなく)layout_heightの値を"0dp"にする。
  4. layout_weightで指定した大きさを超える長い文字列がViewに代入された場合、layout_weightの指定が優先適用され、指定された方向にはサイズは伸びない。そして、Viewは、layout_weightによる指定が無い方向に伸びていく。
  5. android:weightSumに代入する値が、各layout_weightの値の合計よりも多い場合、その多い分が余白となる。つまり、 android:weightSumは余白を設けるコマンドである。
  6. android:weightSumは設けなくても良い。この値が、各layout_weightの値の合計と同一であるならば、android:weightSumを書く意味は無い。
  7. LinearLayoutの子Viewにだけしか使えない。ただし、LinearLayoutを継承したViewGroupの子Viewにも使える。例えば、RadioGroupの子ViewであるRadioButtonにも使える。
  8. LayoutInflaterを用いて追加するViewでは、inflater()の第二引数に親のLinearLayoutを指定することによって、layout_weightが有効に機能する。詳細は動的にレイアウトの挿入&削除を繰り返すを見てください。
例:縦方向の場合
画面の上の方に80%の余白を設け、80%よりも下の位置からButtonを配置する場合

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical"
        android:weightSum="100"
        >
        <!-- 位置調整のためのダミーView -->
        <View
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="80"
            >
        </View>
        <Button (以下、省略)


例:横方向の場合
android:weightSumの余白作成機能を用いて、Viewを中央に配置することができる。この場合、余白を作成するためのダミーのViewを必要としない。
左の余白が1、SeekBarが8、右の余白が1の割合でSeekBarを中央に配置するxmlのサンプルは次のとおりです。
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:weightSum="100"
        android:gravity="center"
        >
        <SeekBar
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="80"
            >
        </SeekBar>
    </LinearLayout>

例:RadioButtonを横方向に均等に並べる場合
    <RadioGroup
        android:id="@+id/radioG_Start"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >
        <RadioButton
            android:id="@+id/radio_StartZero"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="50"
            android:text="@string/RadioEndVideo"
            >
        </RadioButton>
        <RadioButton
            android:id="@+id/radio_StartFavorite"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="50"
            android:text="@string/SetTime"
            >
        </RadioButton>
    </RadioGroup>

私が実験用に作成したxmlを掲載しておきます。
"Buttoooooooooooon"などと、ボタンに執着があるかのような記述がありますが、これは長い文字列を設定した場合の挙動を実験するためのものであり、私個人の嗜好を示すものでは無いことを念のため申し添えておきます。

Eclipseのxmlエディタの下の方にある「Graphical Layout」タブをクリックすると、ビルドすること無く、画面を確認できます。

<?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"
    >
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        >
        <SeekBar
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="60"
            >
        </SeekBar>
        <TextView
            android:text="Valuuuuuuuuuuuuuuue"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="20"
            >
        </TextView>
        <Button
            android:text="Buttoooooooooooon"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="20"
            >
        </Button>
    </LinearLayout>
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:weightSum="120"
        >
        <Button
            android:text="Button"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="60"
            >
        </Button>
        <Button
            android:text="Buttoooooooooooon"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="20"
            >
        </Button>
        <Button
            android:text="Button"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="20"
            >
        </Button>
    </LinearLayout>
</LinearLayout>

【開発環境】
OS:Windows Vista
Eclipse IDE for Java Developers Version: Indigo Release