Android多次添加片段,但只找到一个实例

时间:2016-01-21 05:36:04

标签: android android-fragments

我的活动包含一个包含项目列表的片段。

活动类:

    public class CategoryActivity extends ActionBarActivity {

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

        setupActionBar();

        CategoryFragment fragment = (CategoryFragment) getSupportFragmentManager().findFragmentById(R.id.category_fragment);
        fragment.setBrand(mBrand);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        UIUtils.unbindDrawables(findViewById(R.id.RootView));
        CategoryFragment fragment = (CategoryFragment) getSupportFragmentManager().findFragmentById(R.id.category_fragment);
        if(fragment != null)
            getSupportFragmentManager().beginTransaction().remove(fragment).commit();

        System.gc();
    }

    public static class CategoryFragment extends Fragment {

        private ListView mListView;
        private CategoryAdapter mAdapter;
        private Category mRoot;
        private List<Category> mCategories;
        private Brand brand;

        public CategoryFragment(){}

        public void setBrand(Brand brand) {
            this.brand = brand;
        }

        private boolean mIsAnimating;

        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_category, container, false);
            mListView = (ListView) view.findViewById(R.id.list_view);

            try {
                if (mCategories == null) {
                    loadCategories(AppController.getInstance().getCategory());
                }

                if (mCategories != null) {
                    mAdapter = new CategoryAdapter(getActivity(), mRoot);
                    mAdapter.setListener(new CategoryAdapter.Listener() {
                        @Override
                        public void onCategoryClick(String category) {
                            if (brand == null) return;

                            Utils.openQueryView(getActivity(), category, brand);
                        }

                        @Override
                        public void openSubcategory(Category category) {
                            openSubcategoriesView(category);
                        }

                        @Override
                        public void onBrowseAllClick(Category category) {
                            if (brand == null) return;

                            if (category.name.equals(Category.ROOT)) {
                                Utils.openQueryView(getActivity(), category.name, brand);
                            } else {
                                Utils.openStoreActivity(getActivity(), category, brand);
                            }
                        }
                    });
                    mListView.setAdapter(mAdapter);
                }
            }catch (Exception e){
                LOGD(TAG, e.getMessage());
                return view;
            }
            return view;
        }

        private void loadCategories(Category root) {
            // stuff here... it work well
        }

        private void openSubcategoriesView(Category category) {

            AnalyticsManager.sendScreenView(SCREEN_NAME);
            final CategoryFragment fragment = new CategoryFragment();
            fragment.setCategories(category);
            fragment.setBrand(brand);
            if (mIsAnimating) {
                return;
            }
            mIsAnimating = true;

            FragmentTransaction transaction = getFragmentManager().beginTransaction();
            transaction.setCustomAnimations(R.anim.slide_in_right, 0, 0,
                    R.anim.slide_out_right);
            transaction.add(R.id.category_fragment, fragment);
            transaction.addToBackStack(null);
            transaction.commit();

            mIsAnimating = false;

            List<Fragment> fs = getFragmentManager().getFragments();

            for(int i = 0; i< fs.size(); i++){
                LOGD(TAG, "fragment[" + i + "]: " + fs.get(i).getActivity());
            }
        }

        public void setCategories(Category category) {
            loadCategories(category);
        }
    }
}

活动布局xml:

  

    <fragment
        android:id="@+id/the_main_fragment"
        android:tag="firstFragment"
              android:name="com.greelane.gapp.ui.CategoryActivity$CategoryFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <LinearLayout
        android:background="@drawable/header_shadow"
        android:layout_width="match_parent"
        android:layout_height="12dp"></LinearLayout>
</RelativeLayout>

每当我点击一个项目时,一个新的 CategoryFragment 将被添加到此活动的 FragmentManager 中,并在xml布局(category_fragment id)中使用相同的Fragment类作为子项如果孩子有孩子,这个概念将会重复。

每次点击某个项目时,我都会尝试记录以查看我有多少片段:

功能 openSubcategoriesView

List<Fragment> fs = getFragmentManager().getFragments();
// fs size > 1, but just one instance of CategoryFragment
           for(int i = 0; i< fs.size(); i++){
               LOGD(TAG, "fragment[" + i + "]: " + fs.get(i).getActivity());
           }

导航到活动后第一次点击某个项目,它只创建一个片段,因此 fs 大小为1;

然后我点击返回&gt;点击另一个项目,我看到 fs 大小为2,但我找到了 CategoryFragment 的一个实例。

我不知道它如何与活动中的片段一起使用,但有时我的应用程序在 onCreateActivity setContentView(R.layout.activity_subcategory)的这一行崩溃;

错误日志:

  

引起:android.view.InflateException:二进制XML文件行#14:错误膨胀类片段

第14行从我的xml布局

开始
  

片段               android:id =“@ + id / category_fragment”......

所以问题是:

  1. 为什么我在添加多次后只有一个 CategoryFragment 实例以及如何解决此问题?

  2. 为什么有时我会得到上面描述的膨胀异常广告以及如何解决此问题?

  3. #Updated1

    第二个问题的错误日志,找不到任何异常堆栈跟踪:

    01-21 11:16:54.276 16783-16783/? W/System.err: java.lang.RuntimeException: Unable to start activity ComponentInfo{vn.app.alezaa/com.greelane.gapp.ui.CategoryActivity}: android.view.InflateException: Binary XML file line #14: Error inflating class fragment
    01-21 11:16:54.279 16783-16783/? W/System.err: Caused by: android.view.InflateException: Binary XML file line #14: Error inflating class fragment
    01-21 11:16:54.282 16783-16783/? W/System.err: Caused by: android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment com.greelane.gapp.ui.CategoryActivity$CategoryFragment: make sure class name exists, is public, and has an empty constructor that is public
    01-21 11:16:54.286 16783-16783/? W/System.err: Caused by: java.lang.InstantiationException: can't instantiate class com.greelane.gapp.ui.CategoryActivity$CategoryFragment; no empty constructor
    

    #Updated2

    1. 我无法使用android:name=...CategoryActivity.CategoryFragment代替android:name=...CategoryActivity$CategoryFragment但仍有相同的错误
    2. 第一次发布这个问题时,空的构造函数已经有了。
    3. 我编写了我的代码来添加新的CategoryFragment,如:

      final CategoryFragment fragment = new CategoryFragment();
              fragment.setCategories(category);
              fragment.setBrand(brand);
              if (mIsAnimating) {
                  return;
              }
              mIsAnimating = true;
      
              FragmentTransaction transaction = getFragmentManager().beginTransaction();
              transaction
                      .setCustomAnimations(R.anim.slide_in_right, 0, 0, R.anim.slide_out_right)
                      .replace(R.id.container, fragment, category.title)// set tag is title of ctg
                      .addToBackStack(category.title)
                      .commit();
      
              mIsAnimating = false;
      
              List<Fragment> fs = getFragmentManager().getFragments();
      // `error raised here after two times add CategoryFragment, the **fs** size > 1, but just one instance of it.`
              for(int i = 0; i< fs.size(); i++){
                  LOGD(TAG, "fragment[" + i + "]: " + fs.get(i).getActivity());
              }
      

      编辑的xml布局:

        

          <fragment
              android:id="@+id/the_main_fragment"
              android:tag="firstFragment"
              android:name="com.greelane.gapp.ui.CategoryActivity$CategoryFragment"
              android:layout_width="match_parent"
              android:layout_height="match_parent" />
      
          <LinearLayout
              android:background="@drawable/header_shadow"
              android:layout_width="match_parent"
              android:layout_height="12dp"></LinearLayout>
      </RelativeLayout>
      

      或者我需要将CategoryFragment分离到其他类并转向非静态,因为现在我无法删除静态。有什么想法吗?

2 个答案:

答案 0 :(得分:0)

要回答您多次加载相同片段的问题,可以查看here,注意部分中的内容

  

每个片段都需要系统可以使用的唯一标识符   如果重新启动活动,则恢复片段(以及可以执行的操作)   用于捕获片段以执行事务,例如remove   它)。有三种方法可以为片段提供ID:

     
      
  • 使用唯一ID提供android:id属性。
  •   
  • 使用唯一字符串提供android:tag属性。
  •   
  • 如果您不提供前两个,系统将使用容器视图的ID。
  •   

因此,您可以多次使用相同的片段,但如果通过layout.xml添加唯一ID和标记,或者如果通过代码添加,则需要使用唯一标记。

在您的情况下,您可以考虑使用Category对象中的一些唯一字段在加载片段时用作TAG,因此您加载的每个CategoryFragment实例都将具有唯一标记,并且在fragmentmanager堆栈中将被视为唯一。

关于你的第二个问题,两个建议

  1. 将xml中fragment标签中的class属性更改为android:name并添加完全限定的片段类名,即 android:name = "mypackage.CategoryFragment"

  2. 同样根据您的崩溃检查,提到 java.lang.InstantiationException:无法实例化类com.greelane.gapp.ui.CategoryActivity $ CategoryFragment;没有空构造函数。因此,创建一个空的或默认的构造函数,如

    public CategoryFragment(){

    }

答案 1 :(得分:0)

关于这个错误,我终于想通了

  List<Fragment> fs = getFragmentManager().getFragments();
// `error raised here after two times add CategoryFragment, the **fs** size > 1, but just one instance of it.`
        for(int i = 0; i< fs.size(); i++){
            LOGD(TAG, "fragment[" + i + "]: " + fs.get(i).getActivity());
        }

基于该文件,

  

调用commit()不会立即执行事务。相反,一旦线程能够这样做,它就会将其安排在活动的UI线程(&#34; main&#34;线程)上运行。但是,如果需要,您可以从UI线程调用executePendingTransactions()以立即执行commit()提交的事务。除非事务是其他线程中作业的依赖项,否则通常不需要这样做。

所以我们需要等待一段时间才能完成提交。现在好了。

但仍在等待测试我上面提到的第二个错误。如果有一个好的结果,我会很快得到更新。