我的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()
方法,但问题仍然存在......有人能指出我可能出错的地方吗?
答案 0 :(得分:5)
onViewCreated
之后立即调用 onCreateView
,但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();
}
答案 1 :(得分:5)
基于以下事实:
done()
调用getListView()
时尚无根视图。done()
的查询得到回复时,会调用retrieveAcceptedRoutes()
。retrieveAcceptedRoutes
在多个地方被调用,包括OnRefreshListener
mOnRefreshListener
,在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);
答案 2 :(得分:5)
我再次阅读你的问题然后我想:
当您的后台任务继续运行时,您的ListFragment将被销毁。所以当它完成后,你的回调想要更新不再存在的ListView。
实际上,viewPager.setOffscreenPageLimit(3);
可能会成功,但这不是一个好习惯。它会强制您的ViewPager在内存中创建和存储更多碎片,这是不必要的。这就是我拒绝投票的原因。你可以在不这样做的情况下解决这个问题。
你应该做什么:以下两种做法之一应该没问题,或两者兼而有之:
在onPause
之前,在onDestroyView
或任何生命周期方法中销毁您的任务。
排除在done()
方法中更新ListView的代码。使它成为一个本地方法,您将仔细检查ListView,在那里,您应该要求您的更新过程在UI线程上运行,以避免任何线程问题。确保检查getView()
是否为空(但不是getListView()
,因为如果getView()返回null,则抛出异常。
我建议您同时使用它们来确保:您的视图仍然可用,并且在invisible
片段中运行任务时不会浪费资源。不要忘记默认情况下,一旦你的片段不可见,被认为是被销毁(并非总是如此,例如ViewPager会保留2个片段的引用,但请保留记住那个案例)。
希望得到这个帮助。
答案 3 :(得分:4)
删除所有这些导入:
import com.yourName.runmate.R;
然后重新同步您的gradle并重建您的项目。
另见这里:
"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,那么你就不会把它们从我能看到的地方拉得很好。
首先:
除了视图inflater之外,从onCreate和onCreateView(对于所有片段)中取出所有内容。
将所有额外代码放入onViewCreated或onActivityCreated。这样就不能在null视图上调用任何方法,因为这些方法在创建之后被调用。
其次,您需要理清您的活动以及您正在努力实现的目标。
您需要一个页面查看器和一个片段列表。页面浏览器需要与活动或活动片段相关联,而不是与片段相关联。否则,没有视图将页面浏览器页面附加到。
使用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,以确保您的活动已创建。您需要从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);
}
此代码只是一个指南,您需要对其进行调整。
如果有帮助,请告诉我。
这些Q&amp; As非常棒。
android Illegal state exception content view not yet create?
答案 4 :(得分: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;