美文网首页
Activity加载布局相关知识点

Activity加载布局相关知识点

作者: 因为我的心 | 来源:发表于2023-04-14 17:25 被阅读0次

    一、加载tabBar相关

    1、Activity和AppCompatActivity去掉tabBar的方法

    //Activity
    class MainActivity : Activity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            //Activity设置无title必须使用这个
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            setContentView(R.layout.activity_main)
        }
    }
    
    //AppCompatActivity
    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            //AppCompatActivity设置无title必须使用这个
            supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
            setContentView(R.layout.activity_main)
        }
    }
    

    2、为什么requestWindowFeature()要在setContentView()之前调用?

    requestWindowFeature 实际调用的是 PhoneWindow.requestFeature,
    在这个方法里面会判断如果变量 mContentParentExplicitlySet 为true则报错,
    而这个变量会在 PhoneWindow.setContentView 调用的时候设置为true。

    //加载布局的时候置为true
     private boolean mContentParentExplicitlySet = false;
      @Override
        public boolean requestFeature(int featureId) {
            if (mContentParentExplicitlySet) {
    //可以看到这个注释
                throw new AndroidRuntimeException("requestFeature() must be called before adding content");
            }
            final int features = getFeatures();
            final int newFeatures = features | (1 << featureId);
            if ((newFeatures & (1 << FEATURE_CUSTOM_TITLE)) != 0 &&
                    (newFeatures & ~CUSTOM_TITLE_COMPATIBLE_FEATURES) != 0) {
                // Another feature is enabled and the user is trying to enable the custom title feature
                // or custom title feature is enabled and the user is trying to enable another feature
                throw new AndroidRuntimeException(
                        "You cannot combine custom titles with other title features");
            }
            if ((features & (1 << FEATURE_NO_TITLE)) != 0 && featureId == FEATURE_ACTION_BAR) {
                return false; // Ignore. No title dominates.
            }
            if ((features & (1 << FEATURE_ACTION_BAR)) != 0 && featureId == FEATURE_NO_TITLE) {
                // Remove the action bar feature if we have no title. No title dominates.
                removeFeature(FEATURE_ACTION_BAR);
            }
    
            if (featureId == FEATURE_INDETERMINATE_PROGRESS &&
                    getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
                throw new AndroidRuntimeException("You cannot use indeterminate progress on a watch.");
            }
            return super.requestFeature(featureId);
        }
    
      @Override
        public void setContentView(int layoutResID) {
            // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
            // decor, when theme attributes and the like are crystalized. Do not check the feature
            // before this happens.
            if (mContentParent == null) {
                installDecor();
            } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                mContentParent.removeAllViews();
            }
    
            if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                        getContext());
                transitionTo(newScene);
            } else {
                mLayoutInflater.inflate(layoutResID, mContentParent);
            }
            mContentParent.requestApplyInsets();
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
           //加载布局之后就为true了
            mContentParentExplicitlySet = true;
        }
    

    3、AppCompatActivity类中为什么 调用requestWindowFeature(Window.FEATURE_NO_TITLE);设置无效?

    需要用 supportRequestWindowFeature(Window.FEATURE_NO_TITLE),因为继承的是AppCompatActivity,这个类里面会覆盖设置。

    //可以看到requestWindowFeature方法被覆盖了
     public boolean supportRequestWindowFeature(int featureId) {
            return getDelegate().requestWindowFeature(featureId);
        }
    

    二、LayoutInflate几个参数的作用?

    调用LayoutInflater.from(this).inflate()方法最终都会走三参的方法
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)

    这里先简单说明下三个参数:

    • resource:需要加载的布局文件的ID,即要将哪个布局文件转换成View对象。
    • root:生成的View对象的父View,如果传入null,则表示不需要将生成的View添加到任何父View中。
    • attachToRoot:是否将生成的View添加到root中,如果为true,则将生成的View添加到root中,如果为false,则不添加。
    public class InflateActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_inflate);
            LinearLayout ll = findViewById(R.id.ll);
    
    
            // 方式一:将布局添加成功
           // View view = LayoutInflater.from(this).inflate(R.layout.inflate_layout, ll, true);
    
            // 方式二:报错,一个View只能有一个父亲(The specified child already has a parent.)
    //        View view = LayoutInflater.from(this).inflate(R.layout.inflate_layout, ll, true);
    //        ll.addView(view);//view已经有一个父类了,不能再addView了
    
            // 方式三:布局成功,第三个参数为false
            // 目的:想要 inflate_layout 的根节点的属性(宽高)有效,又不想让其处于某一个容器中
    //        View view = LayoutInflater.from(this).inflate(R.layout.inflate_layout, ll, false);
    //        ll.addView(view);
    
            // 方式四:root = null,这个时候不管第三个参数是什么,显示效果一样
            // inflate_layout 的根节点的属性(宽高)设置无效,只是包裹子View,
            // 但是子View(Button)有效,因为Button是出于容器下的
            View view = LayoutInflater.from(this).inflate(R.layout.inflate_layout, null, false);
            ll.addView(view);
        }
    }
    
    

    三、描述下include、merge、ViewStub标签的特点

    1、include:

    1. 不能作为根元素,需要放在 ViewGroup中
    2. findViewById查找不到目标控件,这个问题出现的前提是在使用include时设置了id,而在findViewById时却用了被include进来的布局的根元素id。

    为什么会报空指针呢?
    如果使用include标签时设置了id1,这个id1就会覆盖layout根view中设置的id2,从而找不到这个id2

    代码:

    LayoutInflate.parseInclude 
                --》final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
                --》if (id != View.NO_ID) {
                        view.setId(id);
                    } 
    

    **例子:
    1、IncludeActivity :

    public class IncludeActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_include);
    
            // 使用include最常见的问题就是findViewById查找不到目标控件
            noFind();
            find();
    
            // 直接查找 my_title_layout.xml 布局中的 子View 也是没问题的
    //        TextView titleTextView = (TextView)findViewById(R.id.title_tv) ;
    //        titleTextView.setText("new Title");
        }
    
        // 查找不到,会报错
        private void noFind() {
            //找不到根布局rl_layout
            View titleView = findViewById(R.id.rl_layout);
            // 此时 titleView 为空,找不到。此时空指针(File error accessing recents directory (directory doesn't exist?).)
            TextView titleTextView = titleView.findViewById(R.id.title_tv);
            titleTextView.setText("new Title2");
        }
    
        // 正确查找方式 -- 使用include中的id
        private void find() {
            // 使用include时设置的id,即R.id.include_layout
            View titleView = findViewById(R.id.include_layout);
            // 通过titleView找子控件
            TextView titleTextView = (TextView) titleView.findViewById(R.id.title_tv);
            titleTextView.setText("new Title3");
        }
    
    }
    

    2、activity_include

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <include
            android:id="@+id/include_layout"
            layout="@layout/include_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
    </LinearLayout>
    

    3、include_layout

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/rl_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
        <ImageButton
            android:id="@+id/back_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/ic_launcher" />
    
        <TextView
            android:id="@+id/title_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="20dp"
            android:layout_toRightOf="@+id/back_btn"
            android:gravity="center"
            android:text="我的title"
            android:textSize="18sp" />
    
    </RelativeLayout>
    

    2、merge:

    在Android中,merge是一个布局标签,用于将多个布局文件合并为一个布局文件。它可以用于简化布局文件的层次结构,减少布局文件的嵌套层数,提高布局文件的可读性和可维护性。

      1. merge标签必须使用在根布局
      1. 因为merge标签并不是View,所以在通过LayoutInflate.inflate()方法渲染的时候,第二个参数必须指定一个父容器,且第三个参数必须为true,也就是必须为merge下的视图指定一个父亲节点.
      1. 由于merge不是View所以对merge标签设置的所有属性都是无效的.

    1、在merge标签中添加需要合并的布局文件内容

    <merge xmlns:android="http://schemas.android.com/apk/res/android">
        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!" />
    </merge>
    

    2、在其他布局文件中使用include标签引用merge_layout.xml文件,例如:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <include layout="@layout/merge_layout" />
    </LinearLayout>
    

    这样,merge_layout.xml文件中的布局内容就会被合并到当前布局文件中,从而实现布局的复用和简化。

    3、ViewStub使用:

    ViewStub是一个轻量级的View,它可以在需要时延迟加载布局,从而提高应用程序的性能。ViewStub在布局文件中被定义为一个占位符,当需要显示它时,可以通过调用inflate()方法来加载它。

    以下是使用ViewStub的步骤:

    1、在布局文件中定义ViewStub,layout加载其它布局

    <ViewStub
        android:id="@+id/stub"
        android:inflatedId="@+id/subTree"
        android:layout="@layout/my_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    

    2、在代码中获取ViewStub的引用

    ViewStub stub = findViewById(R.id.stub);
    

    3、在需要显示ViewStub时,调用inflate()方法

    View inflated = stub.inflate();
    // 显示 ViewStub:使用setVisibility和inflate 都可以
    viewStub.setVisibility(View.VISIBLE);
    

    4、对inflated进行操作

    TextView textView = inflated.findViewById(R.id.text_view);
    textView.setText("Hello World!");
    

    注意事项:

    • ViewStub只能被inflate一次,如果需要再次显示,需要重新获取ViewStub的引用。
    • ViewStub只能包含一个子View,如果需要显示多个View,可以使用ViewGroup来包含多个ViewStub。
    • ViewStub在inflate()方法调用后会被移除,如果需要再次显示,需要重新获取ViewStub的引用。

    4、ViewStub源码:

      1. ViewStub:就是一个宽高都为0的一个View,它默认是不可见的
      1. 类似include,但是一个不可见的View类,用于在运行时按需懒加载资源,只有在代码中调用了viewStub.inflate()或者viewStub.setVisible(View.visible)方法时才内容才变得可见。
      1. 这里需要注意的一点是,当ViewStub被inflate到parent时,ViewStub就被remove掉了,即当前view hierarchy中不再存在ViewStub,而是使用对应的layout视图代替。

    1、ViewStub的

    public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        // 获取 InflatedId
        mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
        // 设置不显示
        setVisibility(GONE);
    }
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 设置宽高为0
        setMeasuredDimension(0, 0);
    }
    

    2、inflate方法

    public View inflate() {
        final ViewParent viewParent = getParent();
    
        if (viewParent != null && viewParent instanceof ViewGroup) {
            if (mLayoutResource != 0) {
                final ViewGroup parent = (ViewGroup) viewParent;
                // 设置ID
                final View view = inflateViewNoAdd(parent);
                // 替换自己和View
                replaceSelfWithView(view, parent);
    
                mInflatedViewRef = new WeakReference<>(view);
                if (mInflateListener != null) {
                    mInflateListener.onInflate(this, view);
                }
    
                return view;
            } else {
                throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
            }
        } else {
            throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
        }
    }
    
    private View inflateViewNoAdd(ViewGroup parent) {
        if (mInflatedId != NO_ID) {
            view.setId(mInflatedId);
        }
    }
    
    private void replaceSelfWithView(View view, ViewGroup parent) {
        final int index = parent.indexOfChild(this);
        // 移除自己
        parent.removeViewInLayout(this);
    
        final ViewGroup.LayoutParams layoutParams = getLayoutParams();
        // 添加View
        if (layoutParams != null) {
            parent.addView(view, index, layoutParams);
        } else {
            parent.addView(view, index);
        }
    }
    

    相关文章

      网友评论

          本文标题:Activity加载布局相关知识点

          本文链接:https://www.haomeiwen.com/subject/oaltjdtx.html