Custom View Tips

Toru Hosokawa
twitter:@anton0825
facebook:toru.hosokawa1
github:hosokawa0825
blog:http://d.hatena.ne.jp/anton0825/

2015.4.15

一つのlayout xmlを複数回includeしている時に内部のViewにアクセスする方法

子layout xml


                    <TextView android:id="@+id/text_view"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />
                

親layout xml


                    <include layout="@layout/view_contact_name" android:id="+id/test1"/>
                    <include layout="@layout/view_contact_name" android:id="+id/test2"/>
                

findViewByIdを複数回実行してViewを取得する


                    @Override
                    public void onViewCreated(View view, Bundle savedInstanceState) {
                        super.onViewCreated(view, savedInstanceState);
                        TextView tv1 = view.findViewById(R.id.test1).findViewById(R.id.text_view);
                        TextView tv2 = view.findViewById(R.id.test2).findViewById(R.id.text_view);
                    }
                

Custom ViewのonSave/RestoreInstanceStateを簡潔に実装する

BaseSavedStateを継承したSavedStateクラスを定義する方法が一般的だが、コード量が増えてしまう。

コード例

Bundleを使えば簡潔に書ける

コード例

Custom Viewを複数並べる際の注意点

Custom Viewを複数並べると、onRestoreInstanceStateが実行された際に同じ状態になってしまうことがある

以下の例は「注文価格」の行がCustom Viewになっている

実行前    実行後

なぜそうなるか?

ViewGroup


                    @Override
                    protected void dispatchRestoreInstanceState(SparseArray container) {
                        super.dispatchRestoreInstanceState(container);
                        final int count = mChildrenCount;
                        final View[] children = mChildren;
                        for (int i = 0; i < count; i++) {
                            View c = children[i];
                            if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
                                c.dispatchRestoreInstanceState(container);
                            }
                        }
                    }
                

Viewの状態を保存するcontainerはFragment/Activity毎に一つ

View


                    protected void dispatchRestoreInstanceState(SparseArray container) {
                        if (mID != NO_ID) {
                            Parcelable state = container.get(mID);
                            if (state != null) {
                                // Log.i("View", "Restoreing #" + Integer.toHexString(mID)
                                // + ": " + state);
                                mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
                                onRestoreInstanceState(state);
                                if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
                                    throw new IllegalStateException(
                                        "Derived class did not call super.onRestoreInstanceState()");
                                }
                            }
                        }
                    }
                

ViewのIDをkeyとしてstateをcontainerから取得するため、IDが同じViewには同じstateが渡される

解決策

android:id属性を削除する。android:tagを付ける。findViewWithTagを使用する


                    @Override
                    public void onFinishInflate() {
                        inflate(getContext(), R.layout.parent_view, this);
                        childView = ((NumberPicker) this.findViewWithTag("child_view"));
                    }