简体   繁体   English

片段错误中的Android getListView()

[英]Android getListView() in fragment error

I keep having an issue with my android app where it is crashing with the following error when swiping between tabs: 我的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)

This is the MyRunsFragment: 这是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();
        }
    };

}

And the fragment_my_runs layout file: 和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>

The TabFragmentContainer 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;
    }

}

The FragmentPagerAdapter: 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;
    }
}

The tab_layout_fragment_container file that contains the ViewPager widget: 包含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>

The onCreate() method in my MainActivity: 我的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();
}

I have followed other answers here and added the getListView() functionality to the onViewCreated() method but the problem still persists... Can anyone point out where I might be going wrong? 我在这里已经按照其他答案并将getListView()函数添加到onViewCreated()方法但问题仍然存在...有谁可以指出我可能出错的地方?

onViewCreated is called immediately after onCreateView , but the super.onViewCreated call is missing, perhaps this is root cause of your issue. 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();
}

Based on these facts: 基于这些事实:

  • The exception is thrown because there is no root view yet when done() calls getListView() . 抛出异常是因为当done()调用getListView()没有根视图。
  • done() is called when the query made by retrieveAcceptedRoutes() gets a response. retrieveAcceptedRoutes()的查询获得响应时,将调用done()
  • retrieveAcceptedRoutes is called in multiple places, including the OnRefreshListener mOnRefreshListener , which is registered as the refresh listener in onCreateView() before there is a root view (that is, before onCreateView() returns). retrieveAcceptedRoutes在多个地方被调用,包括OnRefreshListener mOnRefreshListener ,它有根视图之前(即在onCreateView()返回之前onCreateView()onCreateView()注册为刷新侦听器。

...it is possible for getListView() to be called before there is a root view. ...在有根视图之前,可以调用getListView()

Try moving these 3 statements from onCreateView() to onViewCreated() , so that way the refresh listener can only be called when there is a root view. 尝试将这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);

I read your question again then I guess that: 我再次阅读你的问题然后我猜:

  • Your ListFragment is destroyed while your background task keeps running. 您的ListFragment在后台任务继续运行时被销毁。 So when it's done, your callback would like to update the ListView which is no longer alive. 所以当它完成后,你的回调想要更新不再存在的ListView。

  • Actually, viewPager.setOffscreenPageLimit(3); 实际上, viewPager.setOffscreenPageLimit(3); may do the trick, but it's not a good practice. 可以做到这一点,但这不是一个好习惯。 It forces your ViewPager to create and store more Fragments in memory which is not necessary. 它会强制您的ViewPager在内存中创建和存储更多碎片,这是不必要的。 That's why I down vote that answer. 这就是我拒绝投票的原因。 You can solve this without doing so. 你可以不用这样解决这个问题。

What you should do: one of the following two practice should be fine, or both: 你应该做什么:以下两种做法之一应该没问题,或两者兼而有之:

  • Destroy your task in your onPause or whatever lifecycle method, before your onDestroyView . onDestroyView之前,在onPause或任何生命周期方法中销毁你的任务。

  • Exclude the code where you update your ListView inside your done() method. 排除在done()方法中更新ListView的代码。 Make it a local method where you will check your ListView carefully, and there, you should ask your update process to run on UI thread to avoid any threading problem. 使它成为一个本地方法,您将仔细检查ListView,在那里,您应该要求您的更新过程在UI线程上运行,以避免任何线程问题。 Make sure to check if your getView() is not null (but not your getListView() , since it throws Exception if getView() returns null). 确保检查你的getView()是否为null(但不是你的getListView() ,因为如果getView()返回null,它会抛出Exception)。

I recommend you to use both of them to make sure: your view is still useable and you don't waste your resource when running task in invisible fragment. 我建议您使用它们来确保:您的视图仍然可用,并且在以invisible片段运行任务时不会浪费资源。 Don't forget that by default , once your fragment is invisible, it is considered to be destroyed (not always, for example ViewPager keep reference of 2 fragments, but keep in mind that case). 不要忘记, 默认情况下 ,一旦你的片段不可见,它就被认为是被破坏的 (并非总是如此,例如ViewPager保留对2个片段的引用,但请记住这种情况)。

Hope this help. 希望这有帮助。

remove all these imports: 删除所有这些导入:

import com.yourName.runmate.R;

Then resync your gradle and rebuild your project. 然后重新同步您的gradle并重建您的项目。

在此输入图像描述

Also see here: 另见:
"cannot resolve symbol R" in Android Studio Android Studio中“无法解析符号R”

edit 编辑

Your first obvious mistake in your Main is 你主要的第一个明显错误是

mFragmentManager = getSupportFragmentManager();

should be: 应该:

mFragmentManager = getFragmentManager();

or change your Main activity to: 或将您的主要活动更改为:

MainActivity extends FragmentActivity to make use of the support fragment manager. MainActivity extends FragmentActivity以使用支持片段管理器。


You have a lot of unnecessary code in your question, majority of comments can be removed and imports for the purpose of this question. 您的问题中有很多不必要的代码,大多数注释都可以删除并导入以用于此问题。

What I have come up with is there is no activity, being used. 我想出的是没有活动,被使用。 The ListFragment needs to be attached to an Activity or you are trying to call that activity view before it is created. 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)

If you are using Main, then you are not pulling them together well, from what I can see. 如果你使用的是Main,那么你就不会把它们拉得很好,从我能看到的。

Firstly: 首先:

Take everything out of your onCreate and onCreateView (for all fragments) except the view inflater. 除了视图inflater之外,从onCreate和onCreateView(对于所有片段)中取出所有内容。

Place all the extra code into either onViewCreated or onActivityCreated. 将所有额外代码放入onViewCreated或onActivityCreated。 That way no methods can be called on a null view, as these are called after it is created. 这样就不能在null视图上调用任何方法,因为这些方法在创建后被调用。

Secondly, you need to sort out your activities and with what you're exactly trying to achieve. 其次,你需要理清你的活动以及你正在努力实现的目标。

You want a page viewer and a fragmentlist. 您需要页面查看器和片段列表。 The pageviewer needs to be associated with an activity, or activity fragment, not a fragment. 页面浏览器需要与活动或活动片段相关联,而不是与片段相关联。 Otherwise there is no view to attach the pageviewer pages to. 否则,没有视图将页面浏览器页面附加到。

Use a FragmentActivity not a Fragment. 使用FragmentActivity而不是Fragment。 To be the activity you run your ListFragment from. 要成为您运行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);

    }
}

I would suggest putting this into your ListFragment, to ensure your activity is created. 我建议将其放入ListFragment,以确保您的活动已创建。 You will need to move most of your code from your onCreate methods and put them in onViewCreated or onActivityCreated 您需要从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);
}

This code is just a guide, you'll need to tweak it. 这段代码只是一个指南,你需要调整它。

Let me know if this helps. 如果这有帮助,请告诉我。

These Q&As are excellent. 这些问答非常好。

Content view not yet created 尚未创建的内容视图

android Illegal state exception content view not yet create? android非法状态异常内容视图尚未创建?


Fragment same principles applies to viewpager fragments ViewPager Fragment相同的原则适用于viewpager片段ViewPager

Try to declare: 尝试声明:

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

before: 之前:

    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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM