精华内容
下载资源
问答
  • 主要为大家详细介绍了Android使用addView动态添加组件的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • Android使用addView动态添加组件,自定义添加列表项,添加LinearLayout
  • 安卓使用WindowManager.addView()简单实现悬浮窗口播放视频
  • 三层listview addview嵌套

    2017-04-18 17:03:01
    通过addview 实现三层listview动态嵌套
  • ADDvIEW

    2019-04-06 20:58:04
    public AddView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); View view =LayoutInflater.from(context).inflate(R.layout.myview,this,true); jia = view....

    package com.example.aason.zhusha46.view;

    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.widget.EditText;
    import android.widget.LinearLayout;
    import android.widget.TextView;
    import android.widget.Toast;

    import com.example.aason.zhusha46.R;

    public class AddView extends LinearLayout {

    private TextView jia,jian;
    private EditText count;
    private int num=1;
    
    public AddView(Context context) {
        super(context);
    }
    
    public AddView(Context context,AttributeSet attrs) {
        this(context, attrs,0);
    }
    
    public AddView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        View view  =LayoutInflater.from(context).inflate(R.layout.myview,this,true);
        jia = view.findViewById(R.id.jia);
        jian = view.findViewById(R.id.jian);
        count = view.findViewById(R.id.count);
        count.setText("1");
        jia.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                num++;
                count.setText(num+"");
                if (addCallBack != null) {
                    addCallBack.numCallBack(num);
                }
            }
        });
        jian.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                num--;
                if (num == 0) {
                    num=1;
                    Toast.makeText(getContext(), "不能再减了", Toast.LENGTH_SHORT).show();
                }
                count.setText(num+"");
                if (addCallBack != null) {
                    addCallBack.numCallBack(num);
                }
            }
        });
    }
    public int getNum() {
        return num;
    }
    private  AddCallBack addCallBack;
    
    public void setAddCallBack(AddCallBack addCallBack) {
        this.addCallBack = addCallBack;
    }
    public interface AddCallBack{
        void numCallBack(int num);
    }
    

    }

    展开全文
  • 开发过程中经常会通过addView()方法动态添加子控件,如果不注意的话会出现异常: Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the ...

    开发过程中经常会通过addView()方法动态添加子控件,如果不注意的话会出现异常:
    Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child’s parent first.

    一、实际案例

    下面我们通过一个demo来演示分析:

    1、创建自定义控件并提供addTag方法来添加子view
    • 示例代码
    class CustomLinearLayout @JvmOverloads constructor(
        context: Context,
        attributeSet: AttributeSet,
        defaultStyle: Int? = 0
    ) : LinearLayout(context, attributeSet) {
    
        fun addTag(list: List<String>) {
            if (list.isEmpty()) return
            val tagView = createTagView()
            for (index in list.indices) {
                tagView.text = list[index]
                addView(tagView)
            }
    
        }
    
        private fun createTagView(): TextView {
            return LayoutInflater.from(context).inflate(R.layout.item_text, null) as TextView
        }
    }
    
    2、直接运行报异常:

    Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child’s parent first.

    3、分析

    代码分析:

    通过上述代码我们通过createTagView()方法创建一个textView实例,并通过addView的方式将该实例添加到CustomLinearLayout中

    运行结果分析:

    通过运行,得到的结果是java.lang.IllegalStateException

    原因分析:

    通过异常提示可知,是因为该child实例已经拥有了一个parent,导致异常出现,也就意味着每一个child实例只能拥有一个parent

    4、解决方法

    结合上述分析是因为一个实例,已经了parent导致,那么我们可以从两个方面来解决:

    • 方法一:添加前调用removeView

    在添加childview之前先进行parent判断,如果不为空就调用removeView

      fun addTag(list: List<String>) {
            if (list.isEmpty()) return
            val tagView = createTagView()
            for (index in list.indices) {
                tagView.text = list[index]
                val parentView = tagView.parent as ViewGroup
                if (parentView != null) {
             parentView.removeView(tagView)
                    addView(tagView)
                }
            }
    
        }
    
    • 运行效果图
      在这里插入图片描述

    但是通过运行发现,当使用同一个child实例时,添加多个子view时只显示了最后一个child,因为child是同一个实例子,与预期不符,该解决方法不起作用

    • 方法二 使用多个实例

    每次都创建一个新的child实例来添加到CustomLinearLayout中

    class CustomLinearLayout @JvmOverloads constructor(
    context: Context,
    attributeSet: AttributeSet,
    defaultStyle: Int? = 0
    ) : LinearLayout(context, attributeSet) {

    fun addTag(list: List<String>) {
        if (list.isEmpty()) return
        for (index in list.indices) {
            val tagView = createTagView()
            tagView.text = list[index]
            addView(tagView)
        }
    
    }
    
    private fun createTagView(): TextView {
        val contentView =
            LayoutInflater.from(context).inflate(R.layout.item_text, null) as LinearLayout
        return contentView.findViewById<TextView>(R.id.tv_text)
    }
    

    }

    • 运行效果图
      在这里插入图片描述

    运行结果和预期相符,正常显示多个子控件

    5、全部示例代码
    • AddViewDemoActivity
    class AddViewDemoActivity :AppCompatActivity(){
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_add_view_demo)
            custom_lin.addTag(listOf("Activity","Fragment","Viewpager"))
        }
    }
    
    • 布局文件 activity_add_view_demo
    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <com.petergee.myapplication.view.customView.CustomLinearLayout
            android:id="@+id/custom_lin"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
    
    • CustomLinearLayout
    class CustomLinearLayout @JvmOverloads constructor(
        context: Context,
        attributeSet: AttributeSet,
        defaultStyle: Int? = 0
    ) : LinearLayout(context, attributeSet) {
    
        fun addTag(list: List<String>) {
            if (list.isEmpty()) return
            for (index in list.indices) {
                val tagView = createTagView()
                tagView.text = list[index]
                addView(tagView)
            }
    
        }
    
        private fun createTagView(): TextView {
            val contentView =
                LayoutInflater.from(context).inflate(R.layout.item_text, null) as LinearLayout
            return contentView.findViewById<TextView>(R.id.tv_text)
        }
    }
    
    • 布局文件item_text
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
    
        <TextView xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="2dp"
            android:textColor="@android:color/black"
            android:textSize="16sp"
            android:id="@+id/tv_text">
    
        </TextView>
    </LinearLayout>
    

    二、源码查看

    1、addView(View child)
      /**
         * <p>Adds a child view. If no layout parameters are already set on the child, the
         * default parameters for this ViewGroup are set on the child.</p>
         *
         * <p><strong>Note:</strong> do not invoke this method from
         * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
         * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
         *
         * @param child the child view to add
         *
         * @see #generateDefaultLayoutParams()
         */
        public void addView(View child) {
            addView(child, -1);
        }
    
    2、addView(View child, int index)
        /**
         * Adds a child view. If no layout parameters are already set on the child, the
         * default parameters for this ViewGroup are set on the child.
         *
         * <p><strong>Note:</strong> do not invoke this method from
         * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
         * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
         *
         * @param child the child view to add
         * @param index the position at which to add the child
         *
         * @see #generateDefaultLayoutParams()
         */
        public void addView(View child, int index) {
            if (child == null) {
                throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
            }
            LayoutParams params = child.getLayoutParams();
            if (params == null) {
                params = generateDefaultLayoutParams();
                if (params == null) {
                    throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
                }
            }
            addView(child, index, params);
        }
    

    if (child == null) {
    throw new IllegalArgumentException(“Cannot add a null child view to a ViewGroup”);
    }

    • child为空,同样会报异常
    • layoutParams为空,同样会报异常
    3、 addView(child, index, params)
      /**
         * Adds a child view with the specified layout parameters.
         *
         * <p><strong>Note:</strong> do not invoke this method from
         * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
         * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
         *
         * @param child the child view to add
         * @param index the position at which to add the child or -1 to add last
         * @param params the layout parameters to set on the child
         */
        public void addView(View child, int index, LayoutParams params) {
            if (DBG) {
                System.out.println(this + " addView");
            }
    
            if (child == null) {
                throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
            }
    
            // addViewInner() will call child.requestLayout() when setting the new LayoutParams
            // therefore, we call requestLayout() on ourselves before, so that the child's request
            // will be blocked at our level
            requestLayout();
            invalidate(true);
            addViewInner(child, index, params, false);
        }
    

    关键方法addViewInner(child, index, params, false);

    4、addViewInner(child, index, params, false)
     /**
         * Prevents the specified child to be laid out during the next layout pass.
         *
         * @param child the child on which to perform the cleanup
         */
        protected void cleanupLayoutState(View child) {
            child.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT;
        }
    
        private void addViewInner(View child, int index, LayoutParams params,
                boolean preventRequestLayout) {
    
            if (mTransition != null) {
                // Don't prevent other add transitions from completing, but cancel remove
                // transitions to let them complete the process before we add to the container
                mTransition.cancel(LayoutTransition.DISAPPEARING);
            }
    
            if (child.getParent() != null) {
                throw new IllegalStateException("The specified child already has a parent. " +
                        "You must call removeView() on the child's parent first.");
            }
    
            if (mTransition != null) {
                mTransition.addChild(this, child);
            }
    
            if (!checkLayoutParams(params)) {
                params = generateLayoutParams(params);
            }
    
            if (preventRequestLayout) {
                child.mLayoutParams = params;
            } else {
                child.setLayoutParams(params);
            }
    
            if (index < 0) {
                index = mChildrenCount;
            }
    
            addInArray(child, index);
    
            // tell our children
            if (preventRequestLayout) {
                child.assignParent(this);
            } else {
                child.mParent = this;
            }
            if (child.hasUnhandledKeyListener()) {
                incrementChildUnhandledKeyListeners();
            }
    
            final boolean childHasFocus = child.hasFocus();
            if (childHasFocus) {
                requestChildFocus(child, child.findFocus());
            }
    
            AttachInfo ai = mAttachInfo;
            if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
                boolean lastKeepOn = ai.mKeepScreenOn;
                ai.mKeepScreenOn = false;
                child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
                if (ai.mKeepScreenOn) {
                    needGlobalAttributesUpdate(true);
                }
                ai.mKeepScreenOn = lastKeepOn;
            }
    
            if (child.isLayoutDirectionInherited()) {
                child.resetRtlProperties();
            }
    
            dispatchViewAdded(child);
    
            if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
                mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
            }
    
            if (child.hasTransientState()) {
                childHasTransientStateChanged(child, true);
            }
    
            if (child.getVisibility() != View.GONE) {
                notifySubtreeAccessibilityStateChangedIfNeeded();
            }
    
            if (mTransientIndices != null) {
                final int transientCount = mTransientIndices.size();
                for (int i = 0; i < transientCount; ++i) {
                    final int oldIndex = mTransientIndices.get(i);
                    if (index <= oldIndex) {
                        mTransientIndices.set(i, oldIndex + 1);
                    }
                }
            }
    
            if (mCurrentDragStartEvent != null && child.getVisibility() == VISIBLE) {
                notifyChildOfDragStart(child);
            }
    
            if (child.hasDefaultFocus()) {
                // When adding a child that contains default focus, either during inflation or while
                // manually assembling the hierarchy, update the ancestor default-focus chain.
                setDefaultFocus(child);
            }
    
            touchAccessibilityNodeProviderIfNeeded(child);
        }
    

    if (child.getParent() != null) {
    throw new IllegalStateException("The specified child already has a parent. " +
    “You must call removeView() on the child’s parent first.”);
    }

    至此,我们找到了报错的出处,是因为通过child.getParent() != null来进行判断的,如果child.getParent() != null,则报异常。

    三、小结

    通过一步步分析,问题就解决了,工作中遇到问题在所难免,遇到问题要善于分析问题出现的原因,时间允许的情况下尽量通过查看源码找到问题出现的点,并做好笔记,这样的话才能有所进步和提高。

    展开全文
  • AddView和layoutParams总结

    2020-08-10 17:08:15
    一、AddVIew addview方法可以用于动态地向布局内添加view addview重载了五个方法,如下所示 addView(View child) // child-被添加的View addView(View child, int index) // index-被添加的View的索引 addView(View ...

    一、AddVIew

    addview方法可以用于动态地向布局内添加view

    addview重载了五个方法,如下所示

    addView(View child)   // child-被添加的View
    addView(View child, int index)   // index-被添加的View的索引
    addView(View child, int width, int height)   // width,height被添加的View指定的宽高
    addView(View view, ViewGroup.LayoutParams params)   // params被添加的View指定的布局参数
    addView(View child, int index, LayoutParams params) 
    

    1.addView(View child)

    以linearlayout为例,直接往索引为index的地方添加view。如图所示

    2.addView(View child, int index)

    这样一来这个方法就很好理解了,直接往指定索引的地方添加view。如图所示

    3.addView(View child, int width, int height)

    和addView(View view) 类似,默认在index的位置放入一个view,唯一区别在于可以控制view的宽高。

    TextView newTextView=new TextView(this);
    LinearLayout linearLayout=findViewById(R.id.container);
    newTextView.setBackgroundColor(getResources().getColor(R.color.colorAccent));
    linearLayout.addView(newTextView,200,200);
    

    效果如图所示

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TGn6GvJ7-1597050244918)(/Users/bjhl/Library/Application Support/typora-user-images/image-20200721191629319.png)]

    4.addView(View view, ViewGroup.LayoutParams params) // params被添加的View指定的布局参数

    通过设置LayoutParams属性,可以设置控件的更多更多样的布局属性。

    关于LayoutParams会另外开一篇文章进行更加详细的记录。

    5.addView(View child, int index, LayoutParams params)

    和上面的方法基本类似,此外还能指定出现的index层级

    6.index在不同布局中的意义

    1 linearlayout 根据所设置的orientation的方向排列

    2 relativelayout 呈现堆叠的方式,index越大,越在别的控件的上方

    3 framlayout 同relativelayout

    4 ConstraintLayout还没测试,目前还不会约束布局的动态添加。以后会了再补这个坑

    二、layoutParams

    LayoutParams

    在动态添加view的过程中,需要指定被添加的view在parent中的一些布局参数,比如宽高,比如位置信息等。

    1.ViewGroup.LayoutParams

    作为最基本的layoutparams 只需要描述子view的宽高即可,它的值可以被设置为 Fill_parent(API8之后 改名),MATCH_PARENT或者具体的某个值。

    成员变量

    height 指定高度

    width 指定宽度

    layoutAnimationParameters 设置用于布局的动画。

    构造方法

    LayoutParams(Context c, AttributeSet attrs)
    根据上下文和传入的AttributeSet创建布局参数,它们被映射到xml文件中就变成了layout_width与layout_height。

    LayoutParams(int width, int height)
    根据宽高指定布局参数

    LayoutParams(ViewGroup.LayoutParams source)
    直接从源参数复制一份

    公有方法

    public void resolveLayoutDirection (int layoutDirection)

    根据布局的方向解析布局参数,关注布局方向改变的子类应该重写这个方法。现在是空实现。layoutDirection有两个值,分别是View#LAYOUT_DIRECTION_LTR 水平从左向右

    View#LAYOUT_DIRECTION_RTL 水平从右向左

    受保护的方法

    protected void setBaseAttributes (TypedArray a,
    int widthAttr,
    int heightAttr)

    根据被提供的属性,提取出属性参数

    2.MarginLayoutParams

    继承自ViewGroup.LayoutParams,除了以上提到的属性以外,还可以设置margin。

    映射到xml中,提供了以下的属性

    android:layout_margin 四周
    android:layout_marginBottom
    android:layout_marginEnd
    android:layout_marginHorizontal 水平
    android:layout_marginLeft
    android:layout_marginRight
    android:layout_marginStart
    android:layout_marginTop
    android:layout_marginVertical 竖直

    这里主要讲述一下marginStart与marginLeft的区别(marginEnd与marginRight同理)

    布局有两种布局方式,分别是上面所提到的

    View#LAYOUT_DIRECTION_LTR 水平从左向右

    View#LAYOUT_DIRECTION_RTL 水平从右向左

    当所采取的布局使用的是LAYOUT_DIRECTION_LTR时,marginStart和marginLeft没有区别,如果使用的是LAYOUT_DIRECTION_RTL,则marginLeft和marginStart正好相反。换句话说也就是,marginStart根据布局方向的方向,设置距离开始参照物的margin,而marginLeft则是一个固定方向,即用户所在的左手方向作为margin的参照物。

    成员变量

    public int bottomMargin

    public int leftMargin

    public int rightMargin

    public int topMargin

    即上下左右的margin

    构造方法

    相较于它的父类,多了一个可以根据MarginLayoutParams创建布局参数的一个构造方法

    公有方法

    int getLayoutDirection()

    返回布局方向,所有布局默认是0 左到右

    int getMarginEnd()

    int getMarginStart()

    boolean isMarginRelative()

    查看margin是否是相对的,如果设置了marginStart或者marginEnd 则返回true

    void resolveLayoutDirection(int layoutDirection)

    此方法会被 View.requestLayout()所调用。根据布局方向,左右margin可能会被覆盖

    void setLayoutDirection(int layoutDirection)
    设置layoutdirection

    void setMarginEnd(int end)
    设置相对的marginEnd

    void setMarginStart(int start)
    设置相对的marginStart

    void setMargins(int left, int top, int right, int bottom)

    设置四个margin

    直接继承的有 FrameLayout.LayoutParams,LinearLayout.LayoutParams, RelativeLayout.LayoutParams(其实还有别的,但本文主要讲述这三个布局下的布局参数)

    3.LinearLayout.LayoutParams

    成员变量

    public int gravity gravity指定了一个控件如何放在父布局中。

    对应的xml中android:layout_gravity,下方是它可以设置的值。

    ConstantValueDescription
    bottom50将控件放在父布局的底部,不改变其大小
    center11将控件放在父布局水平与竖直方向的中心,不改变大小
    center_horizontal1将控件放在父布局水平方向的中心,不改变大小
    center_vertical10将控件放在父布局竖直方向的中心,不改变大小
    clip_horizontal8可以设置为将子对象的左边缘和/或右边缘剪裁到其容器边界的附加选项。剪辑将基于水平重力:左重力将剪辑右边缘,右重力将剪辑左边缘,两者都不会剪辑两条边。(不明白作用?)
    clip_vertical80同上,竖直方向。
    end800005将控件放在父布的尾部,不改变大小
    fill77填充父布局
    fill_horizontal7水平方向填充父布局
    fill_vertical70竖直方向填充父布局
    left3将控件放在父布局的左边,不改变大小
    right5将控件放在父布局的右边,不改变大小
    start800003将控件放在父布局的开始位置,不改变大小
    top30将控件放在父布局的上边,不改变大小

    public float weight

    描述了父布局余下空间应该被分配多少给该view。如果不想获取额外空间设置为0,否则和所有大于0的子view按照权重对剩余空间进行分配。

    构造方法

    和MarginLayoutParams 相较多了一个可以根据LinearLayout.Params创建布局参数的构造方法

    公有方法

    String debug(String output)

    文档中没有对方法进行解释,查看源码可以看到

    @Override
    public String debug(String output) {
        return output + "LinearLayout.LayoutParams={width=" + sizeToString(width) +
                ", height=" + sizeToString(height) + " weight=" + weight +  "}";
    }
    

    是返回了一个拼接的字符串,包含了输入值,布局参数的宽高一级weight

    4.FrameLayout.LayoutParams

    成员变量

    public int gravity

    对应android:layout_gravity,默认值是常量UNSPECIFIED_GRAVITY,相当于Gravity.TOP | Gravity.START

    构造方法

    和MarginLayoutParams 相较多了一个可以根据FrameLayout.LayoutParams 创建布局参数的构造方法

    5.RelativeLayout.LayoutParams

    xml属性

    XML attributes
    android:layout_above将view放在给定id的view上方
    android:layout_alignBaseline将view的基线放在给定id的view 的基线上
    android:layout_alignBottom将view的下边缘与给定id的view 的下边缘对齐
    android:layout_alignEnd同上,只是对齐的是尾边缘
    android:layout_alignLeft同上,只是对齐的是左边缘
    android:layout_alignParentBottom如果为true,将view的底边缘和parent底边缘对齐
    android:layout_alignParentEnd同上,只是对齐的是parent的尾边缘
    android:layout_alignParentLeft同上,只是对齐的是parent的左边缘
    android:layout_alignParentRight同上,只是对齐的是parent的右边缘
    android:layout_alignParentStart同上,只是对齐的是parent的开始边缘
    android:layout_alignParentTop同上,只是对齐的是parent的顶部边缘
    android:layout_alignRight将view的右边缘与给定id的view 的右边缘对齐
    android:layout_alignStart同上,只是对齐的是开始边缘
    android:layout_alignTop同上,只是对齐的是顶部边缘
    android:layout_alignWithParentIfMissing如果为true,当找不到layout_toLeftOf, layout_toRightOf等属性的锚点时,将会把parent作为锚点
    android:layout_below将view放在给定id的view下方
    android:layout_centerHorizontal如果为true,将位于parent的水平居中位置
    android:layout_centerInParent如果为true,将位于parent的水平、竖直居中位置
    android:layout_centerVertical如果为true,将位于parent的竖直居中位置
    android:layout_toEndOf将view放在给定id的view尾部
    android:layout_toLeftOf将view放在给定id的view左边
    android:layout_toRightOf将view放在给定id的view右边
    android:layout_toStartOf将view放在给定id的view开始位置

    成员变量

    public boolean alignWithParent

    如果为true 当锚点view不存在或者处于gone的状态时,将使用parent作为锚点

    构造方法

    和MarginLayoutParams 相较多了一个可以根据RelativeLayout.LayoutParams 创建布局参数的构造方法

    公有方法

    void addRule(int verb, int subject)

    添加一个规则,必须和兄弟节点相关联,来解释relativelayout布局

    int verb 一个布局的动词,官网文档以RelativeLayout.ALIGN_RIGHT为例,点开进去看到还有很多类似的比如ALIGN_TOP等,个人理解是添加了一种规则。

    int subject 锚点view的id或者是一个布尔值(表示RelativeLayout.TRUE 来表示true 或者0表示false,当为true时代表启用这个verb,false时不启用这个verb,适用于不需要锚点的verb)

    void addRule(int verb)
    添加一个规则,必须不能与兄弟节点相关联或者属性值为布尔值,来解释relativelayout布局

    String debug(String output)

    作用同之前讲过的

    int getRule(int verb)
    获取规则

    int[] getRules()
    获取一组规则

    void removeRule(int verb)

    移除一个规则

    void resolveLayoutDirection(int layoutDirection)
    作用同之前讲过的

    展开全文
  • Android 图形架构 之五——深入分析addView所发生的的一切 Android 图形架构 之六——深入分析draw()是如何工作的 Android 图形架构 之七——Choreographer 源码分析 Android图形架构 之八——硬件VSync、VSync-app...

    前言

    前几篇文章,分析了在SurfaceFlinger 进程,WMS进程 中,图形架构的流程和关键类的介绍。现在我们来分析一下,app进程中是如何与这些进程进行交互,以及何时交互。

    Android 图形架构 之一 ——概述
    Android 图形架构 之二—— SurfaceFlinger 启动和连接
    Android 图形架构 之三—— 创建Layer、Surface、SurfaceControl
    Android 图形架构 之四——图形缓冲区的申请和消费流程及核心类
    Android 图形架构 之五——深入分析addView所发生的的一切
    Android 图形架构 之六——深入分析draw()是如何工作的
    Android 图形架构 之七——Choreographer 源码分析
    Android图形架构 之八——硬件VSync、VSync-app、Vsync-sf

    UI显示过程的三个进程

    Android显示的整个过程由App进程、System_server进程、SurfaceFlinger进程一起配合完成。

    1. App进程: App需要将自己的内容显示在屏幕上,所以需要负责发起Surface创建的请求。同时触发对控件的测量、布局、绘制以及输入事件的派发处理,这些主要在ViewRootImpl中触发;

    2. System_server进程: 主要是WindowManagerService,负责接收App请求,同时和SurfaceFlinger建立连接,向SurfaceFlinger发起具体请求创建Surface,并且创建Surace的辅助管理类SurfaceControl(和window一一对应)(AMS作用是统一调度所有App的Activity);

    3. SurfaceFlinger: 为App创建具体的Surface,在SurfaceFLinger对应成Layer,然后负责管理、合成所有图层,最终显示。

    在这里插入图片描述

    前几篇文章分析了第二点,第三点,这篇文章主要分析第一点

    Activity、Window、PhoneWindow、DecorView、View的对应关系

    在这里插入图片描述

    1. 一个Activity对应创建一个Surface,每个Surface 对应SurfaceFlinger中的一个Layer

    2. Window:每一个Activity都包含一个Window对象(抽象类,提供了绘制窗口的一组通用API),通常由PhoneWindow实现。是Activity和整个View系统交互的接口。

    3. PhoneWindow:继承于Window,是Window类的具体实现。该类内部包含了一个DecorView对象,该DecorView对象是所有应用窗口(Activity界面)的根View。
      简而言之,PhoneWindow类是把一个FrameLayout类,即DecorView对象进行一定的包装,将他作为应用窗口的根View,并提供一组通用的窗口操作接口。

    4. DecorView:PhoneWindow构造函数中定义,继承FrameLayout类,是所有应用窗口的根View。

    5. WindowManager 继承ViewManager ,它们都是操作UI的接口

    6. WindowManagerImpl 实现了WindowManager,并持有WindowManagerGlobal 的对象

    7. WindowManagerGlobal 是单例模式,每个进程只有一个。持有ViewRootImpl 的对象

    8. ViewRootImpl 在整个Android的GUI系统中占据非常重要的位置,如果把Activity和View 看作 ‘MVC’ 中的V, 把各种后台服务看作Modal,ViewRootImpl 则是’MVC’ 中的’C’ - Controller. Controller在MVC架构里承担着承上启下的作用,一般来说它的逻辑最为复杂。从下图可以看到,ViewRootImpl 与 用户输入系统(接收用户按键,触摸屏输入), 窗口系统(复杂窗口的布局,刷新,动画),显示合成系统(包括定时器Choreograph, SurfaceFlinger), 乃至Audio系统(音效输出)等均有密切的关联。

    流程分析

    我们从 activity的源码 handleResumeActivity 开始分析,熟悉activity启动流程的应该知道,handleResumeActivity 中会执行onResume,添加显示界面。下面这张时序图,可以说概括了,本系列文章的主干流程,可以细细品味 (图片来源
    在这里插入图片描述

    开始源码分析

    frameworks/base/core/java/android/app/ActivityThread.java

    代码一:
        final void handleResumeActivity(IBinder token,
                boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
            ActivityClientRecord r = mActivities.get(token);
    
           ....
            // TODO Push resumeArgs into the activity for consideration
            r = performResumeActivity(token, clearHide, reason);
    
            if (r != null) {
                final Activity a = r.activity;
    
               ....
                if (r.window == null && !a.mFinished && willBeVisible) {
                    r.window = r.activity.getWindow();
                    View decor = r.window.getDecorView();
                    decor.setVisibility(View.INVISIBLE);
                    //获取WindowManagerImpl,因为它implements WindowManager,后者implements ViewManager ,此处是重点,接下来会分析
                    ViewManager wm = a.getWindowManager();
                    WindowManager.LayoutParams l = r.window.getAttributes();
                    a.mDecor = decor;
                    l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                    l.softInputMode |= forwardBit;
                    if (r.mPreserveWindow) {
                        a.mWindowAdded = true;
                        r.mPreserveWindow = false;
                        // Normally the ViewRoot sets up callbacks with the Activity
                        // in addView->ViewRootImpl#setView. If we are instead reusing
                        // the decor view we have to notify the view root that the
                        // callbacks may have changed.
                        ViewRootImpl impl = decor.getViewRootImpl();
                    }
                    if (a.mVisibleFromClient) {
                        if (!a.mWindowAdded) {
                            a.mWindowAdded = true;
                            //重要,添加根布局 
                            wm.addView(decor, l);
                        } else {
                            ....
                        }
                    }
    
                    ....
                    if (r.activity.mVisibleFromClient) {
                        //显示根布局
                        r.activity.makeVisible();
                    }
                }
    
                 ....
            }
        }
    

    接着分析上面ViewManager wm = a.getWindowManager();
    代码路径:frameworks/base/core/java/android/app/Activity.java

    代码二:
        final void attach(Context context, ActivityThread aThread,
                Instrumentation instr, IBinder token, int ident,
                Application application, Intent intent, ActivityInfo info,
                CharSequence title, Activity parent, String id,
                NonConfigurationInstances lastNonConfigurationInstances,
                Configuration config, String referrer, IVoiceInteractor voiceInteractor,
                Window window, ActivityConfigCallback activityConfigCallback) {
            attachBaseContext(context);
    
            mFragments.attachHost(null /*parent*/);
            //activity 关联的Window 是PhoneWindow
            mWindow = new PhoneWindow(this, window, activityConfigCallback);
             。。。省略代码,一些设置回调和赋值的语句,。。。
    		//该函数为mWindowManager 变量赋值,获取到
            mWindow.setWindowManager(
                    (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                    mToken, mComponent.flattenToString(),
                    (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
            if (mParent != null) {
                mWindow.setContainer(mParent.getWindow());
            }
            mWindowManager = mWindow.getWindowManager();
            mCurrentConfig = config;
    
            mWindow.setColorMode(info.colorMode);
        }
    

    (WindowManager)context.getSystemService(Context.WINDOW_SERVICE) 获取WINDOW_SERVICE服务,它是在

    代码路径:frameworks/base/core/java/android/view/Window.java

    代码三:
        public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
                boolean hardwareAccelerated) {
            mAppToken = appToken;
            mAppName = appName;
            mHardwareAccelerated = hardwareAccelerated
                    || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
            if (wm == null) {
                wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
            }
            //创建本地的 WindowManagerImpl,它关联了当前的Window(PhoneWindow),之后所有的操作 都是交给WindowManagerImpl ,这些操作定义在接口ViewManager
            mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
        }
    

    到此,我们回到代码一,看看如何添加根布局的 wm.addView(decor, l);

    代码路径:frameworks/base/core/java/android/view/WindowManagerImpl.java

    代码四:
        @Override
        public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
            applyDefaultToken(params);
            //这里的mGlobal 全局变量,一个进程中只有一个实例
            mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
        }
    

    代码路径:frameworks/base/core/java/android/view/WindowManagerGlobal.java

    代码五:
        public void addView(View view, ViewGroup.LayoutParams params,
                Display display, Window parentWindow) {
                 ....
                 //因为本类是单例,所以这里创建的ViewRootImpl ,一个进程中也只有一个
                 //代码六 分析
                 root = new ViewRootImpl(view.getContext(), display);
    
                view.setLayoutParams(wparams);
    			
                mViews.add(view);
                mRoots.add(root);
                mParams.add(wparams);
    
                // do this last because it fires off messages to start doing things
                try {
                    // 通过ViewRootImpl 把View 添加到Surface
                    // 代码九 分析
                    root.setView(view, wparams, panelParentView);
                } catch (RuntimeException e) {
                    // BadTokenException or InvalidDisplayException, clean up.
                    if (index >= 0) {
                        removeViewLocked(index, true);
                    }
                    throw e;
                }
            }
        }
    
    

    接着 看看ViewRootImpl 的创建,因为这里涉及到Session,它负责创建Surface、SurfaceSession(java端的SurfaceComposerClient)

    代码路径:frameworks/base/core/java/android/view/ViewRootImpl.java

    代码六:
        public ViewRootImpl(Context context, Display display) {
            mContext = context;
            // 还是由WindowManagerGlobal ,来创建Session
            //ViewRootImpl 持有Session 对象后,后续就可以控制UI的绘制了(measure,layout,draw等),涉及到Surface 等操作,就交给mWindowSession 来操作了
            mWindowSession = WindowManagerGlobal.getWindowSession();
            ....
        }
    
    代码七:
        public static IWindowSession getWindowSession() {
            synchronized (WindowManagerGlobal.class) {
                if (sWindowSession == null) {
                    try {
                        InputMethodManager imm = InputMethodManager.getInstance();
                        //获取WindowManagerService ,IWindowManager 是aidl 
                        IWindowManager windowManager = getWindowManagerService();
                        //创建Session
                        sWindowSession = windowManager.openSession(
                                new IWindowSessionCallback.Stub() {
                                    @Override
                                    public void onAnimatorScaleChanged(float scale) {
                                        ValueAnimator.setDurationScale(scale);
                                    }
                                },
                                imm.getClient(), imm.getInputContext());
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
                return sWindowSession;
            }
        }
    
        public static IWindowManager getWindowManagerService() {
            synchronized (WindowManagerGlobal.class) {
                if (sWindowManagerService == null) {
                    // 获取WMS服务,并转换为IWindowManager.Stub 类型
                    sWindowManagerService = IWindowManager.Stub.asInterface(
                            ServiceManager.getService("window"));
                    ....
                }
                return sWindowManagerService;
            }
        }
    
    

    这里的openSession 是跨进程通信。Session、WMS 是在另外的一个进程

    代码八:
        public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
                IInputContext inputContext) {
            ....
            //创建Session,所以每一个View 对应一个
            Session session = new Session(this, callback, client, inputContext);
            return session;
        }
    

    上面创建了Session,这里我们回到代码五 root.setView(view, wparams, panelParentView);

    这里的root ,是ViewRootImpl对象,调用了setView函数

    代码九:
       public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
            synchronized (this) {
                   ....
                    // Schedule the first layout -before- adding to the window
                    // manager, to make sure we do the relayout before receiving
                    // any other events from the system.
                    //这里最终就是调用了三大UI 函数 onMesure, onLayout,onDraw
                    requestLayout();
                    try {
                      ....
                       //调用Session 为添加UI 做准备,
                       //这里主要是创建WindowState(每个View 都对应一个)、创建SurfaceSession,下面会分析
                        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                                getHostVisibility(), mDisplay.getDisplayId(),
                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                                mAttachInfo.mOutsets, mInputChannel);
                    }
                     ....
            }
    }
    

    这里先分析mWindowSession.addToDisplay ,再分析requestLayout() ,因为它主要执行的就是把View输出到屏幕,在下一次屏幕刷新时,也会有相应的逻辑,放在后面分析,流程看上去更符合逻辑。

    这里为什么requestLayout() 的执行要放在mWindowSession.addToDisplay 之前,我没太明白,知道的大佬,评论里指点一下

    代码路径:frameworks/base/services/core/java/com/android/server/wm/Session.java

    代码十:
        public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
                int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
                Rect outOutsets, InputChannel outInputChannel) {
                 //调用WMS 的addWindow
            return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                    outContentInsets, outStableInsets, outOutsets, outInputChannel);
        }
    

    代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

    代码十一:
     public int addWindow(Session session, IWindow client, int seq,
                WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
                Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
                InputChannel outInputChannel) {
            
            synchronized(mWindowMap) {
               。。。。。
                //创建WindowState 对象,每一个View 对应一个
                final WindowState win = new WindowState(this, session, client, token, parentWindow,
                        appOp[0], seq, attrs, viewVisibility, session.mUid,
                        session.mCanAddInternalSystemWindow);
                 。。。。。       
                //下面看看这个函数
                win.attach();
               。。。。
            }
    
            。。。。
    
            return res;
        }
    

    代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowState.java

    代码十二:
        void attach() {
            if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
            //调用到Session ,创建SurfaceSession
            mSession.windowAddedLocked(mAttrs.packageName);
        }
    

    代码路径:frameworks/base/services/core/java/com/android/server/wm/Session.java

    代码十三:
        void windowAddedLocked(String packageName) {
            mPackageName = packageName;
            mRelayoutTag = "relayoutWindow: " + mPackageName;
            if (mSurfaceSession == null) {
    			//创建mSurfaceSession
                mSurfaceSession = new SurfaceSession();
                //把当前的Session对象,与WMS 关联起来
                mService.mSessions.add(this);
                ....
            }
            mNumWindow++;
        }
    

    看到这个SurfaceSession 的创建,我们应该很熟悉了,里面的流程 在Android 图形架构 之三—— 创建Layer、Surface、SurfaceControl 文章中分析过了

    到此,我们已经有了可以控制Surface 的SurfaceSession (也就是SurfaceComposerClient),也有了View,下面就该创建Surface,并开始绘制了。

    我们现在回到代码九,看看requestLayout(),它

    代码十四:
        @Override
        public void requestLayout() {
            if (!mHandlingLayoutInLayoutRequest) {
               //检查是否在主线程,
                checkThread();
                mLayoutRequested = true;
                //执行UI遍历,它就是向Choreographer 发送CALLBACK_TRAVERSAL事件消息
                scheduleTraversals();
            }
        }
       
        void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                //发送同步消息屏障,会优先处理异步消息
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                 //该函数发送了事件后,就等待mChoreographer ,回调mTraversalRunnable,最终执行doTraversal
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }
    

    关于同步消息屏障的,可阅读深入源码分析Handler 消息机制 、Looper、MessageQueue 消息同步屏障、IdleHandler、Message 复用

    Android 图形架构 之六——Choreographer 源码分析文章中,我们可以知道,屏幕收到刷新的信号,会调用到Choreographer,由该类去处理分发事件,屏幕绘制会调用到 ViewRootImpl#doTraversal()

    下面我们就来分析ViewRootImpl#doTraversal函数:

    代码十五:
        void doTraversal() {
            if (mTraversalScheduled) {
                mTraversalScheduled = false;
                //取消同步消息屏障
                mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); 
                .....
                performTraversals();
                .....
            }
        }
    
    代码十六:
       private void performTraversals() {
           
           relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
           //下面这三大函数,大家应该很熟悉了,就不分析了
           .....
           performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
           .....
           performLayout(lp, mWidth, mHeight);
           .....
           performDraw();
        }
    
    代码十七:
        private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
                boolean insetsPending) throws RemoteException {
    
            .....
            //调用Session 的relayout,
            //最后一个参数是mSurface,该成员变量的初始值 final Surface mSurface = new Surface(); 
            //但是此时的mSurface 还没有与底层的surface 关联起来,后面会通过copyFrom 进行关联
            int relayoutResult = mWindowSession.relayout(
                    mWindow, mSeq, params,
                    (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                    (int) (mView.getMeasuredHeight() * appScale + 0.5f),
                    viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                    mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                    mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame,
                    mPendingMergedConfiguration, mSurface);
    
             .....
            return relayoutResult;
        }
    

    继续 进入Session#relayout 进行

    代码十八:
        public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
                int requestedWidth, int requestedHeight, int viewFlags,
                int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
                Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
                MergedConfiguration mergedConfiguration, Surface outSurface) {
            .....
            继续进入WMS 看看relayoutWindow
            int res = mService.relayoutWindow(this, window, seq, attrs,
                    requestedWidth, requestedHeight, viewFlags, flags,
                    outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
                    outStableInsets, outsets, outBackdropFrame, mergedConfiguration, outSurface);
            .....
            return res;
        }
    

    下面进入到WMS 中relayoutWindow 函数

    代码十九:
        public int relayoutWindow(Session session, IWindow client, int seq,
                WindowManager.LayoutParams attrs, int requestedWidth,
                int requestedHeight, int viewVisibility, int flags,
                Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
                Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
                MergedConfiguration mergedConfiguration, Surface outSurface) {
             ...
             //这里创建SurfaceControl,它会触发调用Native层的
             result = createSurfaceControl(outSurface, result, win, winAnimator);
             ...
       }
    

    这里在java 层创建SurfaceControl,继而调用copyFrom ,把java层的surface与Native层的surface关联起来,继续跟进看看

    代码二十:
        private int createSurfaceControl(Surface outSurface, int result, WindowState win,
                WindowStateAnimator winAnimator) {
    
            WindowSurfaceController surfaceController;
            try {
                //这里继续创建SurfaceControl,一直调用到Native层
                //代码二十一 分析此函数
                surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
            } 
            
            if (surfaceController != null) {
               //有了surfaceController,就去给surface 赋值
                surfaceController.getSurface(outSurface);
            } else {
                outSurface.release();
            }
    
            return result;
        }
    

    上面有两处重要的函数实现,这里先分析winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);,它进入到了 WindowStateAnimator.java类中,路径frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java

    代码二十一:
        WindowSurfaceController createSurfaceLocked(int windowType, int ownerUid) {
            final WindowState w = mWin;
               // 通过该类来判断创建哪一种SurfaceControl
                mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
                        attrs.getTitle().toString(),
                        width, height, format, flags, this, windowType, ownerUid);
                mSurfaceFormat = format;
            ....
            return mSurfaceController;
        }
    

    继续进入到WindowSurfaceController,看它的构造函数

    代码二十二:
        public WindowSurfaceController(SurfaceSession s, String name, int w, int h, int format,
                int flags, WindowStateAnimator animator, int windowType, int ownerUid) {
    
            if (DEBUG_SURFACE_TRACE) {
                mSurfaceControl = new SurfaceTrace(
                        s, name, w, h, format, flags, windowType, ownerUid);
            } else {
                //一般会创建这个SurfaceControl
                mSurfaceControl = new SurfaceControl(
                        s, name, w, h, format, flags, windowType, ownerUid);
            }
    
            if (mService.mRoot.mSurfaceTraceEnabled) {
                mSurfaceControl = new RemoteSurfaceTrace(
                        mService.mRoot.mSurfaceTraceFd.getFileDescriptor(), mSurfaceControl, win);
            }
        }
    

    最后来看看SurfaceControl,

    代码二十三:
        public SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
                SurfaceControl parent, int windowType, int ownerUid)
                        throws OutOfResourcesException {
            mName = name;
            //调用Native 函数,调用SurfaceComposerClient的createSurface,
            // 会调用到createSurfaceChecked,在Native层,创建Layer和Surface,
            mNativeObject = nativeCreate(session, name, w, h, format, flags,
                parent != null ? parent.mNativeObject : 0, windowType, ownerUid);
            if (mNativeObject == 0) {
                throw new OutOfResourcesException(
                        "Couldn't allocate SurfaceControl native object");
            }
    
            mCloseGuard.open("release");
        }
    

    这里的逻辑就与 文章 Android 图形架构 之三—— 创建Layer、Surface、SurfaceControl 代码十四 接上了,就不再展开

    至此,SurfaceControl 创建完成,Native层的layer和surfaceControl 也创建完了,接着回到代码二十,看看函数surfaceController.getSurface(outSurface);

    代码二十四:
        void getSurface(Surface outSurface) {
            outSurface.copyFrom(mSurfaceControl);
        }
    
        public void copyFrom(SurfaceControl other) {
             //调用到Native层,创建Surface(在Native 中,也是通过surfaceControl 来创建的,所以这里把参数surfaceControlPtr 传入进去)
            long newNativeObject = nativeGetFromSurfaceControl(surfaceControlPtr);
    
            synchronized (mLock) {
                if (mNativeObject != 0) {
                    nativeRelease(mNativeObject);
                }
                //获取到Native层的surface ,保存到java层surface 的mNativeObject 变量
                setNativeObjectLocked(newNativeObject);
            }
        }
    

    这里的逻辑就与 文章 Android 图形架构 之三—— 创建Layer、Surface、SurfaceControl 代码十五 接上了,就不再展开

    至此,surface 相关的就已经创建初始化完毕,我们回到代码十六,最终调用了performDraw();,通过draw函数来绘制界面,具体原理,我们放在下一篇文章分析

    结语

    在framework层,关于view 的主要流程,解决分析完了,下一篇文章,我们深入分析一下draw 是如何把数据写入surface的,它是如何与生产消费数据交互的

    请点赞、收藏,感谢大家的支持,欢迎评论区提问、吐槽

    参考:

    Android 图形显示框架
    Android SurfaceFlinger 学习之路(八)----Surface管理图形缓冲区

    展开全文
  • 使用LinearLayout.addView来添加子view,比如上面的黄色,白色,蓝色,绿色几个子view都是通过LinearLayout的addView方法添加进来的,因为牵扯到下拉刷新,所以整体的代码框架就是如下: LinearLayout listView = ...
  • 但是如果项目的设计架构本身就是 MVC 这样我们还可以用其他的一些方法来分解我们 Activity 和 Fragment 中的代码冗余,对,就像标题中说的,利用 ViewGroup 中的 addView 方法来实现 接下来我们就来演示一下具体是...
  • 先列出调用流程,让读者能有个大概的认知: windowmanager.addview() WindowManagerImpl.addview() WindowManagerGlobal.addview() ViewRootImpl.setView() WindowManagerImpl.addview() WindowManagerImpl.addView...
  • 不能使用myImage.layout()方法,来改变位置。...而调用addView往布局添加新的控件时,是根据layoutparam来重新布局控件位置的。 这里需要用另一种方法:先获取控件的layoutparam,改变其中相关的值后,再设置回去。
  • 浮窗中addView()不显示 分析思路

    千次阅读 2018-08-23 20:07:35
    在我们平时写代码的时候,经常要用到windowmanager这个类,一般是通过addview的方式,把一个view添加到一个窗口中,在系统中,比如toast,悬浮框,状态栏,通知栏,锁屏界面他们都不是平常的activity中的window,因此...
  • android addView的使用

    万次阅读 多人点赞 2018-03-22 08:26:58
    addView动态的添加控件到界面上。 这个其实很简单,之前我使用时发生了一些问题,所以在网上查找了资料,最终解决了问题。这里记录一下。 一、说明 添加视图文件的时候有两种方式:1、通过在xml文件定义...
  • addview

    2013-05-20 16:31:00
     2、在程序中,new出这个视图,找到framelayout或LinearLayout,直接用framelayout.addView(我们自己new的视图)。  例:mainView = new LinearLayout(mCtx);  mainView.setLayoutParams(new LayoutParams...
  • 报错Attempt to invoke virtual method 'void android.widget.RadioGroup.addView(android.view.View)' on a null object reference;我的定义radiobutton了啊 ``` public void initViewTwo(){ RadioGroup ...
  • 题记最近项目中遇到一个问题,一个自定义view使用addview加入父布局后,突然不居中了,原因是对父布局增加了一层嵌套;分析问题前我们需要理解LayoutParams概念。LayoutParamsAndroid SDK中的介绍如下:LayoutParams...
  • windowmanager addview显示view

    千次阅读 2020-01-11 10:47:04
    WindowManager的方法很简单,有三个方法,AddView(),removeView(),updateViewLayout(); AddView();//添加View removeView();//移除VIew updateViewLayout()//更新View View view = LayoutInflater.from(this).inf....
  • Android addView()方法简述(一)

    万次阅读 2019-01-21 15:26:12
    大家都知道addView()一般用来动态添加View,LinearLayout、RelativeLayout添加子控件都可以用addView()。去看Android官方文档:ViewGroup里有addView的相关介绍。大概如下: 方法 解释 addView (View child,...
  • 子view就是一个桌子,通过调用父布局的addview加入到父布局中 遇到这个问题的时候去查过源码,源码中addview(View view)最后会调用 ``` public void addView(View child, int index, LayoutParams params) ...
  • WindowManagerGlobal WindowManager服务其实是...这个变量用于存储windowManager.addView(view) 里面的view。 如果mDyingViews.add重复add进去的话就会抛异常,异常信息:view 已经存在了。 Global 全局的 ...
  • Android addView()方法分为以下几个部分 addView()方法简述(一) 本章简介 本章将针对上文提到的addView()的方法进行实例操作,并就可能出现的问题以面试题的方式进行解答。 1 .addView (View child, ViewGroup....
  • (ps:addView是ViewGroup中特有的方法,而单一的View是不存在该方法的) – addView的使用 addView(View child) // child-被添加的View addView(View child, int index) // index-被添加的View的索引 addView(View ...
  • Android ViewGroup中addView方法使用

    千次阅读 2018-05-21 19:41:43
    Android 中ViewGroup 中的添加子控件的方法addVIew方法 //child:要添加的view public void addView(View child) ; //child:要添加的View index:view在父布局中位于第几个 public void addView(View child, int ...
  • WindowManage addView 监听按键消息 直接使用 setOnKeyListener 监听不到 OnKeyListener 事件的。 view . setOnKeyListener ( new View . OnKeyListener ( ) { @Override public boolean onKey ...
  • WindowManager addView

    千次阅读 2017-01-13 22:56:46
    public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ... root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); ...
  • 1.一般添加布局的时候通常用到view.addView(chidView); View childView = View.inflate(this, getLayoutId(), null); mBaseView.addView(childView); 这个是子View的布局 我们想要的结果是撑满屏幕 <...
  • Android 获取View的位置参数:x,y和相对父容器的偏移量以及中心点坐标Android 利用代码动态添加Viw关于代码中的屏幕适配最后,奉上工具类感谢观看 你好! Android 利用代码动态添加Viw ...addView(...
  • addView 有多个构造方法,其中 addView(View child, int index, LayoutParams params)第二个参数可以控制需要添加到的层级。 其他没有index参数的方法,其实还是会执行这个带有index参数的方法,只不过默认为-1。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 69,709
精华内容 27,883
关键字:

addview