2015-09-16 26 views
8

我一直有我的Android應用程序的問題,即它與下面的錯誤選項卡之間滑動時崩潰:的Android getListView()的片段錯誤

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(); 
} 

我有FO在這裏提供了其他答案,並將getListView()功能添加到onViewCreated()方法,但問題仍然存在......任何人都可以指出我可能會出錯的地方嗎?

+0

謝謝你。將看看它。 –

+0

我想問你什麼時候出現這個錯誤?你第一次打開這個ListFragment顯示的活動,或者你轉向另一個Fragment時,你的應用程序會崩潰?因爲可能有一段時間,你的片段視圖被破壞,但你的任務繼續運行... –

+0

你是如何得到這一切的? –

回答

5

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(); 
} 
+2

'getView()'返回null時拋出'IllegalStateException:尚未創建的內容視圖' – Blackbelt

+0

getListView或getView? – petey

+2

'getListView()'檢查getView()是否返回null。如果'getView()'返回null'內容視圖尚未創建'拋出 – Blackbelt

4

刪除所有這些進口:

import com.yourName.runmate.R; 

然後重新同步您的gradle產出和重建項目。

enter image description here

而且在這裏看到:
"cannot resolve symbol R" in Android Studio

編輯

在主你的第一個明顯的錯誤是

mFragmentManager = getSupportFragmentManager(); 

應該是:

mFragmentManager = getFragmentManager(); 

或更改您的主要活動:

MainActivity extends FragmentActivity利用支持片段經理。


在你的問題中,你有很多不必要的代碼,大多數評論可以被刪除並導入這個問題的目的。

我想到的是沒有活動,正在使用。ListFragment需要附加到一個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。這樣,在空視圖上不能調用任何方法,因爲它們在創建之後被調用。

其次,你需要整理你的活動和你正在努力實現的目標。

你想要一個頁面查看器和一個片段列表。頁面查看器需要與活動或活動片段關聯,而不是片段。否則,無法將瀏覽器頁面附加到該視圖。

使用FragmentActivity不是片段。成爲你運行你的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中,以確保你的活動被創建。您需要將大部分代碼在onViewCreated移動從您的onCreate方法,並把它們或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); 
} 

此代碼只是一個指南,你需要調整它。

讓我知道這是否有幫助。

這些Q &非常好。

Content view not yet created

android Illegal state exception content view not yet create?


Fragment相同的原則適用於viewpager片段ViewPager

+0

你可以告訴我在'TabFragmentContainer'' onCreate()'方法中使用'view'對象的位置嗎?另外,'onCreate()'中的'getFragment()'方法產生'can not resolve method' - 我嘗試用'getChildFragmentManager()'替換它,但仍然出現錯誤 - 是否應該是別的? – KvnH

+0

我改成了'getFragmentManager()',但它在整個代碼中引發了導入錯誤 - 我試圖改變這些導入(主要從'support.v4.app ...'到'app ...'但它似乎只是創造更多的問題,而且我還沒有設法實現你在原始答案中列出的步驟 - 更改爲'FragmentActivity'等,因爲我仍然遇到了我提到的問題。我對Android和編碼很陌生,所以有很多我不確定的東西,但是我非常感謝所有幫助。 – KvnH

+0

我已經刪除了導入,使用gradle重新生成並重建項目...'無法解析符號'R''現在出現在'R.id ...'被調用的地方 - 我應該重新導入它嗎? – KvnH

5

基於這些事實:

  • 拋出異常,因爲沒有根視圖然而w母雞done()調用getListView()
  • done()在由retrieveAcceptedRoutes()發出的查詢得到響應時被調用。
  • 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); 
+0

thanks-you @cybersam - 我已將該代碼移至「onViewCreated()」,這是正確的。不過,我仍然得到同樣的錯誤,所以我不能說這是根本原因。 Upvote整理代碼。 – KvnH

2

試圖聲明:

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; 
4

我再次看了你的問題,然後我猜:

  • 你ListFragment,同時摧毀了你後臺任務繼續運行。所以,當它完成後,你的回調想要更新不再活着的ListView。

  • 實際上,viewPager.setOffscreenPageLimit(3);可能會訣竅,但這不是一個好習慣。它強制你的ViewPager在內存中創建和存儲更多的片段,這是沒有必要的。這就是爲什麼我反對投票。您可以在不這樣做的情況下解決此問題

你應該做的:以下兩種做法之一應該是罰款,或兩者:

  • 摧毀你的任務你或任何生命週期方法,你onDestroyView之前。

  • 排除您在done()方法內更新ListView的代碼。將它作爲一個本地方法,您將仔細檢查您的ListView,並且在那裏,您應該讓您的更新過程在UI線程上運行,以避免任何線程問題。請務必檢查您的getView()是否爲空(但不是您的getListView(),因爲如果getView()返回null,它會拋出異常)。

,我建議你使用這兩者來確保:你的看法是仍然可用在invisible片段正在運行的任務時,你就不會浪費你的資源。不要忘記,默認,一旦你的片段是看不見的,它被認爲是銷燬(並不總是,例如ViewPager保持2片段的引用,但請記住這種情況)。

希望得到這個幫助。