2012年3月12日月曜日

複雑な形状のViewの一覧を作成する

複雑な形状のViewの一覧を作成する

ListViewを使えば複雑な形状のViewの一覧を作成できる。
参考:SimpleAdapterのsample code
しかし、この技法の欠点は、書き込み可能なView、例えば、EditTextを配置しても、そのEditTextに対して利用者が書き込みを行うことはできない、ということである。
ListViewのような作り込まれたものは、プログラミングが終わった後で、予期しない不都合が判明する。

素直に、ScrollView & LayoutInflaterを使って、地道に作成する。

ただし、この場合、EditTextにはIDを与えてはいけない、という点に留意しなければならない。
例えば、次のxmlのように、android:id="@+id/EditText"を設けてはいけない。削除しよう。
    <EditText
        android:id="@+id/EditText"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        >
    </EditText>

仮に、IDを設けたとしても、初回起動時には、上手く表示される。しかし、configuration changeに伴う再起動が発生した後で表示されるEditTextには、全て同一の文字列が代入されてしまう。
この問題を避けるために、ID指定を書いてはいけないのである。
TextViewの場合はID指定をしても問題は発生しない。

main.xmlは次のとおりである。IDがParentListのLinearLayoutに、複数の子Viewを動的に追加する。
<?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"
    >
    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        >
        <LinearLayout
            android:id="@+id/ParentList"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            >
        </LinearLayout>
    </ScrollView>>
</LinearLayout>

上記のxmlの中に、次のlistitem.xmlを動的に挿入する。各ViewにはIDを付けていない。
<?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"
    android:padding="2dp"
    android:background="#555"
    >
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:background="#000"
        >
        <CheckBox
            android:text="@string/Apply"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            >
        </CheckBox>
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            >
            <TextView
                android:text="@string/Before"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
            >
            </TextView>
            <EditText
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                >
            </EditText>
        </LinearLayout>
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            >
            <TextView
                android:text="@string/After"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
            >
            </TextView>
           <EditText
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                >
            </EditText>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

上記2個のxmlを使って、複数のViewを動的に生成し、一覧を作成するJavaのcodeは次のとおりである。ViewのIDが存在しないため、個々のViewの識別はアドレス(相対位置と表現して良いかもしれない)で行う。
public class InflateListActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); 
        setContentView(R.layout.main);
        
        int i;
        ArrayList<String> al;
        BackupData bd;
        
        bd = (BackupData)getLastNonConfigurationInstance();
        if(bd==null){
            //サンプルプログラムなので適当にデータを代入する。
            al = new ArrayList<String>();
            al.add("true");
            al.add("Green");
            al.add("Blue");
            al.add("false");
            al.add("Green");
            al.add("Blue");
            al.add("true");
            al.add("Green");
            al.add("Blue");
            al.add("false");
            al.add("Red");
            al.add("Blue");
            al.add("true");
            al.add("Yellow");
            al.add("Red");
        }
        else{//configuration changeに伴う再起動
            al = bd.getBackupData();
        }
        
        for(i=0; i<al.size()/3; i++){
            LayoutInflater inf;
            LinearLayout llParent, ll, ll2, llChild;
            CheckBox cb;
            EditText et;
            String s;
            
            //親Viewを取得する。
            llParent = (LinearLayout)findViewById(R.id.ParentList);
            
            //挿入する子Viewを作成する。
            inf = getLayoutInflater();
            ll = (LinearLayout)inf.inflate(R.layout.listitem, null);
            
            ll2 = (LinearLayout)ll.getChildAt(0);
            
            //子Viewにデータを入れる。
            cb = (CheckBox)ll2.getChildAt(0);
            s = al.get(i*3);
            if(s.equals("true")==true) cb.setChecked(true);
            else cb.setChecked(false);
            
            llChild = (LinearLayout)ll2.getChildAt(1);
            et = (EditText)llChild.getChildAt(1);
            et.setText(al.get(i*3+1));
            
            llChild = (LinearLayout)ll2.getChildAt(2);
            et = (EditText)llChild.getChildAt(1);
            et.setText(al.get(i*3+2));
            
            //親Viewに子Viewを入れる。
            llParent.addView(ll);
        }
    }
    
    //configuration changeに伴う再起動への対応
    @Override
    public Object onRetainNonConfigurationInstance(){
        ArrayList<String> al;
        BackupData bd;
        LinearLayout llParent;
        int i, iSize;
        
        al = new ArrayList<String>();
        llParent = (LinearLayout)findViewById(R.id.ParentList);
        iSize = llParent.getChildCount();
        for(i=0; i<iSize; i++){
            LinearLayout ll, ll2, llChild;
            CheckBox cb;
            EditText et;
            String s;
            
            ll = (LinearLayout)llParent.getChildAt(i);
            ll2 = (LinearLayout)ll.getChildAt(0);
            
            cb = (CheckBox)ll2.getChildAt(0);
            if(cb.isChecked()==true) s = "true";
            else s = "false";
            al.add(s);
            
            llChild = (LinearLayout)ll2.getChildAt(1);
            et = (EditText)llChild.getChildAt(1);
            s = et.getText().toString();
            al.add(s);
            
            llChild = (LinearLayout)ll2.getChildAt(2);
            et = (EditText)llChild.getChildAt(1);
            s = et.getText().toString();
            al.add(s);
        }
        
        bd = new BackupData();
        bd.setBackupData(al);
        return bd;
    }
    
    private class BackupData{
        private ArrayList<String> dataBackup;
        void setBackupData(ArrayList<String> al){
            dataBackup = al;
        }
        ArrayList<String> getBackupData(){
            return dataBackup;
        }
    }
}



0 件のコメント:

コメントを投稿