ViewsStateBundle.java revision 81a36a4dd93bf2f14c2eb88ae01464f85ddb0706
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14package android.support.v17.leanback.widget;
15
16import android.os.Bundle;
17import android.os.Parcelable;
18import android.util.SparseArray;
19import android.view.View;
20
21/**
22 * Maintains a bundle of states for a group of views. Each view must have a unique id to identify
23 * it. There are four different strategies {@link #SAVE_NO_CHILD} {@link #SAVE_ON_SCREEN_CHILD}
24 * {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}.
25 * <p>
26 * This class serves purpose of nested "listview" e.g.  a vertical list of horizontal list.
27 * Vertical list maintains id->bundle mapping of all it's children (even the children is offscreen
28 * and being pruned).
29 * <p>
30 * The class is currently used within {@link GridLayoutManager}, but it might be used by other
31 * ViewGroup.
32 */
33class ViewsStateBundle {
34
35    /** dont save states of any child views */
36    public static final int SAVE_NO_CHILD = 0;
37    /** only save on screen child views, the states are lost when they become off screen */
38    public static final int SAVE_ON_SCREEN_CHILD = 1;
39    /** save on screen views plus save off screen child views states up to {@link #getLimitNumber()} */
40    public static final int SAVE_LIMITED_CHILD = 2;
41    /**
42     * save on screen views plus save off screen child views without any limitation. This might cause out
43     * of memory, only use it when you are dealing with limited data
44     */
45    public static final int SAVE_ALL_CHILD = 3;
46
47    public static final int DEFAULT_LIMIT = 100;
48
49    private int mSavePolicy;
50    private int mLimitNumber;
51
52    private final Bundle mChildStates;
53
54    public ViewsStateBundle(int policy, int limit) {
55        mSavePolicy = policy;
56        mLimitNumber = limit;
57        mChildStates = new Bundle();
58    }
59
60    public void clear() {
61        mChildStates.clear();
62    }
63
64    public void remove(int id) {
65        if (!mChildStates.isEmpty()) {
66            mChildStates.remove(getSaveStatesKey(id));
67        }
68    }
69
70    /**
71     * @return the saved views states
72     */
73    public final Bundle getChildStates() {
74        return mChildStates;
75    }
76
77    /**
78     * @return the savePolicy, see {@link #SAVE_NO_CHILD} {@link #SAVE_ON_SCREEN_CHILD}
79     *         {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}
80     */
81    public final int getSavePolicy() {
82        return mSavePolicy;
83    }
84
85    /**
86     * @return the limitNumber, only works when {@link #getSavePolicy()} is
87     *         {@link #SAVE_LIMITED_CHILD}
88     */
89    public final int getLimitNumber() {
90        return mLimitNumber;
91    }
92
93    /**
94     * @see ViewsStateBundle#getSavePolicy()
95     */
96    public final void setSavePolicy(int savePolicy) {
97        this.mSavePolicy = savePolicy;
98    }
99
100    /**
101     * @see ViewsStateBundle#getLimitNumber()
102     */
103    public final void setLimitNumber(int limitNumber) {
104        this.mLimitNumber = limitNumber;
105    }
106
107    /**
108     * Load view from states, it's none operation if the there is no state associated with the id.
109     *
110     * @param view view where loads into
111     * @param id unique id for the view within this ViewsStateBundle
112     */
113    public final void loadView(View view, int id) {
114        String key = getSaveStatesKey(id);
115        SparseArray<Parcelable> container = mChildStates.getSparseParcelableArray(key);
116        if (container != null) {
117            view.restoreHierarchyState(container);
118        }
119    }
120
121    /**
122     * Save views regardless what's the current policy is.
123     *
124     * @param view view to save
125     * @param id unique id for the view within this ViewsStateBundle
126     */
127    protected final void saveViewUnchecked(View view, int id) {
128        String key = getSaveStatesKey(id);
129        SparseArray<Parcelable> container = new SparseArray<Parcelable>();
130        view.saveHierarchyState(container);
131        mChildStates.putSparseParcelableArray(key, container);
132    }
133
134    /**
135     * The on screen view is saved when policy is not {@link #SAVE_NO_CHILD}.
136     *
137     * @param view
138     * @param id
139     */
140    public final void saveOnScreenView(View view, int id) {
141        if (mSavePolicy != SAVE_NO_CHILD) {
142            saveViewUnchecked(view, id);
143        }
144    }
145
146    /**
147     * Save off screen views according to policy.
148     *
149     * @param view view to save
150     * @param id unique id for the view within this ViewsStateBundle
151     */
152    public final void saveOffscreenView(View view, int id) {
153        switch (mSavePolicy) {
154            case SAVE_LIMITED_CHILD:
155                if (mChildStates.size() > mLimitNumber) {
156                    // TODO prune the Bundle to be under limit
157                }
158                // slip through next case section to save view
159            case SAVE_ALL_CHILD:
160                saveViewUnchecked(view, id);
161                break;
162            default:
163                break;
164        }
165    }
166
167    static String getSaveStatesKey(int id) {
168        return Integer.toString(id);
169    }
170}
171