DevBytes: RequestDuringLayout

简介:

Gooogle Android 团队在  https://www.youtube.com/watch?v=HbAeTGoKG6k 的课程《DevBytes: RequestDuringLayout》,描述了,我们不应该在代码布局的时候,我们不应该在上面写一些code,特别是布局的代码,这将要影响布局。

Demo下载地址:http://developer.android.com/shareables/devbytes/RequestDuringLayout.zip

Code:

**
 * This example shows what horrible things can result from calling requestLayout() during
 * a layout pass. DON'T DO THIS.
 * This example shows 一个糟糕的结果,当调用了 requestLayout() 在布局的过程中。不要这样做!
 *
 * Watch the associated video for this demo on the DevBytes channel of developer.android.com
 * or on YouTube at https://www.youtube.com/watch?v=HbAeTGoKG6k.
 */
public class RequestDuringLayout extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_request_during_layout);

        final MyLayout myLayout = (MyLayout) findViewById(R.id.container);
        Button addViewButton = (Button) findViewById(R.id.addView);
        Button removeViewButton = (Button) findViewById(R.id.removeView);
        Button forceLayoutButton = (Button) findViewById(R.id.forceLayout);

        addViewButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myLayout.mAddRequestPending = true;
                myLayout.requestLayout();
            }
        });

        removeViewButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myLayout.mRemoveRequestPending = true;
                myLayout.requestLayout();
            }
        });

        forceLayoutButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myLayout.requestLayout();
            }
        });

    }

    /**
     * Custom layout to enable the convoluted way of requesting-during-layout that we're
     * trying to show here. Yes, it's a hack. But it's a case that many apps hit (in much more
     * complicated and less demoable ways), so it's interesting to at least understand the
     * artifacts that come from this sequence of events.
     * 
     * 
     */
    static class MyLayout extends LinearLayout {

        int numButtons = 0;
        boolean mAddRequestPending = false;
        boolean mRemoveRequestPending = false;

        public MyLayout(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }

        public MyLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        public MyLayout(Context context) {
            super(context);
        }

        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
        	super.onLayout(changed, l, t, r, b);
            // Here is the root of the problem: we are adding/removing views during layout. This
            // means that this view and its container will be put into an uncertain state that
            // can be difficult to discover and recover from.
        	// Better approach: just add/remove at a time when layout is not running, certainly not
            // in the middle of onLayout(), or other layout-associated logic.
//      >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>	
//       	 Here is the root of the problem:we adding/removing views during layout这意味着,view和
//       	它的container(容器)将要存在一个不安全的状态,这将是很难发现和修复的。
//        	Better approach: just add/remove 在一个布局不是在运行的时候,也不是在布局的任何的逻辑顺序的时候。
        	
            if (mRemoveRequestPending) {
                removeButton();
                mRemoveRequestPending = false;
            }
            if (mAddRequestPending) {
                addButton();
                mAddRequestPending = false;
            }
            

        }

        private void removeButton() {
            if (getChildCount() > 1) {
                removeViewAt(1);
            }
        }

        private void addButton() {
            Button button = new Button(getContext());
            button.setLayoutParams(new LayoutParams(
                    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
            button.setText("Button " + (numButtons++));
            addView(button);
        }

    }

}


<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2013 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<view xmlns:android="http://schemas.android.com/apk/res/android"
      class="com.android.requestduringlayout.RequestDuringLayout$MyLayout"
      android:orientation="vertical"
      android:id="@+id/container"
      android:layout_width="match_parent"
      android:layout_height="match_parent" >

    <LinearLayout
            android:orientation="horizontal"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" >
        <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/addView"
                android:id="@+id/addView" />
        <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/removeView"
                android:id="@+id/removeView" />
        <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/forceLayout"
                android:id="@+id/forceLayout" />
    </LinearLayout>

</view>

截图:

      

当点击了Add button的时候,没有效果,在点击Layout Button的时候,才会出现图二的效果。

在容器进行布局的时候会onMeasure  onLayout ...之类的过程.

我们不应该在图形布局的时候的任何的逻辑的过程中,进行一些影响布局的操作。

view绘制过程:

Category Methods Description
Creation Constructors There is a form of the constructor that are called when the view is created from code and a form that is called when the view is inflated from a layout file. The second form should parse and apply any attributes defined in the layout file.
onFinishInflate() Called after a view and all of its children has been inflated from XML.
Layout onMeasure(int, int) Called to determine the size requirements for this view and all of its children.
onLayout(boolean, int, int, int, int) Called when this view should assign a size and position to all of its children.
onSizeChanged(int, int, int, int) Called when the size of this view has changed.
Drawing onDraw(android.graphics.Canvas) Called when the view should render its content.
Event processing onKeyDown(int, KeyEvent) Called when a new hardware key event occurs.
onKeyUp(int, KeyEvent) Called when a hardware key up event occurs.
onTrackballEvent(MotionEvent) Called when a trackball motion event occurs.
onTouchEvent(MotionEvent) Called when a touch screen motion event occurs.
Focus onFocusChanged(boolean, int, android.graphics.Rect) Called when the view gains or loses focus.
onWindowFocusChanged(boolean) Called when the window containing the view gains or loses focus.
Attaching onAttachedToWindow() Called when the view is attached to a window.
onDetachedFromWindow() Called when the view is detached from its window.
onWindowVisibilityChanged(int) Called when the visibility of the window containing the view has c
ps:view的绘制的流程 https://developer.android.com/intl/zh-cn/reference/android/view/View.html

解决方案:

解决这个问题有很多种方式,最简单的方式就是注释掉onLayout中的Code,直接调用addButton方法即可。

见code:

public class RequestDuringLayout extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_request_during_layout);

        final MyLayout myLayout = (MyLayout) findViewById(R.id.container);
        Button addViewButton = (Button) findViewById(R.id.addView);
        Button removeViewButton = (Button) findViewById(R.id.removeView);
        Button forceLayoutButton = (Button) findViewById(R.id.forceLayout);

        addViewButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//                myLayout.mAddRequestPending = true;
//                myLayout.requestLayout();
            	myLayout.addButton();
            }
            
        });

        removeViewButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//                myLayout.mRemoveRequestPending = true;
//                myLayout.requestLayout();
            	myLayout.removeButton();
            }
        });

        forceLayoutButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myLayout.requestLayout();
            }
        });

    }

    static class MyLayout extends LinearLayout {

        int numButtons = 0;
        boolean mAddRequestPending = false;
        boolean mRemoveRequestPending = false;

        public MyLayout(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }

        public MyLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        public MyLayout(Context context) {
            super(context);
        }

        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
        	super.onLayout(changed, l, t, r, b);
        }

        private void removeButton() {
            if (getChildCount() > 1) {
                removeViewAt(1);
            }
        }

        private void addButton() {
            Button button = new Button(getContext());
            button.setLayoutParams(new LayoutParams(
                    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
            button.setText("Button " + (numButtons++));
            addView(button);
        }

    }

}

参考:



发布了120 篇原创文章 · 获赞 1 · 访问量 27万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章