【问题标题】:How to build a complex layout in android with multiple listviews如何在具有多个列表视图的 android 中构建复杂的布局
【发布时间】:2013-12-02 23:37:47
【问题描述】:

我们正在开发一个包含多个组件的应用程序,我们希望将这些组件集成到帖子的详细视图中(请参阅模型。)每个部分都是可扩展的(通常通过 ajax 调用)并且整个屏幕都可以滚动查看所有内容。

我们不确定解决此问题的最佳方法,因为根据 Google 的说法,不应将列表视图放入滚动视图中。似乎有人在这样做,就像How can I put a ListView into a ScrollView without it collapsing? 一样。

一般而言,帖子中的内容量足够小,可以一次全部膨胀,但可能会收到包含 100 多张照片或 300 多张图片的帖子,而内存可能是个问题。

p>

我的问题是,如果不是官方的,那么构建这种布局的最佳方式是什么。我有兴趣听取有关整个布局结构的建议,因此我了解标题等更静态的内容如何与照片或 cmets 一起播放。

我想优化小型帖子(几张照片,20-50 cmets)的性能/易于实施 - 但我们必须能够处理大型帖子而不会导致应用程序崩溃。

【问题讨论】:

  • 注意 - 我在问题中提到了列表视图 - 但它可能是 relativeLayouts 会更好 - 我只是更喜欢可以通过适配器之类的东西轻松添加新元素的东西。
  • 尝试使用 listview 和 addHeaderView() 或 addFooterView() 实现..
  • @Haresh - 这里有多个潜在的列表视图 - 你能详细说明一下吗?

标签: android android-layout listview


【解决方案1】:

一开始以为是平板的复杂布局,但如果只有1个列表,可以使用自定义列表适配器来实现。

将此布局想象为具有不同视图的单个列表。所以帖子标题是类型 1 的列表项,照片视图是类型 3,按钮是类型 4。这是您的布局,项目位置用红色数字标记,视图类型用蓝色文本标记。

您应该使用viewType 属性为每个项目创建模型类,因此对于 11 种视图类型,将有 11 个类(或一个基类和几个派生类)。 然后以正确的顺序将这些模型添加到您的适配器,如下所示:

    this.add(new ListItemModelBase(ListItemViewTypes.POST_TITLE));
    this.add(new PhotoViewItemModel("image1.jpg", "Image 1"));
    this.add(new PhotoViewItemModel("image2.jpg", "Image 2"));
    this.add(new PhotoViewItemModel("image3.jpg", "Image 3"));
    this.add(new ListItemModelBase(ListItemViewTypes.SEE_MORE_PHOTOS_BUTTON));
    // other models

然后您可以通过更改模型来更新/添加/删除列表项。例如,如果您单击“查看更多文本”按钮,则调用以下函数:

private void onSeeMoreTextButtonClicked(ListItemModelBase item){
    item.setViewType(ListItemViewTypes.POST_TITLE_EXPANDED);
    this.notifyDataSetChanged();
}

如果你点击“查看更多照片”按钮,调用这个函数:

private void onSeeMorePhotosButtonClicked(ListItemModelBase item){      
    int index = this.getPosition(item);

    this.remove(item);
    this.insert(new PhotoViewItemModel("image4.jpg", "Image 4"), index);
    this.insert(new PhotoViewItemModel("image5.jpg", "Image 5"), index + 1);
    this.insert(new PhotoViewItemModel("image6.jpg", "Image 6"), index + 2);
}

我认为这个想法很明确。

以下是 4 种视图类型的完整示例:

public class ListItemViewTypes {
    public static final int VIEW_TYPES_COUNT = 4;

    // Must start from 0
    public static final int POST_TITLE = 0;
    public static final int POST_TITLE_EXPANDED = 1;
    public static final int PHOTO_VIEW = 2;
    public static final int SEE_MORE_PHOTOS_BUTTON = 3;
}

public class ListItemModelBase {
    private int mViewType;

    public ListItemModelBase(int viewType){
        mViewType = viewType;
    }

    public int getViewType() {
        return mViewType;
    }

    public void setViewType(int viewType) {
        mViewType = viewType;
    }
}

public class PhotoViewItemModel extends ListItemModelBase {
    private final String mUrl;
    private final String mDescription;

    public PhotoViewItemModel(String url, String description) {
        super(ListItemViewTypes.PHOTO_VIEW);
        mUrl = url;
        mDescription = description;
    }

    public String getUrl() {
        return mUrl;
    }

    public String getDescription() {
        return mDescription;
    }
}

public class TestListAdapter extends ArrayAdapter<ListItemModelBase> {
    private final LayoutInflater mInflater;

    public TestListAdapter(Context context) {
        super(context, -1);

        mInflater = LayoutInflater.from(context);

        this.add(new ListItemModelBase(ListItemViewTypes.POST_TITLE));
        this.add(new PhotoViewItemModel("image1.jpg", "Image 1"));
        this.add(new PhotoViewItemModel("image2.jpg", "Image 2"));
        this.add(new PhotoViewItemModel("image3.jpg", "Image 3"));
        this.add(new ListItemModelBase(ListItemViewTypes.SEE_MORE_PHOTOS_BUTTON));
    }

    @Override
    public int getViewTypeCount() {
        return ListItemViewTypes.VIEW_TYPES_COUNT;
    }

    @Override
    public int getItemViewType(int position) {
        // important method so that convertView has a correct type
        return this.getItem(position).getViewType();
    }

    @Override
    public boolean isEnabled(int position) {
        // enable context menu for photo views, comments and related posts
        // or always return false if context menu is not needed at all 
        if (this.getItem(position).getViewType() == ListItemViewTypes.PHOTO_VIEW) {
            return true;
        }

        return false;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        final ListItemModelBase item = this.getItem(position);
        int viewType = item.getViewType();

        if (convertView == null) {
            // different layouts for different `viewType`
            int layoutId = -1;
            if (viewType == ListItemViewTypes.POST_TITLE) {
                layoutId = R.layout.post_title;
            } else if (viewType == ListItemViewTypes.POST_TITLE_EXPANDED) {
                layoutId = R.layout.post_title_expanded;
            } else if (viewType == ListItemViewTypes.PHOTO_VIEW) {
                layoutId = R.layout.photo_view;
            } else if (viewType == ListItemViewTypes.SEE_MORE_PHOTOS_BUTTON) {
                layoutId = R.layout.more_photos_button;
            }

            convertView = this.mInflater.inflate(layoutId, null);
        }

        // update the current view based on data from the current model
        if (viewType == ListItemViewTypes.POST_TITLE) {
            Button seeMoreTextButton = (Button)convertView.findViewById(R.id.button_see_more_text);
            seeMoreTextButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onSeeMoreTextButtonClicked(item);
                }
            });
        } else if (viewType == ListItemViewTypes.SEE_MORE_PHOTOS_BUTTON) {
            Button seeMorePhotosButton = (Button)convertView.findViewById(R.id.button_see_more_photos);
            seeMorePhotosButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onSeeMorePhotosButtonClicked(item);
                }
            });
        } else if (viewType == ListItemViewTypes.PHOTO_VIEW){
            PhotoViewItemModel photoViewItem = (PhotoViewItemModel)item;
            TextView photoDescriptionText = (TextView)convertView.findViewById(R.id.photo_view_text);
            photoDescriptionText.setText(photoViewItem.getDescription());
        }

        return convertView;
    }

    private void onSeeMoreTextButtonClicked(ListItemModelBase item){
        item.setViewType(ListItemViewTypes.POST_TITLE_EXPANDED);
        this.notifyDataSetChanged();
    }

    private void onSeeMorePhotosButtonClicked(ListItemModelBase item){      
        int index = this.getPosition(item);

        this.remove(item);
        this.insert(new PhotoViewItemModel("image4.jpg", "Image 4"), index);
        this.insert(new PhotoViewItemModel("image5.jpg", "Image 5"), index + 1);
        this.insert(new PhotoViewItemModel("image6.jpg", "Image 6"), index + 2);
    }
}

public class MainActivity extends ListActivity {

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

        this.getListView().setChoiceMode(ListView.CHOICE_MODE_NONE);
        this.getListView().setDivider(null);

        TestListAdapter adapter = new TestListAdapter(this.getApplicationContext());
        this.getListView().setAdapter(adapter);
    }
}

【讨论】:

  • 谢谢!这是我们迄今为止采取的方法。起初我对这种方法非常怀疑——感觉非常不雅。它的一个优点是,如果列表很长(例如,几十张照片,数百个 cmets),由于列表视图,您仍然可以获得良好的性能。我暗中希望有一个不同的解决方案 - 但我很欣赏这种同意的方法。
  • 感谢您提供详细信息/示例!
  • @Yenosef 这是在性能方面有效处理大型列表的唯一方法。另一种解决方案是使用WebView 并使用html/css/javascript 开发UI,但Web UI 往往比原生UI 差。
【解决方案2】:

看到您的要求,我可以想到这 3 种可能的解决方案:

  1. 使用可扩展列表视图 - 我认为这将帮助您查看上面的图片,请看这里 - http://www.androidhive.info/2013/07/android-expandable-list-view-tutorial/

  2. 通过代码编写动态布局,点击新建布局。

  3. 使用 getViewTypeCount() 方法在列表视图中使用不同的行项目。更多信息在这里 - http://android.amberfog.com/?p=296 Android ListView with different layouts for each rowhttp://www.jiahaoliuliu.com/2012/08/android-list-view-with-different-views.html

我个人认为您使用 1 和 3 的组合来解决您当前的问题

我强烈建议不要在列表视图中使用列表视图或在滚动视图中使用列表视图。它们将是大量的性能问题和其他你真的不想涉及的东西。希望这会有所帮助。

【讨论】:

  • 谢谢 - 可扩展的列表视图是否为我提供了具有不同视图的单个列表视图所没有的任何东西? (无论如何,我必须有多种视图类型和可扩展的列表视图 - 不是吗?)
  • 我假设您想要列表视图的可扩展选项,普通列表视图不会像可扩展列表视图那样为您提供开箱即用的展开和关闭功能。如果我没有正确理解您的要求,请原谅我。
  • 我明白了 - 你在谈论动画?这对我们的需求并不重要。另外-我不知道这是否重要-但是对于大多数组件,在我发出 api 请求之前我不会知道“可扩展”元素(它不会被预加载)-不确定这对于可扩展列表是否重要.
  • 呃,正如我提到的不好,我认为那些第二个元素是在你展开第一个列表之后显示的。可扩展列表行不通:(我相信 vorrtex 的答案是最接近的!
【解决方案3】:

使用不同适配器的自定义列表视图将解决您的问题

【讨论】:

  • 你能详细说明一下吗,我不确定我是否遵循。你的意思是有多个列表视图或一个列表视图将所有不同的部分(帖子文本、成员、照片、cmets、相关帖子)?
【解决方案4】:

图形布局可以轻松解决问题。 首先是垂直布局,然后是带有小部件的相对布局 滚动项的属性。

【讨论】:

    猜你喜欢
    • 2020-10-29
    • 1970-01-01
    • 2016-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多