Activity加载布局相关知识点

2023-04-14  本文已影响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)

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

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标签中添加需要合并的布局文件内容

<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!");

注意事项:

4、ViewStub源码:

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);
    }
}
上一篇下一篇

猜你喜欢

热点阅读