【问题标题】:Strange behavior of fragmentTransaction.replace() methodfragmentTransaction.replace() 方法的奇怪行为
【发布时间】:2017-02-08 06:12:27
【问题描述】:

我刚刚创建了一个演示来了解 FragmentTransaction 的替换方法,但我没有得到 Developer Guide 的结果。

活动和片段

public class MainActivity extends AppCompatActivity {

    private int count;
    private Fragment prevFragment;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final Button button = (Button) findViewById(R.id.activity_main_bv_add);


        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                count++;

                final FragmentTransaction transaction = getFragmentManager().beginTransaction();
                final TestFragment fragment = new TestFragment(count);

                if (count == 1) {
                    transaction.add(R.id.activity_main_rl_container, fragment, fragment.getClass().getSimpleName());

                } else if (count == 5) {
                    transaction.replace(R.id.activity_main_rl_container, fragment, fragment.getClass().getSimpleName());
                    transaction.addToBackStack(null);
                    if (prevFragment != null) {
                        transaction.hide(prevFragment);
                    }
                } else {
                    transaction.add(R.id.activity_main_rl_container, fragment, fragment.getClass().getSimpleName());
                    transaction.addToBackStack(null);
                    if (prevFragment != null) {
                        transaction.hide(prevFragment);
                    }
                }
                prevFragment = fragment;

                transaction.commit();


            }
        });
    }

    public class TestFragment extends Fragment {
        private final String TAG = this.getClass().getSimpleName();
        private int number;


        public TestFragment(final int number) {
            this.number = number;
        }

        public TestFragment() {
        }

        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            Log.e(TAG, "onCreateView : " + number);

            final View view = inflater.inflate(R.layout.row_button, null);

            final Button button = (Button) view.findViewById(R.id.button);
            button.setText("" + number);

            return view;
        }

        @Override
        public void onDestroyView() {
            super.onDestroyView();
            Log.e(TAG, "onDestroyView : " + number);
        }
    }
}

片段 xml

    <?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:background="@color/colorPrimary"
              android:orientation="vertical">

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:focusable="false"
        android:focusableInTouchMode="false"
        />

</LinearLayout>

活动 xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/activity_currency_select"
                xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:descendantFocusability="blocksDescendants"
    >

    <Button
        android:id="@+id/activity_main_bv_add"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:text="Add More"/>

    <RelativeLayout
        android:id="@+id/activity_main_rl_container"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:layout_below="@id/activity_main_bv_add"
        android:layout_marginTop="20dp"
        android:background="@color/colorAccent"></RelativeLayout>


</RelativeLayout>

FragmentTransaction 替换方法: 根据 Android 文档, 替换已添加到容器中的现有片段。这本质上与为所有当前添加的片段调用 remove(Fragment) 相同,这些片段使用相同的 containerViewId 添加,然后使用此处给出的相同参数调用 add(int, Fragment, String)。

假设案例 1:使用 replace 方法将删除当前(最后一个和最新)片段并在容器视图中添加新片段。

假设案例 2:使用 replace 方法将删除添加添加到同一容器视图中的片段。

但根据上面的代码,上述情况似乎不是正确的。

让我们按照显示的代码单击按钮。 每次点击按钮直到 count == 4,Fragment 的新对象将被添加到容器中。

当我们点击按钮时,count == 5,此时使用replace方法将Fragment的新对象添加到容器中,并为第1个和第3个添加的片段调用onDestroyView方法

问题1:如果replace会删除所有添加到同一个容器中的片段,那么为什么不为第二个和第四个添加的片段调用onDestroyView?

问题 2: 如果 replace 方法删除了同一容器中最后添加的片段,那么为什么它不销毁第 4 个片段而是第 1 和第 3 个片段?

据我了解,该行为不符合Developer site 上显示的文档。

如果我错了,请纠正我。

【问题讨论】:

  • 在github上分享完整的demo项目。

标签: android android-fragments


【解决方案1】:

这似乎是replace 方法的错误。 请注意,FragmentManager 的支持版本不会发生这种情况。

我会参考thisthis

第一个标记为Obsolete,第二个标记为Assigned,但两者似乎非常相关。

在两者中都强调了片段的删除(从 ArrayList 中)是在 for 循环内完成的,然后片段的 ArrayList 在这个 for 循环内改变它的大小,这会导致一些索引的跳过,并且然后删除一些(不是全部)片段。

这是链接中提到的 for 循环:

for (int i = 0; i < mManager.mAdded.size(); i++) {
    Fragment old = mManager.mAdded.get(i);
    // ... 
    if (f == null || old.mContainerId == f.mContainerId) {
        if (old == f) {
            op.fragment = f = null;
        } else {
            if (op.removed == null) {
                op.removed = new ArrayList<Fragment>();
            }
            op.removed.add(old);
            old.mNextAnim = op.exitAnim;
            if (mAddToBackStack) {
                old.mBackStackNesting += 1;
                if (FragmentManagerImpl.DEBUG) {
                    Log.v(TAG, "Bump nesting of "
                            + old + " to " + old.mBackStackNesting);
                }
            }
            mManager.removeFragment(old, mTransition, mTransitionStyle);
        }
    }
}

在您的具体情况下,您正在添加片段 1、2、3、4。之后,当您想用第五个片段替换所有这四个片段时,执行上面的 for 循环。因此,您删除第一个(对应于 for 循环中的 i=0),片段的数组列表变为 2、3、4。然后在 for 循环中选择下一个索引(i=1),但与此同时,arraylist 发生了变化,因此取出片段 3(对应于 i=1)并将其删除。最后这个arraylist变成了2,4,i=2,没有一个fragment对应索引2。

我希望我已经帮助你理解了,即使我不拿50分:)

【讨论】:

    【解决方案2】:
    for (int i=0; i<mManager.mAdded.size(); i++) {    
        Fragment old = mManager.mAdded.get(i);
        if (f ==null ||old.mContainerId == f.mContainerId) {
            mManager.removeFragment(old,mTransition, mTransitionStyle);
        }
    }
    

    MManager.mAdded是一个ArrayList列表,在mManager.removeFragment方法的遍历中调用时,该方法调用ArrayList remove方法;

    Public void removeFragment (Fragmentfragment, int transition, inttransitionStyle) {
        MAdded.remove (fragment);    
    }
    

    这意味着当使用for循环遍历ArrayList列表时使用remove

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-16
      • 2021-07-07
      相关资源
      最近更新 更多