【问题标题】:Android getListView() in fragment error片段错误中的Android getListView()
【发布时间】:2015-12-13 07:10:21
【问题描述】:

我的 Android 应用程序一直存在问题,在标签之间滑动时它崩溃并出现以下错误:

09-16 16:19:27.142    4750-4750/com.khackett.runmate E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.khackett.runmate, PID: 4750
    java.lang.IllegalStateException: Content view not yet created
            at android.support.v4.app.ListFragment.ensureList(ListFragment.java:328)
            at android.support.v4.app.ListFragment.getListView(ListFragment.java:222)
            at com.khackett.runmate.ui.MyRunsFragment$1.done(MyRunsFragment.java:167)
            at com.khackett.runmate.ui.MyRunsFragment$1.done(MyRunsFragment.java:135)
            at com.parse.ParseTaskUtils$2$1.run(ParseTaskUtils.java:115)
            at android.os.Handler.handleCallback(Handler.java:739)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5254)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

这是 MyRunsFragment:

public class MyRunsFragment extends ListFragment {

    protected SwipeRefreshLayout mSwipeRefreshLayout;

    // member variable to store the list of routes the user has accepted
    protected List<ParseObject> mAcceptedRoutes;

    private int MY_STATUS_CODE = 1111;

    // Default constructor for MyRunsFragment
    public MyRunsFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        View rootView = inflater.inflate(R.layout.fragment_my_runs, container, false);

        // Set SwipeRefreshLayout component
        mSwipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipeRefreshLayout);
        // Set the onRefreshListener
        mSwipeRefreshLayout.setOnRefreshListener(mOnRefreshListener);
        mSwipeRefreshLayout.setColorSchemeResources(
                R.color.swipeRefresh1,
                R.color.swipeRefresh2,
                R.color.swipeRefresh3,
                R.color.swipeRefresh4);

        return rootView;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        // Retrieve the accepted routes from the Parse backend
        retrieveAcceptedRoutes();
    }

    @Override
    public void onResume() {
        super.onResume();
    }

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);

        // create the message object which is set to the message at the current position
        ParseObject route = mAcceptedRoutes.get(position);

        // String messageType = message.getString(ParseConstants.KEY_FILE_TYPE);

        JSONArray parseList = route.getJSONArray(ParseConstants.KEY_LATLNG_POINTS);
        JSONArray parseListBounds = route.getJSONArray(ParseConstants.KEY_LATLNG_BOUNDARY_POINTS);
        String objectId = route.getObjectId();
        String routeName = route.getString(ParseConstants.KEY_ROUTE_NAME);
        // JSONArray ids = route.getJSONArray(ParseConstants.KEY_RECIPIENT_IDS);

        // Start a map activity to display the route
        Intent intent = new Intent(getActivity(), MapsActivityTrackRun.class);
        intent.putExtra("parseLatLngList", parseList.toString());
        intent.putExtra("parseLatLngBoundsList", parseListBounds.toString());
        intent.putExtra("myRunsObjectId", objectId);
        intent.putExtra("myRunsRouteName", routeName);

        // Start the MapsActivityDisplayRoute activity
        startActivityForResult(intent, MY_STATUS_CODE);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {

    }

    private void retrieveAcceptedRoutes() {
        // query the routes class/table in parse
        // get messages where the logged in user ID is in the list of the recipient ID's (we only want to retrieve the messages sent to us)
        // querying the message class is similar to how we have been querying users
        ParseQuery<ParseObject> queryRoute = new ParseQuery<ParseObject>(ParseConstants.CLASS_ROUTES);
        // use the 'where' clause to search through the messages to find where our user ID is one of the recipients
        queryRoute.whereEqualTo(ParseConstants.KEY_ACCEPTED_RECIPIENT_IDS, ParseUser.getCurrentUser().getObjectId());
        // order results so that most recent message are at the top of the inbox
        queryRoute.addDescendingOrder(ParseConstants.KEY_CREATED_AT);
        // query is ready - run it
        queryRoute.findInBackground(new FindCallback<ParseObject>() {
            // When the retrieval is done from the Parse query, the done() callback method is called
            @Override
            public void done(List<ParseObject> routes, ParseException e) {
                // dismiss the progress indicator here
                // getActivity().setProgressBarIndeterminateVisibility(false);

                // End refreshing once routes are retrieved
                // done() is called from onResume() and the OnRefreshListener
                // Need to check that its called from the the OnRefreshListener before ending it
                if (mSwipeRefreshLayout.isRefreshing()) {
                    mSwipeRefreshLayout.setRefreshing(false);
                }

                // the list being returned is a list of routes
                if (e == null) {
                    // successful - routes found.  They are stored as a list in messages
                    mAcceptedRoutes = routes;

                    // adapt this data for the list view, showing the senders name

                    // create an array of strings to store the usernames and set the size equal to that of the list returned
                    String[] usernames = new String[mAcceptedRoutes.size()];
                    // enhanced for loop to go through the list of users and create an array of usernames
                    int i = 0;
                    for (ParseObject message : mAcceptedRoutes) {
                        // get the specific key
                        usernames[i] = message.getString(ParseConstants.KEY_SENDER_NAME);
                        i++;
                    }

                    // Create the adapter once and update its state on each refresh
                    if (getListView().getAdapter() == null) {
                        // the above adapter code is now replaced with the following line
                        RouteMessageAdapter adapter = new RouteMessageAdapter(getListView().getContext(), mAcceptedRoutes);

                        // Force a refresh of the list once data has changed
                        adapter.notifyDataSetChanged();

                        // need to call setListAdapter for this activity.  This method is specifically from the ListActivity class
                        setListAdapter(adapter);
                    } else {
                        // refill the adapter
                        // cast it to RouteMessageAdapter
                        ((RouteMessageAdapter) getListView().getAdapter()).refill(mAcceptedRoutes);
                    }
                }
            }
        });
    }

    protected SwipeRefreshLayout.OnRefreshListener mOnRefreshListener = new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            // When list is swiped down to refresh, retrieve the users runs from the Parse backend
            retrieveAcceptedRoutes();
        }
    };

}

以及 fragment_my_runs 布局文件:

<RelativeLayout
    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"
    tools:context=".MainActivity$PlaceholderFragment">

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipeRefreshLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true">

        <ListView
            android:id="@android:id/list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_alignParentStart="true"
            android:layout_alignParentTop="true"
            android:clipToPadding="false"
            android:paddingBottom="@dimen/inbox_vertical_margin"/>

    </android.support.v4.widget.SwipeRefreshLayout>

    <TextView
        android:id="@android:id/empty"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/empty_inbox_label"
        android:textSize="@dimen/default_text_size"/>

</RelativeLayout>

TabFragmentContainer

public class TabFragmentContainer extends Fragment {

    // Create the FragmentPagerAdapter that will provide and manage tabs for each section.
    public static MyFragmentPagerAdapter myFragmentPagerAdapter;

    public static TabLayout tabLayout;

    // The ViewPager is a layout widget in which each child view is a separate tab in the layout.
    // It will host the section contents.
    public static ViewPager viewPager;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        // Inflate tab_layout_fragment_container view and setup views for the TabLayout and ViewPager items.
        View view = inflater.inflate(R.layout.tab_layout_fragment_container, null);

        tabLayout = (TabLayout) view.findViewById(R.id.tabs);

        // Set up the ViewPager with the sections adapter.
        viewPager = (ViewPager) view.findViewById(R.id.viewpager);

        // Instantiate the adapter that will return a fragment for each of the three sections of the main activity
        myFragmentPagerAdapter = new MyFragmentPagerAdapter(getActivity(), getChildFragmentManager());

        // Set up the adapter for the ViewPager
        viewPager.setAdapter(myFragmentPagerAdapter);

        // Runnable() method required to implement setupWithViewPager() method
        tabLayout.post(new Runnable() {
            @Override
            public void run() {
                tabLayout.setupWithViewPager(viewPager);
                viewPager.setCurrentItem(1, false);
                // tabLayout.getTabAt(1).select();
            }
        });

        // Return the created View
        return view;
    }

}

FragmentPagerAdapter:

public class MyFragmentPagerAdapter extends FragmentPagerAdapter {

    // The context to be passed in when the adapter is created.
    private Context mContext;
    // The number of tabs in the layout.
    public static int numberOfTabs = 3;

    /**
     * Default constructor that accepts a FragmentManager parameter to add or remove fragments.
     *
     * @param context         the context from the activity using the adapter.
     * @param fragmentManager the FragmentManager for managing Fragments inside of the TabFragmentContainer.
     */
    public MyFragmentPagerAdapter(Context context, FragmentManager fragmentManager) {
        super(fragmentManager);
        mContext = context;
    }

    /**
     * Method to return the relevant fragment for the selected tab.
     */
    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
                return new MyRunsFragment();
            case 1:
                return new InboxRouteFragment();
            case 2:
                return new FriendsFragment();
        }
        return null;
    }

    /**
     * Method that gets the number of tabs in the layout.
     *
     * @return the number of tabs in the layout.
     */
    @Override
    public int getCount() {
        return numberOfTabs;
    }

    /**
     * Method that returns the title of each tab in the layout.
     */
    @Override
    public CharSequence getPageTitle(int position) {
        Locale locale = Locale.getDefault();
        switch (position) {
            case 0:
                return mContext.getString(R.string.title_section1).toUpperCase(locale);
            case 1:
                return mContext.getString(R.string.title_section2).toUpperCase(locale);
            case 2:
                return mContext.getString(R.string.title_section3).toUpperCase(locale);
        }
        return null;
    }
}

包含 ViewPager 小部件的 tab_layout_fragment_container 文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <android.support.design.widget.TabLayout
        android:id="@+id/tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/ColorPrimaryPurple"
        app:tabGravity="fill"
        app:tabIndicatorColor="@color/ColorPrimaryPurple"
        app:tabMode="fixed"
        app:tabSelectedTextColor="@color/textColorPrimary"
        app:tabTextColor="@color/pressedPurpleButton">
    </android.support.design.widget.TabLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </android.support.v4.view.ViewPager>

</LinearLayout>

我的 MainActivity 中的 onCreate() 方法:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    // Initialise the DrawerLayout and NavigationView views.
    mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout);
    mNavigationView = (NavigationView) findViewById(R.id.navigationDrawerMenu);

    // Inflate the first fragment to be displayed when logged into the app.
    mFragmentManager = getSupportFragmentManager();
    mFragmentTransaction = mFragmentManager.beginTransaction();
    mFragmentTransaction.replace(R.id.containerView, new TabFragmentContainer()).commit();

    // Setup click events on the NavigationView items.
    // When an item is selected, replace the tab fragment container with the requested fragment.
    mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
        @Override
        public boolean onNavigationItemSelected(MenuItem menuItem) {
            mDrawerLayout.closeDrawers();
            if (menuItem.getItemId() == R.id.navItemHome) {
                FragmentTransaction tabFragmentContainer = mFragmentManager.beginTransaction();
                tabFragmentContainer.replace(R.id.containerView, new TabFragmentContainer()).commit();
            }
            if (menuItem.getItemId() == R.id.navItemRunHistory) {
                FragmentTransaction runHistoryFragment = mFragmentManager.beginTransaction();
                runHistoryFragment.replace(R.id.containerView, new RunHistoryFragment()).commit();
            }
            if (menuItem.getItemId() == R.id.navItemSettings) {
                FragmentTransaction settingsFragment = mFragmentManager.beginTransaction();
                settingsFragment.replace(R.id.containerView, new SettingsFragment()).commit();
            }
            if (menuItem.getItemId() == R.id.navItemHelp) {
                FragmentTransaction instructionsFragment = mFragmentManager.beginTransaction();
                instructionsFragment.replace(R.id.containerView, new InstructionsFragment()).commit();
            }
            if (menuItem.getItemId() == R.id.navItemMyProfile) {
                FragmentTransaction myProfileFragment = mFragmentManager.beginTransaction();
                myProfileFragment.replace(R.id.containerView, new MyProfileFragment()).commit();
            }
            if (menuItem.getItemId() == R.id.navItemLogOut) {
                // User has selected log out option. Log user out and return to login screen.
                ParseUser.logOut();
                navigateToLogin();
            }
            return false;
        }
    });

    // Set up the Toolbar.
    setupToolbar();
}

我在这里关注了其他答案,并将getListView() 功能添加到onViewCreated() 方法中,但问题仍然存在......谁能指出我可能出错的地方?

【问题讨论】:

  • 我想问你什么时候收到这个错误?当您第一次打开显示此 ListFragment 的活动时,或者您转向另一个 Fragment 时,您的应用程序会崩溃?因为可能有一个时间点,您的片段视图被破坏但您的任务仍在运行......

标签: java android android-fragments android-studio


【解决方案1】:

基于这些事实:

  • 由于done()调用getListView()时还没有根视图,所以抛出异常。
  • retrieveAcceptedRoutes() 的查询得到响应时调用done()
  • retrieveAcceptedRoutes在多个地方被调用,包括OnRefreshListenermOnRefreshListener,在onCreateView()中注册为刷新监听之前有根视图(即onCreateView()之前返回)。

...getListView() 可以在有根视图之前被调用。

尝试将这 3 个语句从 onCreateView() 移动到 onViewCreated(),这样刷新侦听器只能在有根视图时调用。

    // Set SwipeRefreshLayout component 
    mSwipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipeRefreshLayout);
    // Set the onRefreshListener 
    mSwipeRefreshLayout.setOnRefreshListener(mOnRefreshListener);
    mSwipeRefreshLayout.setColorSchemeResources(
            R.color.swipeRefresh1,
            R.color.swipeRefresh2,
            R.color.swipeRefresh3,
            R.color.swipeRefresh4);

【讨论】:

  • 谢谢@cybersam - 我已将该代码移至onViewCreated(),这是正确的。但是我仍然遇到同样的错误,所以我不能说这是根本原因。为整理代码点赞。
【解决方案2】:

我再次阅读了您的问题,然后我猜:

  • 当您的后台任务继续运行时,您的 ListFragment 被破坏。所以当它完成后,你的回调想要更新不再存在的 ListView。

  • 实际上,viewPager.setOffscreenPageLimit(3); 可以解决问题,但这不是一个好习惯。它会强制您的 ViewPager 在内存中创建和存储更多片段,这是不必要的。这就是为什么我反对这个答案。你可以不这样做就解决这个问题。

你应该做什么:以下两种做法中的一种应该可以,或者两者兼而有之:

  • onDestroyView 之前在 onPause 或任何生命周期方法中销毁您的任务。

  • done() 方法中排除更新 ListView 的代码。使其成为本地方法,您将在其中仔细检查 ListView,并且在那里,您应该要求更新过程在 UI 线程上运行以避免任何线程问题。确保检查您的getView() 是否不为空(但不是您的getListView(),因为如果 getView() 返回 null,它会引发异常)。

我建议您同时使用它们以确保:您的视图仍然可用,并且在 invisible 片段中运行任务时不会浪费资源。不要忘记默认情况下,一旦你的片段不可见,它被认为被销毁(并非总是如此,例如 ViewPager 保持对 2 个片段的引用,但保持在请注意这种情况)。

希望对您有所帮助。

【讨论】:

    【解决方案3】:

    onViewCreatedonCreateView 之后立即被调用,但是super.onViewCreated 调用丢失了,这可能是您的问题的根本原因。

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState); // add this line back in
        // Retrieve the accepted routes from the Parse backend
        retrieveAcceptedRoutes();
    }
    

    【讨论】:

    • IllegalStateException: Content view not yet createdgetView() 返回 null 时抛出
    • getListView 还是 getView?
    • getListView() 检查getView() 是否返回空值。如果getView() 返回null,则抛出Content view not yet created
    • @petey 已经拥有android:id="@android:id/list" id 属性。我将其更改为您的建议,但仍然发生崩溃。
    • 如果有什么不同,我会添加xml布局
    【解决方案4】:

    删除所有这些导入:

    import com.yourName.runmate.R;
    

    然后重新同步您的 gradle 并重建您的项目。

    另见此处:
    "cannot resolve symbol R" in Android Studio

    编辑

    您在 Main 中的第一个明显错误是

    mFragmentManager = getSupportFragmentManager();
    

    应该是:

    mFragmentManager = getFragmentManager();
    

    或将您的主要活动更改为:

    MainActivity extends FragmentActivity 使用支持片段管理器。


    您的问题中有很多不必要的代码,大多数 cmets 可以被删除和导入来解决这个问题。

    我想出的是没有活动,正在使用。 ListFragment 需要附加到 Activity,或者您尝试在创建之前调用该 Activity 视图。

    java.lang.IllegalStateException: Content view not yet created
                at android.support.v4.app.ListFragment.ensureList(ListFragment.java:328)
                at android.support.v4.app.ListFragment.getListView(ListFragment.java:222)
    

    如果您使用的是 Main,那么据我所知,您并没有将它们很好地结合在一起。

    首先:

    从您的 onCreate 和 onCreateView 中取出所有内容(对于所有片段),除了视图充气器。

    将所有额外代码放入 onViewCreated 或 onActivityCreated。这样就不能在空视图上调用任何方法,因为这些方法是在创建后调用的。

    其次,您需要整理出您的活动以及您想要实现的目标。

    您需要一个页面查看器和一个片段列表。页面查看器需要与活动或活动片段相关联,而不是片段。否则,没有可将 pageviewer 页面附加到的视图。

    使用 FragmentActivity 而不是 Fragment。成为您运行 ListFragment 的活动。

    public class TabFragmentContainer extends FragmentActivity {
    
        MyPageAdapter pageAdapter;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.tab_layout_fragment_container); // change to view layout.
    
            // Instantiate the adapter that will return a fragment for each of the three sections of the main activity
            myFragmentPagerAdapter = new MyFragmentPagerAdapter(getFragmentManager(), getFragments());
    
            tabLayout = (TabLayout) view.findViewById(R.id.tabs);
    
             // Set up the ViewPager with the sections adapter.
            viewPager = (ViewPager) view.findViewById(R.id.viewpager);
                    // Set up the adapter for the ViewPager
            viewPager.setAdapter(myFragmentPagerAdapter);
    
        }
    }
    

    我建议将其放入您的 ListFragment 中,以确保创建您的活动。您需要将大部分代码从 onCreate 方法中移出,并将它们放入 onViewCreated 或 onActivityCreated

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
      super.onActivityCreated(savedInstanceState);
      ArrayAdapter adapter = ArrayAdapter.createFromResource(getActivity(), R.layout.my_listview)layout, android.R.layout.simple_list_item_1);
    
      setListAdapter(adapter);
      getListView().setOnItemClickListener(this);
    }
    

    此代码只是一个指南,您需要对其进行调整。

    如果这有帮助,请告诉我。

    这些问答非常好。

    Content view not yet created

    android Illegal state exception content view not yet create?


    Fragment同样的原则适用于viewpager片段ViewPager

    【讨论】:

    • 你能告诉我view 对象是在哪里创建的,该对象在TabFragmentContainer onCreate() 方法中使用? onCreate() 中的 getFragment() 方法还产生了一个“无法解决方法” - 我尝试用 getChildFragmentManager() 替换它,但仍然出现错误 - 这应该是别的什么吗?
    • 我更改为`getFragmentManager()`,但它在其余代码中出现导入错误-我尝试更改这些导入(主要从support.v4.app...app...,但它只是似乎正在制造更多问题。此外,我仍然没有设法实施您在原始答案中概述的步骤 - 更改为 FragmentActivity 等,因为我仍然遇到我提到的问题。您可能会告诉我我是新手Android 和编码,所以有很多我不确定的事情,但我非常感谢到目前为止的所有帮助。
    • 我已删除导入,与 gradle 重新同步并重建项目...cannot resolve symbol 'R' 现在出现在调用 R.id... 的位置 - 我应该重新导入它吗?
    • 对不起...当我重建时,我在 Messages Gradle Build 中得到以下信息 - Error:(73, 44) error: package R does not exist
    【解决方案5】:

    尝试声明:

    viewPager = (ViewPager) view.findViewById(R.id.viewpager); 
    viewPager.setAdapter(myFragmentPagerAdapter);  
    myFragmentPagerAdapter = new MyFragmentPagerAdapter(getActivity(), getChildFragmentManager());
    

    之前:

        tabLayout = (TabLayout) view.findViewById(R.id.tabs);
        tabLayout.post(new Runnable() {
                @Override
                public void run() {
                    tabLayout.setupWithViewPager(viewPager);
                    viewPager.setCurrentItem(1, false);
                    // tabLayout.getTabAt(1).select();
                }
            });
    
            // Return the created View
            return view;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-04
      • 1970-01-01
      • 2015-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多