带有动态物品高度的水平RecyclerView [英] Horizontal RecyclerView with dynamic item’s Height

查看:92
本文介绍了带有动态物品高度的水平RecyclerView的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试通过水平滚动实现 RecyclerView ,所以我在水平方向使用了 LinearLayoutManager .问题是我要使用两种不同类型的高度不同的物品来填充RecyclerView.这是我用于该商品的布局:

 <?xml version ="1.0"encoding ="utf-8"?< FrameLayoutxmlns:android ="http://schemas.android.com/apk/res/android"xmlns:tools ="http://schemas.android.com/tools"xmlns:app =" http://schemas.android.com/apk/res-autoandroid:layout_width =" wrap_content"android:layout_height =" wrap_content"android:layout_marginEnd =" 10dp">< LinearLayoutandroid:id =" @ + id/document_view"android:layout_width =" wrap_content"android:layout_height =" wrap_content"android:orientation =水平"android:background =" @ drawable/ic_rounded"android:backgroundTint =" @ color/ms_black_ms_gray"android:gravity ="center"android:layout_gravity ="bottom"android:padding ="5dp"android:paddingStart =" 15dp">< TextViewandroid:id =" @ + id/name"android:layout_width =" wrap_content"android:layout_height =" wrap_content"android:textColor =" @ color/white"android:textSize =" 13sp"android:singleLine =" true"android:maxWidth ="80dp"工具:text ="example_form"/>< TextViewandroid:id ="@@ id/format"android:layout_width =" wrap_content"android:layout_height =" wrap_content"android:textColor =" @ color/white"android:textSize =" 13sp"/>…</LinearLayout>< android.support.v7.widget.CardViewandroid:id ="@@ id/image_view";android:layout_width ="120dp";android:layout_height =" 80dp"android:layout_gravity ="bottom"app:cardCornerRadius ="25dp"app:cardElevation ="0dp"< RelativeLayoutandroid:layout_width =" match_parent"android:layout_height =" match_parent">< ImageViewandroid:id =" @ + id/preview_image"android:layout_width =" match_parent"android:layout_height =" match_parent"android:scaleType =" fitXY"/>…</RelativeLayout></android.support.v7.widget.CardView> 

,这是包含 RecyclerView 的布局,基本上是这样的:

 < LinearLayoutandroid:layout_width =" match_parent"android:layout_height =" wrap_content"android:orientation =水平"android:paddingStart =" 14dp"android:paddingEnd ="14dp">< ImageViewandroid:id ="@@ id/attach"android:layout_width =" wrap_content"android:layout_height =" wrap_content"android:padding ="10dp"android:layout_gravity ="bottom"android:layout_marginBottom =" 19dp"android:visibility ="visible"/>< LinearLayoutandroid:layout_width =" match_parent"android:layout_height =" wrap_content"android:layout_marginStart =" 5dp"android:layout_marginBottom =" 10dp"android:layout_marginTop =" 5dp"android:padding ="3dp"android:foreground =" @ drawable/ic_rounded_strokeandroid:foregroundTint =" @ color/white">< android.support.constraint.ConstraintLayoutandroid:id ="@@ id/chatEdit"android:layout_width =" match_parent"android:layout_height =" wrap_content"android:background =" @ drawable/ic_rounded"android:foreground =" @ drawable/ic_rounded_strokeandroid:padding ="6dp"android:visibility ="visible">< EditTextandroid:id ="@@ id/editText"android:textSize =" 17sp"android:textColor ="#121212android:letterSpacing =-0.02"android:lineSpacingExtra =" 0sp"android:padding ="10dp"android:paddingStart =" 15dp"android:layout_width =" 0dp"android:layout_height =" wrap_content"android:maxLines ="5"android:hint =" @ string/chat_hint"android:inputType =" textCapSentences | textMultiLineandroid:maxLength ="2500"android:background ='"@ null"app:layout_constraintRight_toLeftOf =" @ id/buttonsContainer"app:layout_constraintLeft_toLeftOf ="parent"app:layout_constraintTop_toTopOf =" parent"/>< TextViewandroid:id ="@@ id/send"android:layout_gravity ="bottom"android:visibility =" visible"android:paddingLeft =" 15dp"android:paddingRight ="15dp"android:paddingBottom =" 10dp"android:paddingTop =" 8dp"android:layout_width =" wrap_content"android:layout_height =" wrap_content"android:textSize =" 18sp"android:textColor =#ffffff"android:letterSpacing =-0.02"android:gravity ="center_horizo​​ntal"android:text =" @ string/send"app:layout_constraintRight_toRightOf ="parent"app:layout_constraintBottom_toBottomOf =" parent"/>< android.support.v7.widget.RecyclerViewandroid:id ="@@ id/filesList"android:layout_width =" 0dp"android:layout_height =" match_parent"android:layout_marginTop =" 10dp"android:paddingTop =" 5dp"android:paddingEnd =" 5dp"android:visibility ="gone"app:layout_constraintRight_toLeftOf =" @ id/send"app:layout_constraintLeft_toLeftOf ="parent"app:layout_constraintTop_toBottomOf =" @ id/editText"app:layout_constraintBottom_toBottomOf =" parent"/></android.support.constraint.ConstraintLayout></LinearLayout></LinearLayout> 

我正在使用一个 ViewHolder ,我只是更改了两个子视图的可见性.

我希望得到的结果是这样的:

但是我得到的是这个;使用第二种类型的高度将CardView切成两半:

我看到了

但是,如果我先选择3个pdf,然后选择图片,则会发生这种情况:

有什么办法解决这个问题吗?

解决方案

我在另一个项目中也遇到了类似的问题,我通过使用Google库FlexboxLayoutManager解决了该问题.

  1. 获取最新的FlexboxLayoutManager库( https://github.com/google/flexbox-layout),并将其添加到您的高级依赖项中(实现'com.google.android:flexbox:2.0.1')
  2. 在活动"中添加以下代码行:

      FlexboxLayoutManager layoutManager = new FlexboxLayoutManager(this);layoutManager.setFlexDirection(FlexDirection.ROW);layoutManager.setFlexWrap(FlexWrap.NOWRAP);recyclerView.setLayoutManager(layoutManager);  

  3. 要使FlexboxLayoutManager与水平滚动配合使用,请在BaseViewHolder类的适配器(FilesAdapter)中添加以下代码:

     抽象静态类BaseViewHolder扩展了RecyclerView.ViewHolder {公共BaseViewHolder(@NonNull查看itemView){超级(itemView);ViewGroup.LayoutParams lp = itemView.getLayoutParams();如果(lpbox of FlexboxLayoutManager.LayoutParams){FlexboxLayoutManager.LayoutParams flexboxLp =(FlexboxLayoutManager.LayoutParams)lp;flexboxLp.setFlexShrink(0.0f);flexboxLp.setAlignSelf(AlignItems.FLEX_START);//这将使每个itemView在顶部对齐,或使用AlignItems.FLEX_END在底部对齐}}抽象void绑定(文件文件);}  

I’m trying to implement a RecyclerView with horizontal scrolling, so I’m using this a LinearLayoutManager with horizontal orientation. The problem is that I’m populating the RecyclerView using 2 different types of items, with different heights. This is the layout I’m using for the item:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp">

<LinearLayout
    android:id="@+id/document_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:background="@drawable/ic_rounded"
    android:backgroundTint="@color/ms_black_ms_gray"
    android:gravity="center"
    android:layout_gravity="bottom"
    android:padding="5dp"
    android:paddingStart="15dp">

    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/white"
        android:textSize="13sp"
        android:singleLine="true"
        android:maxWidth="80dp"
        tools:text="example_form"/>

    <TextView
        android:id="@+id/format"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/white"
        android:textSize="13sp" />

    …
</LinearLayout>

<android.support.v7.widget.CardView
    android:id="@+id/image_view"
    android:layout_width="120dp"
    android:layout_height="80dp"
    android:layout_gravity="bottom"
    app:cardCornerRadius="25dp"
    app:cardElevation="0dp">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:id="@+id/preview_image"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="fitXY"/>

        …

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

and this is the layout that contains the RecyclerView, which is basically like this:

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:paddingStart="14dp"
        android:paddingEnd="14dp">

        <ImageView
            android:id="@+id/attach"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:layout_gravity="bottom"
            android:layout_marginBottom="19dp"
            android:visibility="visible"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="5dp"
            android:layout_marginBottom="10dp"
            android:layout_marginTop="5dp"
            android:padding="3dp"
            android:foreground="@drawable/ic_rounded_stroke"
            android:foregroundTint="@color/white">
            <android.support.constraint.ConstraintLayout
                android:id="@+id/chatEdit"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@drawable/ic_rounded"
                android:foreground="@drawable/ic_rounded_stroke"
                android:padding="6dp"
                android:visibility="visible">

                <EditText
                    android:id="@+id/editText"
                    android:textSize="17sp"
                    android:textColor="#121212"
                    android:letterSpacing="-0.02"
                    android:lineSpacingExtra="0sp"
                    android:padding="10dp"
                    android:paddingStart="15dp"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:maxLines="5"
                    android:hint="@string/chat_hint"
                    android:inputType="textCapSentences|textMultiLine"
                    android:maxLength="2500"
                    android:background="@null"
                    app:layout_constraintRight_toLeftOf="@id/buttonsContainer"
                    app:layout_constraintLeft_toLeftOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />


                    <TextView
                        android:id="@+id/send"
                        android:layout_gravity="bottom"
                        android:visibility="visible"
                        android:paddingLeft="15dp"
                        android:paddingRight="15dp"
                        android:paddingBottom="10dp"
                        android:paddingTop="8dp"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:textSize="18sp"
                        android:textColor="#ffffff"
                        android:letterSpacing="-0.02"
                        android:gravity="center_horizontal"
                        android:text="@string/send"
                        app:layout_constraintRight_toRightOf="parent"
                        app:layout_constraintBottom_toBottomOf="parent"
                        />

                <android.support.v7.widget.RecyclerView
                    android:id="@+id/filesList"
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_marginTop="10dp"
                    android:paddingTop="5dp"
                    android:paddingEnd="5dp"
                    android:visibility="gone"
                    app:layout_constraintRight_toLeftOf="@id/send"
                    app:layout_constraintLeft_toLeftOf="parent"
                    app:layout_constraintTop_toBottomOf="@id/editText"
                    app:layout_constraintBottom_toBottomOf="parent"/>
            </android.support.constraint.ConstraintLayout>
        </LinearLayout>
    </LinearLayout>

I’m using a single ViewHolder, I just change the visibility of the 2 child views.

The result I expect to get is this one:

But what I’m getting is this; the CardView being cut in half, using the height of the second type of item:

I saw this post, which is similar to my problem. It recommends using Google’s Flexbox. So, I tried to implement FlexboxLayoutManager:

FlexboxLayoutManager layoutManager = new FlexboxLayoutManager(getContext());
layoutManager.setFlexDirection(FlexDirection.ROW);
layoutManager.setFlexWrap(FlexWrap.NOWRAP);

I’m using row direction and It is showing items on next lines if it does not fit in single line. So, I also added No_wrap. And now it is showing items in a single line but do not provide scrolling. Also in this case it tries to fit all items in a single line by decreasing width of items.

I also played with the flex box sample app, but I couldn’t get the result I want.

Is there a way I can achieve horizontal scrolling with the Flexbox integrated with RecyclerView? Or should I use a different approach?

Thanks

EDIT

Thanks for the tips and everything, but it is not solving it. So, I stripped down the code to bare minimum to reproduce this.

MainActivity:

public class MainActivity extends AppCompatActivity {

private static final int REQUEST_CODE = 1;

private RecyclerView recyclerView;
private FilesAdapter filesAdapter;
private List<File> filesList = new ArrayList<>();

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

    recyclerView = findViewById(R.id.recyclerView);
    LinearLayoutManager filesLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
    recyclerView.setLayoutManager(filesLayoutManager);
    filesAdapter = new FilesAdapter(filesList);
    recyclerView.setAdapter(filesAdapter);

    ImageView attach = findViewById(R.id.attach);
    attach.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent = new Intent();
            intent.setType("*/*");
            intent.addCategory(Intent.CATEGORY_OPENABLE);
            intent.setAction(Intent.ACTION_GET_CONTENT);
            intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
            startActivityForResult(Intent.createChooser(intent,"Select Files"), REQUEST_CODE);
        }
    });
}

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
        try {
            if (data != null) {
                List<File> uriList = new ArrayList<>();
                if (data.getClipData() != null) { // Multiple files
                    for (int i = 0; i < data.getClipData().getItemCount(); i++) {
                        Uri uri = data.getClipData().getItemAt(i).getUri();
                        Pair<Boolean, File> isValid = isFileValid(uri);
                        if (isValid.first) {
                            uriList.add(isValid.second);
                        }
                    }
                } else { // Single file
                    Uri uri = data.getData();
                    Pair<Boolean, File> isValid = isFileValid(uri);
                    if (isValid.first) {
                        uriList.add(isValid.second);
                    }
                }

                if (uriList.size() > 0) {
                    for (File file : uriList) {
                        filesList.add(filesList.size(), file);
                        filesAdapter.notifyItemInserted(filesList.size());
                    }
                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
    super.onActivityResult(requestCode, resultCode, data);
}

private Pair<Boolean, File> isFileValid(Uri uri) throws NullPointerException {
    Pair<Boolean, File> defaultResponse = Pair.create(false, null);
    Cursor c = getContentResolver().query(uri, null, null, null, null);
    if (c != null) {
        c.moveToFirst();

        String filename = c.getString(c.getColumnIndex(OpenableColumns.DISPLAY_NAME));

        if (isSupported(filename)) {
            c.close();
            return Pair.create(true, new File(StringUtils.endsWithIgnoreCase(filename, ".pdf") ? DOCUMENT : IMAGE));
        } else {
            Toast.makeText(this, "File format not supported", Toast.LENGTH_SHORT).show();
            c.close();
            return defaultResponse;
        }
    }
    return defaultResponse;
}

private boolean isSupported(String filename) {
    String[] supportedFormats = { ".pdf", ".jpg", ".gif", ".png" };

    for (String format : supportedFormats) {
        if (StringUtils.endsWithIgnoreCase(filename, format)) {
            return true;
        }
    }
    return false;
}
}

Main activity layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:gravity="bottom"
android:orientation="vertical"
tools:context=".MainActivity">

   <androidx.recyclerview.widget.RecyclerView
   android:id="@+id/recyclerView"
   android:layout_width="match_parent"
   android:layout_height="wrap_content" />

<ImageView
    android:id="@+id/attach"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|center"
    android:layout_marginBottom="19dp"
    android:padding="10dp"
    android:src="@drawable/ic_attach" />
</LinearLayout>

File:

public class File {

public enum Type {
    DOCUMENT,
    IMAGE
}

private Type type;

public File(Type type) {
    this.type = type;
}

public Type getType() {
    return type;
}
}

File Adapter:

public class FilesAdapter extends RecyclerView.Adapter<FilesAdapter.BaseViewHolder> {

private List<File> files;

public FilesAdapter(List<File> files) {
    this.files = files;
}

@NonNull
@Override
public FilesAdapter.BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(viewType == 0 ? R.layout.document_item : R.layout.image_item, parent, false);
    if (viewType == 0) {
        return new DocumentViewHolder(view);
    } else {
        return new ImageViewHolder(view);
    }
}

@Override
public void onBindViewHolder(@NonNull FilesAdapter.BaseViewHolder viewHolder, int position) {
    viewHolder.bind(files.get(position));
}

@Override
public int getItemViewType(int position) {
    if (files.get(position).getType() == File.Type.DOCUMENT) {
        return 0;
    } else {
        return 1;
    }
}

@Override
public int getItemCount() {
    return files.size();
}

abstract static class BaseViewHolder extends RecyclerView.ViewHolder {
    public BaseViewHolder(@NonNull View itemView) {
        super(itemView);
    }
    abstract void bind(File file);
}

static class ImageViewHolder extends BaseViewHolder {

    public ImageViewHolder(@NonNull View itemView) {
        super(itemView);
    }

    @Override
    void bind(File file) { }
}

static class DocumentViewHolder extends BaseViewHolder {

    public DocumentViewHolder(@NonNull View itemView) {
        super(itemView);
    }

    public void bind(File file) { }
}
}

document item:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="150dp"
android:layout_height="40dp"
android:background="@drawable/ic_rounded"
android:backgroundTint="#888888"
android:layout_margin="5dp">

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:gravity="center"
    android:text="PDF"
    android:textColor="@android:color/white"/>

</LinearLayout>

image item:

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="120dp"
android:layout_height="80dp"
android:layout_margin="5dp"
app:cardBackgroundColor="#000000"
app:cardCornerRadius="10dp"
app:cardElevation="0dp">

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:gravity="center"
    android:text="IMAGE"
    android:textColor="@android:color/white"/>

</androidx.cardview.widget.CardView>

if I select an image first, and the several pdfs, it works fine:

But if I first select 3 pdfs, and then an image, this happens:

Any idea how to solve this?

解决方案

I had a similar issue in another project and i solved it by using the Google library FlexboxLayoutManager.

  1. Get the latest FlexboxLayoutManager Library (https://github.com/google/flexbox-layout) and add it into your grandle dependencies (implementation 'com.google.android:flexbox:2.0.1')
  2. In your Activity add the below lines of code:

    FlexboxLayoutManager layoutManager = new FlexboxLayoutManager(this); 
     layoutManager.setFlexDirection(FlexDirection.ROW);
     layoutManager.setFlexWrap(FlexWrap.NOWRAP);
     recyclerView.setLayoutManager(layoutManager);

  3. To make FlexboxLayoutManager work with horizontal scroll add the below code in your adapter (FilesAdapter) in BaseViewHolder class:

    abstract static class BaseViewHolder extends RecyclerView.ViewHolder {
         public BaseViewHolder(@NonNull View itemView) {
             super(itemView);
             ViewGroup.LayoutParams lp = itemView.getLayoutParams();
             if (lp instanceof FlexboxLayoutManager.LayoutParams) {
                 FlexboxLayoutManager.LayoutParams flexboxLp = (FlexboxLayoutManager.LayoutParams) lp;
                 flexboxLp.setFlexShrink(0.0f);
                 flexboxLp.setAlignSelf(AlignItems.FLEX_START); //this will align each itemView on Top or use AlignItems.FLEX_END to align it at Bottom
             }
         }
         abstract void bind(File file);
     }

这篇关于带有动态物品高度的水平RecyclerView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆