fragment findviewbyid找不到对应元素的问题

本文详细分析了在Android项目中,Fragment静态添加时使用findViewById找不到根视图元素的问题。问题出现在FragmentImpl的onCreateView方法中,由于内部逻辑将根视图的ID设置为了容器ID。解决方案包括使用动态添加Fragment或者直接引用容器ID。此外,文章还探讨了Fragment的生命周期及其与Activity的交互机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

fragment findviewbyid找不到对应元素的问题

项目场景:

记录一个fragment使用时遇到的问题,在fragment根viewgroup里定义一个id后,在对应的activity使用该id找不到对应的元素

问题描述:

在fragment的根view定义一个id后,在对应的activity里使用findviewbyid,找不到对应的元素,需要使用activity layout里对应的id,这种情况只发生在静态添加过程中,使用动态添加不存在此问题。 如下,在根viewgroup里添加了一个id:
<!-- TODO: Update blank fragment layout -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/cardview_dark_background"
android:id = "@+id/l1"
tools:context=".BlankFragment">
<TextView
    android:id="@+id/tv1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#00ffffff"
    android:text="@string/hello_blank_fragment" />
之后在activity里使用对应的静态添加:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
<fragment
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:name="com.example.myapplication.BlankFragment"
    android:id="@+id/l2"/>

</LinearLayout>
之后如果在activity里使用l1这个id,是找不到该根view的,需要使用l2这个id,在fragment里,在oncreateview阶段,使用view.findviewbyid 是可以用l1这个id1找到对应的view的,在onactivitycreated之后就无法使用l1找到对应的id了。

原因分析:

具体的原因是在fragmentimpl这个类中,如果使用静态添加方法的话,会经过fragmengimpl的oncreateview方法,该方法里会将根view的id设置为对应container的id,也就是activity里fragment 标签的id,如下:
 @Override
    @Nullable
    public View onCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context,
                             @NonNull AttributeSet attrs) {
        if (!"fragment".equals(name)) {
            return null;
        }

        String fname = attrs.getAttributeValue(null, "class");
        TypedArray a =  context.obtainStyledAttributes(attrs, FragmentTag.Fragment);
        if (fname == null) {
            fname = a.getString(FragmentTag.Fragment_name);
        }
        int id = a.getResourceId(FragmentTag.Fragment_id, View.NO_ID);
        String tag = a.getString(FragmentTag.Fragment_tag);
        a.recycle();

        if (fname == null || !FragmentFactory.isFragmentClass(context.getClassLoader(), fname)) {
            // Invalid support lib fragment; let the device's framework handle it.
            // This will allow android.app.Fragments to do the right thing.
            return null;
        }

        int containerId = parent != null ? parent.getId() : 0;
        if (containerId == View.NO_ID && id == View.NO_ID && tag == null) {
            throw new IllegalArgumentException(attrs.getPositionDescription()
                    + ": Must specify unique android:id, android:tag, or have a parent with an id for " + fname);
        }

        // If we restored from a previous state, we may already have
        // instantiated this fragment from the state and should use
        // that instance instead of making a new one.
        Fragment fragment = id != View.NO_ID ? findFragmentById(id) : null;
        if (fragment == null && tag != null) {
            fragment = findFragmentByTag(tag);
        }
        if (fragment == null && containerId != View.NO_ID) {
            fragment = findFragmentById(containerId);
        }

        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x"
                + Integer.toHexString(id) + " fname=" + fname
                + " existing=" + fragment);
        if (fragment == null) {
            fragment = getFragmentFactory().instantiate(context.getClassLoader(), fname);
            fragment.mFromLayout = true;
            fragment.mFragmentId = id != 0 ? id : containerId;
            fragment.mContainerId = containerId;
            fragment.mTag = tag;
            fragment.mInLayout = true;
            fragment.mFragmentManager = this;
            fragment.mHost = mHost;
            fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
            addFragment(fragment, true);

        } else if (fragment.mInLayout) {
            // A fragment already exists and it is not one we restored from
            // previous state.
            throw new IllegalArgumentException(attrs.getPositionDescription()
                    + ": Duplicate id 0x" + Integer.toHexString(id)
                    + ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId)
                    + " with another fragment for " + fname);
        } else {
            // This fragment was retained from a previous instance; get it
            // going now.
            fragment.mInLayout = true;
            fragment.mHost = mHost;
            // Give the Fragment the attributes to initialize itself.
            fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
        }

        // If we haven't finished entering the CREATED state ourselves yet,
        // push the inflated child fragment along. This will ensureInflatedFragmentView
        // at the right phase of the lifecycle so that we will have mView populated
        // for compliant fragments below.
        if (mCurState < Fragment.CREATED && fragment.mFromLayout) {
            moveToState(fragment, Fragment.CREATED, 0, 0, false);
        } else {
            moveToState(fragment);
        }

        if (fragment.mView == null) {
            throw new IllegalStateException("Fragment " + fname
                    + " did not create a view.");
        }
        if (id != 0) {
            fragment.mView.setId(id);
        }
        if (fragment.mView.getTag() == null) {
            fragment.mView.setTag(tag);
        }
        return fragment.mView;
    }

fragment.mView.setId(id) 这条语句将根view的id设置为了对应container的id。如果使用动态添加的话,则不会经过该方法。具体控制流程在fragmentimpl类中的movetostate方法中,该方法了记录一个当前fragmentimpl类所处状态,并且和当前处理的fragment的状态做对比,从而控制应该经过什么处理流程,具体细节如果有时间的话再继续写。


解决方案:

提示:这里填写该问题的具体解决方案:
使用动态添加或使用container的id。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值