1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.util; 18 19 import android.app.Activity; 20 import android.content.Context; 21 import android.os.Bundle; 22 import android.view.View; 23 import android.view.ViewGroup; 24 import android.view.Window; 25 import android.widget.Button; 26 import android.widget.LinearLayout; 27 import android.widget.ScrollView; 28 import android.widget.TextView; 29 30 import com.google.android.collect.Lists; 31 32 import java.util.List; 33 34 /** 35 * Utility base class for creating scroll view scenarios, allowing you to add 36 * a series of different kinds of views arranged vertically, taking up a 37 * specified amount of the screen height. 38 */ 39 public abstract class ScrollViewScenario extends Activity { 40 41 /** 42 * Holds content of scroll view 43 */ 44 private LinearLayout mLinearLayout; 45 46 /** 47 * The actual scroll view 48 */ 49 private ScrollView mScrollView; 50 51 52 /** 53 * What we need of each view that the user wants: the view, and the ratio 54 * to the screen height for its desired height. 55 */ 56 private interface ViewFactory { create(final Context context)57 View create(final Context context); 58 getHeightRatio()59 float getHeightRatio(); 60 } 61 62 /** 63 * Partially implement ViewFactory given a height ratio. 64 * A negative height ratio means that WRAP_CONTENT will be used as height 65 */ 66 private static abstract class ViewFactoryBase implements ViewFactory { 67 68 private float mHeightRatio; 69 70 @SuppressWarnings({"UnusedDeclaration"}) ViewFactoryBase()71 private ViewFactoryBase() {throw new UnsupportedOperationException("don't call this!");} 72 ViewFactoryBase(float heightRatio)73 protected ViewFactoryBase(float heightRatio) { 74 mHeightRatio = heightRatio; 75 } 76 getHeightRatio()77 public float getHeightRatio() { 78 return mHeightRatio; 79 } 80 } 81 82 /** 83 * Builder for selecting the views to be vertically arranged in the scroll 84 * view. 85 */ 86 @SuppressWarnings({"JavaDoc"}) 87 public static class Params { 88 89 List<ViewFactory> mViewFactories = Lists.newArrayList(); 90 91 int mTopPadding = 0; 92 int mBottomPadding = 0; 93 94 /** 95 * Add a text view. 96 * @param text The text of the text view. 97 * @param heightRatio The view's height will be this * the screen height. 98 */ addTextView(final String text, float heightRatio)99 public Params addTextView(final String text, float heightRatio) { 100 mViewFactories.add(new ViewFactoryBase(heightRatio) { 101 public View create(final Context context) { 102 final TextView tv = new TextView(context); 103 tv.setText(text); 104 return tv; 105 } 106 }); 107 return this; 108 } 109 110 /** 111 * Add multiple text views. 112 * @param numViews the number of views to add. 113 * @param textPrefix The text to prepend to each text view. 114 * @param heightRatio The view's height will be this * the screen height. 115 */ addTextViews(int numViews, String textPrefix, float heightRatio)116 public Params addTextViews(int numViews, String textPrefix, float heightRatio) { 117 for (int i = 0; i < numViews; i++) { 118 addTextView(textPrefix + i, heightRatio); 119 } 120 return this; 121 } 122 123 /** 124 * Add a button. 125 * @param text The text of the button. 126 * @param heightRatio The view's height will be this * the screen height. 127 */ addButton(final String text, float heightRatio)128 public Params addButton(final String text, float heightRatio) { 129 mViewFactories.add(new ViewFactoryBase(heightRatio) { 130 public View create(final Context context) { 131 final Button button = new Button(context); 132 button.setText(text); 133 return button; 134 } 135 }); 136 return this; 137 } 138 139 /** 140 * Add multiple buttons. 141 * @param numButtons the number of views to add. 142 * @param textPrefix The text to prepend to each button. 143 * @param heightRatio The view's height will be this * the screen height. 144 */ addButtons(int numButtons, String textPrefix, float heightRatio)145 public Params addButtons(int numButtons, String textPrefix, float heightRatio) { 146 for (int i = 0; i < numButtons; i++) { 147 addButton(textPrefix + i, heightRatio); 148 } 149 return this; 150 } 151 152 /** 153 * Add an {@link InternalSelectionView}. 154 * @param numRows The number of rows in the internal selection view. 155 * @param heightRatio The view's height will be this * the screen height. 156 */ addInternalSelectionView(final int numRows, float heightRatio)157 public Params addInternalSelectionView(final int numRows, float heightRatio) { 158 mViewFactories.add(new ViewFactoryBase(heightRatio) { 159 public View create(final Context context) { 160 return new InternalSelectionView(context, numRows, "isv"); 161 } 162 }); 163 return this; 164 } 165 166 /** 167 * Add a sublayout of buttons as a single child of the scroll view. 168 * @param numButtons The number of buttons in the sub layout 169 * @param heightRatio The layout's height will be this * the screen height. 170 */ addVerticalLLOfButtons(final String prefix, final int numButtons, float heightRatio)171 public Params addVerticalLLOfButtons(final String prefix, final int numButtons, float heightRatio) { 172 mViewFactories.add(new ViewFactoryBase(heightRatio) { 173 174 public View create(Context context) { 175 final LinearLayout ll = new LinearLayout(context); 176 ll.setOrientation(LinearLayout.VERTICAL); 177 178 // fill width, equally weighted on height 179 final LinearLayout.LayoutParams lp = 180 new LinearLayout.LayoutParams( 181 ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f); 182 for (int i = 0; i < numButtons; i++) { 183 final Button button = new Button(context); 184 button.setText(prefix + i); 185 ll.addView(button, lp); 186 } 187 188 return ll; 189 } 190 }); 191 return this; 192 } 193 addPaddingToScrollView(int topPadding, int bottomPadding)194 public Params addPaddingToScrollView(int topPadding, int bottomPadding) { 195 mTopPadding = topPadding; 196 mBottomPadding = bottomPadding; 197 198 return this; 199 } 200 } 201 202 /** 203 * Override this and initialized the views in the scroll view. 204 * @param params Used to configure the contents of the scroll view. 205 */ init(Params params)206 protected abstract void init(Params params); 207 getLinearLayout()208 public LinearLayout getLinearLayout() { 209 return mLinearLayout; 210 } 211 getScrollView()212 public ScrollView getScrollView() { 213 return mScrollView; 214 } 215 216 /** 217 * Get the child contained within the vertical linear layout of the 218 * scroll view. 219 * @param index The index within the linear layout. 220 * @return the child within the vertical linear layout of the scroll view 221 * at the specified index. 222 */ 223 @SuppressWarnings({"unchecked"}) getContentChildAt(int index)224 public <T extends View> T getContentChildAt(int index) { 225 return (T) mLinearLayout.getChildAt(index); 226 } 227 228 /** 229 * Hook for changing how scroll view's are created. 230 */ 231 @SuppressWarnings({"JavaDoc"}) createScrollView()232 protected ScrollView createScrollView() { 233 return new ScrollView(this); 234 } 235 236 @Override onCreate(Bundle savedInstanceState)237 protected void onCreate(Bundle savedInstanceState) { 238 super.onCreate(savedInstanceState); 239 240 // for test stability, turn off title bar 241 requestWindowFeature(Window.FEATURE_NO_TITLE); 242 int screenHeight = getWindowManager().getDefaultDisplay().getHeight() 243 - 25; 244 mLinearLayout = new LinearLayout(this); 245 mLinearLayout.setOrientation(LinearLayout.VERTICAL); 246 247 // initialize params 248 final Params params = new Params(); 249 init(params); 250 251 // create views specified by params 252 for (ViewFactory viewFactory : params.mViewFactories) { 253 int height = ViewGroup.LayoutParams.WRAP_CONTENT; 254 if (viewFactory.getHeightRatio() >= 0) { 255 height = (int) (viewFactory.getHeightRatio() * screenHeight); 256 } 257 final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( 258 ViewGroup.LayoutParams.MATCH_PARENT, height); 259 mLinearLayout.addView(viewFactory.create(this), lp); 260 } 261 262 mScrollView = createScrollView(); 263 mScrollView.setPadding(0, params.mTopPadding, 0, params.mBottomPadding); 264 mScrollView.addView(mLinearLayout, new ViewGroup.LayoutParams( 265 ViewGroup.LayoutParams.MATCH_PARENT, 266 ViewGroup.LayoutParams.MATCH_PARENT)); 267 268 // no animation to speed up tests 269 mScrollView.setSmoothScrollingEnabled(false); 270 271 setContentView(mScrollView); 272 mScrollView.post(() -> mScrollView.restoreDefaultFocus()); 273 } 274 } 275