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