单击关联的 ListView 项目后显示 ViewPager 的正确方法是什么?

What's the correct way of displaying ViewPager after associated ListView's item click?

我是 Android 的初学者,所以我对所犯的错误深表歉意,如果有任何建设性的批评,我将不胜感激。

我正在编写一个带有图像 ListView 的基本应用程序,当用户单击列表中的项目时,我想在 ViewPager 中显示该图像,用户可以在其中来回滑动以浏览整个图像列表。之后当用户按下后退按钮时,我想切换回 ListView。

我在 MainActivity 中管理业务逻辑,它对 ListView 使用 MainActivityFragment,对 ViewPager 使用 ImageHolderFragment。

目前简化后的代码如下:

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

    mListItems = new ArrayList<>();
    mListItemAdapter = new ListItemAdapter(this, R.layout.list_item, R.id.list_item_name, mListItems);
    mListView = (ListView) findViewById(R.id.list_view_content);
    mListView.setAdapter(mListItemAdapter);

    mDeletedListItems = new ArrayList<>();

    mViewPager = (ViewPager) getLayoutInflater().inflate(R.layout.image_display, null, true);
    mImageAdapter = new ImageAdapter(getSupportFragmentManager(), mListItems);
    mViewPager.setAdapter(mImageAdapter);
    mViewPager.setOffscreenPageLimit(3);

    mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                mViewPager.setCurrentItem(position);
                setContentView(mViewPager);  // TODO: this is very wrong!
            }
        });

    loadImages();
    noContentText = (TextView) findViewById(R.id.no_content_text);
    if (mListItems.isEmpty()) {
        noContentText.setText(R.string.no_images);
    } else {
        mImageAdapter.notifyDataSetChanged();
    }
}

虽然这在某种程度上确实有效,这意味着它在单击列表中的项目时设法显示 ViewPager,但它有两件事敲响了警钟:

如果我的想法完全错误,如果我的情况没有希望,我将不胜感激任何帮助和解释,我希望看到 ListView 和 ViewPager 的成功组合,并在彼此之间进行转换。

I've read that calling setContentView() for the second time in the same class is pretty much a sin. Nobody explained me why.

好吧,你大概知道为什么了。 当您使用 setContentView() 显示另一个 'screen' 时,您没有正确的返回堆栈。 您还保留对不再可见的视图(如 mListView)的引用,因此在您第二次 setContentView() 之后有点 'useless'。 还要记住方向变化或你的应用程序进入后台 - 你必须跟踪你的 Activity 所处的状态,这比你有一个 [=26] 时所处的状态要复杂得多=] 做两件不同的事情。

你不会因为像现在这样做而被捕,但调试和保持无错误更难。

我建议针对您想做的两件不同的事情使用两个不同的 Activity,或者使用一个 Activity 和两个 Fragment,来回交换它们。

如果你坚持把所有的东西都放在一起 Activity 你需要覆盖 onBackPressed() (当用户按下后退按钮时调用)并恢复你的 Activity 的第一个状态(再次 setContentView(),几乎从头开始)。

您的 activity 已经将 R.layout.activity_main 设置为内容视图,它可以正确显示列表视图 - 这就是您定义的 activity 的责任。如果我们想要更改屏幕上显示的内容,我们应该使用构建块的不同实例(activity 或片段)来显示视图寻呼机图像。

至少可以想象一下,如果您想将视图更改为第三项功能或 UI,或第四项...这将是维护、扩展和测试的噩梦' 没有将功能分成可管理的单元。一个视图中需要的字段与另一个视图中需要的字段混合在一起,您的 class 文件会随着每个视图带来其点击侦听器、回调等而变得越来越大,您还必须覆盖后退按钮所以它会做你想做的事——这不是 Android 框架旨在帮助你的方式。如果您想在不同的上下文中重用 UI 组件,同时利用框架的 activity 生命周期回调怎么办?这就是引入 fragments 的原因。

在您的情况下,列表视图可以在您的 MainActivity 中继续 运行,在您的点击侦听器中,onItemClick 您可以开始一个新的 activity持有 viewPager:

Intent i = new Intent(MainActivity.this, MyLargePhotoActivityPager.class);
i.putExtra(KEY_POSITION, position);
// pass the data too
startActivityForResult(i, REQUEST_CODE);

注意如何将位置作为 int 额外传递给此 activity,以便第二个 activity 将 viewPager 很好地设置到用户单击的位置在。我将让您了解如何构建第二个 activity 并将 ViewPager 放在那里。假设您的 launch modes are set accordingly, if needed. One thing to note is that when you do come back to the list View, you'd probably want to scroll to the position from the view pager, which is why you could supply that back as a result via a request code,您还可以获得后退按钮功能。返回的位置可以提供回列表视图。

或者,您可以使用相同的 activity 但有两个片段(请参阅上面的 link)并获得相同的结果。事实上,您的一个片段可以存储列表视图,第二个片段可以是一个全屏 DialogFragment,它存储一个 viewPager,就像一个照片库(一些细节 here)。

希望这对您有所帮助。