1 /* 2 * Copyright (C) 2013 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 com.example.android.requestduringlayout; 18 19 import com.android.requestduringlayout.R; 20 21 import android.app.Activity; 22 import android.content.Context; 23 import android.os.Bundle; 24 import android.util.AttributeSet; 25 import android.view.View; 26 import android.widget.Button; 27 import android.widget.LinearLayout; 28 29 /** 30 * This example shows what horrible things can result from calling requestLayout() during 31 * a layout pass. DON'T DO THIS. 32 * 33 * Watch the associated video for this demo on the DevBytes channel of developer.android.com 34 * or on YouTube at https://www.youtube.com/watch?v=HbAeTGoKG6k. 35 */ 36 public class RequestDuringLayout extends Activity { 37 38 @Override onCreate(Bundle savedInstanceState)39 public void onCreate(Bundle savedInstanceState) { 40 super.onCreate(savedInstanceState); 41 setContentView(R.layout.activity_request_during_layout); 42 43 final MyLayout myLayout = (MyLayout) findViewById(R.id.container); 44 Button addViewButton = (Button) findViewById(R.id.addView); 45 Button removeViewButton = (Button) findViewById(R.id.removeView); 46 Button forceLayoutButton = (Button) findViewById(R.id.forceLayout); 47 48 addViewButton.setOnClickListener(new View.OnClickListener() { 49 @Override 50 public void onClick(View v) { 51 myLayout.mAddRequestPending = true; 52 myLayout.requestLayout(); 53 } 54 }); 55 56 removeViewButton.setOnClickListener(new View.OnClickListener() { 57 @Override 58 public void onClick(View v) { 59 myLayout.mRemoveRequestPending = true; 60 myLayout.requestLayout(); 61 } 62 }); 63 64 forceLayoutButton.setOnClickListener(new View.OnClickListener() { 65 @Override 66 public void onClick(View v) { 67 myLayout.requestLayout(); 68 } 69 }); 70 71 } 72 73 /** 74 * Custom layout to enable the convoluted way of requesting-during-layout that we're 75 * trying to show here. Yes, it's a hack. But it's a case that many apps hit (in much more 76 * complicated and less demoable ways), so it's interesting to at least understand the 77 * artifacts that come from this sequence of events. 78 */ 79 static class MyLayout extends LinearLayout { 80 81 int numButtons = 0; 82 boolean mAddRequestPending = false; 83 boolean mRemoveRequestPending = false; 84 MyLayout(Context context, AttributeSet attrs, int defStyle)85 public MyLayout(Context context, AttributeSet attrs, int defStyle) { 86 super(context, attrs, defStyle); 87 } 88 MyLayout(Context context, AttributeSet attrs)89 public MyLayout(Context context, AttributeSet attrs) { 90 super(context, attrs); 91 } 92 MyLayout(Context context)93 public MyLayout(Context context) { 94 super(context); 95 } 96 97 @Override onLayout(boolean changed, int l, int t, int r, int b)98 protected void onLayout(boolean changed, int l, int t, int r, int b) { 99 super.onLayout(changed, l, t, r, b); 100 // Here is the root of the problem: we are adding/removing views during layout. This 101 // means that this view and its container will be put into an uncertain state that 102 // can be difficult to discover and recover from. 103 // Better approach: just add/remove at a time when layout is not running, certainly not 104 // in the middle of onLayout(), or other layout-associated logic. 105 if (mRemoveRequestPending) { 106 removeButton(); 107 mRemoveRequestPending = false; 108 } 109 if (mAddRequestPending) { 110 addButton(); 111 mAddRequestPending = false; 112 } 113 } 114 removeButton()115 private void removeButton() { 116 if (getChildCount() > 1) { 117 removeViewAt(1); 118 } 119 } 120 addButton()121 private void addButton() { 122 Button button = new Button(getContext()); 123 button.setLayoutParams(new LayoutParams( 124 LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); 125 button.setText("Button " + (numButtons++)); 126 addView(button); 127 } 128 129 } 130 131 } 132