Android - 垂直滚动视图中的水平滚动视图

4
在Android上,我想要实现以下缩略图滚动网格类型的滚动:
顶部两行水平滚动...整个页面包括顶部两行垂直滚动。
当水平滚动时,两个水平行都会动态加载其他图片...同时,随着垂直滚动,网格也会动态加载。(未知的总项目数)
我关心的问题是:
1.水平滑动和垂直滑动都可以很好地协同工作。
2.视图回收将适用于所有滚动视图集。
3.即使有8个水平行,底部网格不可见,它仍然能够被垂直滚动。
4.您会为屏幕上的每个部分推荐哪种视图(例如ListView、ScrollView等...)?
5.我可能需要处理哪些问题(例如将轻扫传递给父视图等)?
谢谢...我刚开始我的Android编码冒险!

样本网格视图
(来源: brianrice.com)

2个回答

5
你的长篇故事简单来说就是:在垂直RecyclerView中使用水平RecyclerView - 就像新的Google Play布局。

按照以下步骤操作:

步骤1

创建如下两个模型类。

SingleItemModel.java

package com.pratap.gplaystore.models;


public class SingleItemModel {


    private String name;
    private String url;
    private String description;


    public SingleItemModel() {
    }

    public SingleItemModel(String name, String url) {
        this.name = name;
        this.url = url;
    }


    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }


}

SectionDataModel.java

package com.pratap.gplaystore.models;

import java.util.ArrayList;


public class SectionDataModel {



    private String headerTitle;
    private ArrayList<SingleItemModel> allItemsInSection;


    public SectionDataModel() {

    }
    public SectionDataModel(String headerTitle, ArrayList<SingleItemModel> allItemsInSection) {
        this.headerTitle = headerTitle;
        this.allItemsInSection = allItemsInSection;
    }



    public String getHeaderTitle() {
        return headerTitle;
    }

    public void setHeaderTitle(String headerTitle) {
        this.headerTitle = headerTitle;
    }

    public ArrayList<SingleItemModel> getAllItemsInSection() {
        return allItemsInSection;
    }

    public void setAllItemsInSection(ArrayList<SingleItemModel> allItemsInSection) {
        this.allItemsInSection = allItemsInSection;
    }


}

步骤:2

创建一个带有RecyclerView的Activity,以垂直方式显示列表。

MainActivity.java

package com.pratap.gplaystore;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;

import com.pratap.gplaystore.adapters.RecyclerViewDataAdapter;
import com.pratap.gplaystore.models.SectionDataModel;
import com.pratap.gplaystore.models.SingleItemModel;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private Toolbar toolbar;


    ArrayList<SectionDataModel> allSampleData;


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

        toolbar = (Toolbar) findViewById(R.id.toolbar);

        allSampleData = new ArrayList<SectionDataModel>();

        if (toolbar != null) {
            setSupportActionBar(toolbar);
            toolbar.setTitle("G PlayStore");

        }


        createDummyData();


        RecyclerView my_recycler_view = (RecyclerView) findViewById(R.id.my_recycler_view);

        my_recycler_view.setHasFixedSize(true);

        RecyclerViewDataAdapter adapter = new RecyclerViewDataAdapter(this, allSampleData);

        my_recycler_view.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));

        my_recycler_view.setAdapter(adapter);


    }

    public void createDummyData() {
        for (int i = 1; i <= 5; i++) {

            SectionDataModel dm = new SectionDataModel();

            dm.setHeaderTitle("Section " + i);

            ArrayList<SingleItemModel> singleItem = new ArrayList<SingleItemModel>();
            for (int j = 0; j <= 5; j++) {
                singleItem.add(new SingleItemModel("Item " + j, "URL " + j));
            }

            dm.setAllItemsInSection(singleItem);

            allSampleData.add(dm);

        }
    }
}

请为上述活动类创建XML布局。
activity_main.xml
<?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="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        android:elevation="8dp"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />


    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="none" />


</LinearLayout>

步骤:3

现在,在MainActivity中为recyclerView创建一个适配器类。

RecyclerViewDataAdapter.java

package com.pratap.gplaystore.adapters;

/**
 * Created by pratap.kesaboyina on 24-12-2014.
 */

import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.pratap.gplaystore.R;
import com.pratap.gplaystore.models.SectionDataModel;

import java.util.ArrayList;

public class RecyclerViewDataAdapter extends RecyclerView.Adapter<RecyclerViewDataAdapter.ItemRowHolder> {

    private ArrayList<SectionDataModel> dataList;
    private Context mContext;

    public RecyclerViewDataAdapter(Context context, ArrayList<SectionDataModel> dataList) {
        this.dataList = dataList;
        this.mContext = context;
    }

    @Override
    public ItemRowHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_item, null);
        ItemRowHolder mh = new ItemRowHolder(v);
        return mh;
    }

    @Override
    public void onBindViewHolder(ItemRowHolder itemRowHolder, int i) {

        final String sectionName = dataList.get(i).getHeaderTitle();

        ArrayList singleSectionItems = dataList.get(i).getAllItemsInSection();

        itemRowHolder.itemTitle.setText(sectionName);

        SectionListDataAdapter itemListDataAdapter = new SectionListDataAdapter(mContext, singleSectionItems);

        itemRowHolder.recycler_view_list.setHasFixedSize(true);
        itemRowHolder.recycler_view_list.setLayoutManager(new LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false));
        itemRowHolder.recycler_view_list.setAdapter(itemListDataAdapter);


        itemRowHolder.btnMore.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {


                Toast.makeText(v.getContext(), "click event on more, "+sectionName , Toast.LENGTH_SHORT).show();



            }
        });


       /* Glide.with(mContext)
                .load(feedItem.getImageURL())
                .diskCacheStrategy(DiskCacheStrategy.ALL)
                .centerCrop()
                .error(R.drawable.bg)
                .into(feedListRowHolder.thumbView);*/
    }

    @Override
    public int getItemCount() {
        return (null != dataList ? dataList.size() : 0);
    }

    public class ItemRowHolder extends RecyclerView.ViewHolder {

        protected TextView itemTitle;

        protected RecyclerView recycler_view_list;

        protected Button btnMore;



        public ItemRowHolder(View view) {
            super(view);

            this.itemTitle = (TextView) view.findViewById(R.id.itemTitle);
            this.recycler_view_list = (RecyclerView) view.findViewById(R.id.recycler_view_list);
            this.btnMore= (Button) view.findViewById(R.id.btnMore);


        }

    }

}

现在为上面的适配器类创建一个xml布局文件。 list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"

    android:background="?android:selectableItemBackground"
    android:orientation="vertical"
    android:padding="5dp">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="2dp">


        <TextView
            android:id="@+id/itemTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_centerVertical="true"
            android:layout_gravity="center_vertical"
            android:layout_toLeftOf="@+id/btnMore"
            android:text="Sample title"
            android:textColor="@android:color/black"
            android:textSize="18sp" />

        <Button
            android:id="@+id/btnMore"
            android:layout_width="wrap_content"
            android:layout_height="42dp"
            android:layout_alignParentEnd="true"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:theme="@style/MyButton"
            android:text="more"
            android:textColor="#FFF" />


    </RelativeLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view_list"
        android:layout_width="match_parent"
        android:layout_height="160dp"
        android:layout_gravity="center_vertical"
        android:orientation="horizontal" />


</LinearLayout>

步骤:4

现在,为了创建一个水平的RecyclerView,我们需要为每一行创建一个布局和适配器类。

list_single_card.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView 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="horizontal"
    app:cardCornerRadius="5dp"
    app:cardUseCompatPadding="true"
    >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="0dp"
        android:background="?android:selectableItemBackground"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/itemImage"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_gravity="center_horizontal"
            android:scaleType="fitCenter"
            android:src="@drawable/android" />


        <TextView
            android:id="@+id/tvTitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/itemImage"
            android:gravity="center"
            android:padding="5dp"
            android:text="Sample title"
            android:textColor="@android:color/black"
            android:textSize="18sp" />


    </LinearLayout>

</android.support.v7.widget.CardView>

水平RecyclerView的适配器类

SectionListDataAdapter.java

package com.pratap.gplaystore.adapters;

/**
 * Created by pratap.kesaboyina on 24-12-2014.
 */

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.pratap.gplaystore.R;
import com.pratap.gplaystore.models.SingleItemModel;

import java.util.ArrayList;

public class SectionListDataAdapter extends RecyclerView.Adapter<SectionListDataAdapter.SingleItemRowHolder> {

    private ArrayList<SingleItemModel> itemsList;
    private Context mContext;

    public SectionListDataAdapter(Context context, ArrayList<SingleItemModel> itemsList) {
        this.itemsList = itemsList;
        this.mContext = context;
    }

    @Override
    public SingleItemRowHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_single_card, null);
        SingleItemRowHolder mh = new SingleItemRowHolder(v);
        return mh;
    }

    @Override
    public void onBindViewHolder(SingleItemRowHolder holder, int i) {

        SingleItemModel singleItem = itemsList.get(i);

        holder.tvTitle.setText(singleItem.getName());


       /* Glide.with(mContext)
                .load(feedItem.getImageURL())
                .diskCacheStrategy(DiskCacheStrategy.ALL)
                .centerCrop()
                .error(R.drawable.bg)
                .into(feedListRowHolder.thumbView);*/
    }

    @Override
    public int getItemCount() {
        return (null != itemsList ? itemsList.size() : 0);
    }

    public class SingleItemRowHolder extends RecyclerView.ViewHolder {

        protected TextView tvTitle;

        protected ImageView itemImage;


        public SingleItemRowHolder(View view) {
            super(view);

            this.tvTitle = (TextView) view.findViewById(R.id.tvTitle);
            this.itemImage = (ImageView) view.findViewById(R.id.itemImage);


            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {


                    Toast.makeText(v.getContext(), tvTitle.getText(), Toast.LENGTH_SHORT).show();

                }
            });


        }

    }

}

就这些,朋友们!

http://android-pratap.blogspot.com.ng/2015/12/horizontal-recyclerview-in-vertical.html


嗨,如果我想让整个RecyclerView具有水平滚动和垂直行,该怎么办?http://stackoverflow.com/questions/41675989/horizontal-and-vertical-scroll-in-recyclerview - Waseem Akram
我认为这不会按预期工作(除非我没有理解你的问题),即使它能够运行,这也不是一个好的设计布局,因为RecyclerView的主要本质现在已经被征服。你正在尝试实现的是在RecyclerView内部的GridView,如果你问我,那就有点乱七八糟了。 - The Billionaire Guy
我怎样才能获取第二行第三个项目的位置? - Arbaz Alam
在 MainActivity 中尝试运行这个逻辑(createDummyData() 函数)。如果 i = 2,那么它是第二行,j = 2 则为第三个项目。这样就是第二行的第三个项目,也就是第三行第二个项目。 - The Billionaire Guy

2
所以,对于您的前两个视图,我建议您使用HorizontalListView,然后对于其他项目,如果它们始终是6个正方形一行,则创建一些简单的具有水平方向和6个内部视图的LinearLayout。至于整个布局,请使用普通的ListView
ListView中使用HorizontalListView没有什么特别之处,因为它们在不同的轴上滚动,但是在适配器中,您需要实现getItemViewType(int position)getViewTypeCount()方法,因为您的布局中有两种不同类型的视图,使用这些方法,Android就知道要提供什么样的视图以供在getView(int position,View convertView,ViewGroup parent)中重复使用的convertView。 并且使用这种机制-视图回收将完美地工作,因为它最多会创建2个HorizontalListView和约8个其他视图项。

我遇到的问题是HorizontalListView似乎从ListView中夺取了垂直滑动... 在我有一个屏幕充满HorizontalListViews的情况下,我无法使其在垂直方向上滚动... 有没有办法让HorizontalListViews将垂直滑动传递给其父级? - Brian Rice
此外,我相信HorizontalListView没有视图回收的适配器...所以我需要使用Gallery吗? - Brian Rice
嗯,是的,我刚想起来当我有一个HorizontalScrollView和普通视图的组合时,我使用普通视图滚动ListView,而且是的,HorizontalScrollView正在窃取触摸事件。你可以尝试自定义一下HorizontalScrollView并覆盖onTouch事件,使其始终返回false,这样你也会将触摸事件传递给ListView。 - Desert
现在谈论视图回收,是的,你说得对它不会回收视图,你可以使用Gallery,但这是一个已弃用的API,所以你应该问自己一个问题,是否真的有那么多项目在一行中,需要水平回收? - Desert
好的,我测试了从onTouch返回false,但是它没有帮助 :) 所以应该还有其他问题。 - Desert
我可以建议尝试将所有从HorizontallScrollView调用重定向到ListView。 - Desert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接