简体   繁体   English

如何在 NestedScrollView 中限制 RecyclerView 的高度

[英]How to limit the height of a RecyclerView inside a NestedScrollView

Within my current Android application, I have a screen that displays an android.support.v4.app.DialogFragment .在我当前的 Android 应用程序中,我有一个显示android.support.v4.app.DialogFragment的屏幕。

This DialogFragment view contains the following UI componentsDialogFragment视图包含以下 UI 组件

HEADING
== Sub Heading
== NestedScrollView
==== RecyclerView
==== RadioGroup
==== Spinner
==== EditText
==== Action Buttons

The DialogFragment is configured to be Full Screen using Style as follows:- DialogFragment使用样式配置为全屏,如下所示:-

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setStyle(DialogFragment.STYLE_NO_TITLE, R.style.AppDialogTheme);
}

My dialog style is我的对话风格是

<!-- Define your custom dialog theme here extending from base -->
<style name="AppDialogTheme" parent="Theme.AppCompat.Light.Dialog">
    <!-- Define color properties as desired -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">#000</item>
    <item name="android:textColorHighlight">@color/background_url</item>
    <item name="colorAccent">@color/dark_grey</item>
    <item name="colorControlNormal">@color/colorPrimaryDark</item>
    <!-- Define window properties as desired -->
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowBackground">@android:color/white</item>
    <item name="android:windowIsFloating">true</item>
    <item name="android:windowCloseOnTouchOutside">false</item>
</style>

The reason I employ a NestedScrollView is so that the View will work in both Portrait and Landscape mode.我使用NestedScrollView的原因是View可以在纵向和横向模式下工作。

I wish to limit the height of the RecyclerView我想限制RecyclerViewheight

The closest I have got is using the layout below.我最接近的是使用下面的布局。

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/headline_literal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:gravity="center"
        android:padding="10dp"
        android:text="Heading"
        android:textSize="20sp"
        android:textStyle="bold" />

    <View
        android:id="@+id/divider"
        android:layout_width="fill_parent"
        android:layout_height="2dp"
        android:layout_marginTop="5dp"
        android:background="#c0c0c0" />

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        tools:context=".MainActivity">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:weightSum="5"
            android:orientation="vertical">

            <TextView
                android:id="@+id/sub_headline_literal"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:padding="10dp"
                android:text="Some long texts having a long size so that it takes multiple lines in the view to replicate the real-life app use case. This is important to have 3-4 lines this textview so that we can see if the views are being populated correctly. Hope this sentence is long enough to replicate the real-life scenario of this TextView content. Thank you."
                android:textSize="16sp"
                android:textStyle="normal" />

            <android.support.v7.widget.RecyclerView
                android:id="@+id/dummy_rv"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_margin="10dp"
                android:layout_marginStart="9dp"
                android:layout_marginEnd="9dp"
                android:layout_weight="1"
                android:background="@drawable/rv_border"
                android:fadingEdge="horizontal"
                android:fadingEdgeLength="10dp"
                android:padding="10dp"
                android:requiresFadingEdge="vertical" />

            <RadioGroup
                android:id="@+id/myRadioGroup"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_marginTop="10dp"
                android:checkedButton="@+id/sound">

                <RadioButton
                    android:id="@+id/sound"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Sound" />

                <RadioButton
                    android:id="@+id/vibration"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Vibration" />

                <RadioButton
                    android:id="@+id/silent"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Silent" />
            </RadioGroup>

            <EditText
                android:id="@+id/notes"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Notes" />

            <LinearLayout
                android:id="@+id/buttons"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                android:padding="10dp">

                <TextView
                    android:id="@+id/cancel_button"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:gravity="center"
                    android:text="Cancel" />

                <TextView
                    android:id="@+id/submit_button"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:gravity="center"
                    android:text="Submit" />
            </LinearLayout>
        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>
</LinearLayout>

By using weightSum on the inner LinearLayout of the NestedScrollView I can limit the height of the Recyclerview .通过在weightSum的内部LinearLayout上使用NestedScrollView我可以限制Recyclerview的高度。 However the NestedScrollView height is far too large, with more than half its height being blank.然而, NestedScrollView高度太大了,一半以上的高度是空白的。

How can I limit the height of my RecyclerView and get NestedScrollView to wrap_content ?如何限制RecyclerView的高度并使NestedScrollViewwrap_content

I've tried NestedScrollView with height wrap_content but this has no effect.我试过NestedScrollView与高度wrap_content但这没有效果。

How can I achieve the desired UI?如何实现所需的用户界面? Thanks in advance!提前致谢!

Instead of having a NestedRecyclerView , I would like to suggest to have a header and a footer added to your RecyclerView which will nicely place the overall content as far as I have seen your layout. 我没有使用NestedRecyclerView ,而是建议在您的RecyclerView中添加页眉和页脚,这样就可以很好地将整个内容放到我看到的布局中。 I want to provide you a link to my answer here where you can find how to add a footer and a header along with your RecyclerView . 我想在此提供一个链接到我的答案 ,您可以其中找到如何添加页脚和标题以及RecyclerView

Hence, I would like to suggest to create a view with headline_literal and the divider and use this as a header whereas the RadioGroup , EditText and the Button will be in the footer. 因此,我建议使用headline_literaldivider创建一个视图,并将其用作标题,而RadioGroupEditTextButton将在页脚中。 Let me know if you face any problem with it. 如果您遇到任何问题,请告诉我。

I have tried to implement the behavior that you want by myself and let me know if the following implementation works for you. 我试图自己实现您想要的行为,如果以下实现适合您,请告诉我。 I have added this in Github as well. 我也在Github中添加了这个。

Let us first declare an adapter for adding a header and a footer to the RecyclerView . 让我们首先声明一个适配器,用于向RecyclerView添加页眉和页脚。

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

import java.util.ArrayList;

public class RecyclerViewWithHeaderFooterAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private static final int FOOTER_VIEW = 1;
    private static final int HEADER_VIEW = 2;
    private ArrayList<String> data; // Take any list that matches your requirement.
    private Context context;

    // Define a constructor
    public RecyclerViewWithHeaderFooterAdapter(Context context, ArrayList<String> data) {
        this.context = context;
        this.data = data;
    }

    // Define a ViewHolder for Header view
    public class HeaderViewHolder extends ViewHolder {
        public HeaderViewHolder(View itemView) {
            super(itemView);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Do whatever you want on clicking the item
                }
            });
        }
    }

    // Define a ViewHolder for Footer view
    public class FooterViewHolder extends ViewHolder {
        public FooterViewHolder(View itemView) {
            super(itemView);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Do whatever you want on clicking the item
                }
            });
        }
    }

    // Now define the ViewHolder for Normal list item
    public class NormalViewHolder extends ViewHolder {
        public NormalViewHolder(View itemView) {
            super(itemView);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Do whatever you want on clicking the normal items
                }
            });
        }
    }

    // And now in onCreateViewHolder, you have to pass the correct view
    // while populating the list item.

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View v;

        if (viewType == FOOTER_VIEW) {
            v = LayoutInflater.from(context).inflate(R.layout.list_item_footer, parent, false);
            FooterViewHolder vh = new FooterViewHolder(v);
            return vh;
        } else if (viewType == HEADER_VIEW) {
            v = LayoutInflater.from(context).inflate(R.layout.list_item_header, parent, false);
            HeaderViewHolder vh = new HeaderViewHolder(v);
            return vh;
        }

        // Otherwise populate normal views
        v = LayoutInflater.from(context).inflate(R.layout.list_item_normal, parent, false);
        NormalViewHolder vh = new NormalViewHolder(v);

        return vh;
    }

    // Now bind the ViewHolder in onBindViewHolder
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

        try {
            if (holder instanceof NormalViewHolder) {
                NormalViewHolder vh = (NormalViewHolder) holder;

                vh.bindView(position);
            } else if (holder instanceof FooterViewHolder) {
                FooterViewHolder vh = (FooterViewHolder) holder;
            } else if (holder instanceof HeaderViewHolder) {
                HeaderViewHolder vh = (HeaderViewHolder) holder;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // Now the critical part. You have return the exact item count of your list
    // I've only one footer. So I returned data.size() + 1
    // If you've multiple headers and footers, you've to return total count
    // like, headers.size() + data.size() + footers.size()

    @Override
    public int getItemCount() {
        if (data == null) {
            return 0;
        }

        if (data.size() == 0) {
            // Return 1 here to show nothing
            return 1;
        }

        // Add extra view to show the header view
        // Add another extra view to show the footer view
        // So there are two extra views need to be populated
        return data.size() + 2;
    }

    // Now define getItemViewType of your own.
    @Override
    public int getItemViewType(int position) {
        if (position == 0) {
            // This is where we'll add the header.
            return HEADER_VIEW;
        } else if (position == data.size() + 1) {
            // This is where we'll add a footer.
            return FOOTER_VIEW;
        }

        return super.getItemViewType(position);
    }

    // So you're done with adding a footer and its action on onClick.
    // Now set the default ViewHolder for NormalViewHolder
    public class ViewHolder extends RecyclerView.ViewHolder {
        // Define elements of a row here
        public ViewHolder(View itemView) {
            super(itemView);
            // Find view by ID and initialize here
        }

        public void bindView(int position) {
            // bindView() method to implement actions
        }
    }
}

Now let us define the layouts one by one. 现在让我们逐个定义布局。 Here is the list_item_normal.xml . 这是list_item_normal.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">

    <TextView
        android:id="@+id/normal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:text="This is a text to be displayed in each item in the RecyclerView"
        android:textSize="16sp"
        android:textStyle="normal" />

</LinearLayout>

And the list_item_footer.xml should look like the following. list_item_footer.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:layout_marginTop="16dp"
    android:orientation="vertical">

    <RadioGroup
        android:id="@+id/myRadioGroup"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="10dp"
        android:checkedButton="@+id/sound">

        <RadioButton
            android:id="@+id/sound"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Sound" />

        <RadioButton
            android:id="@+id/vibration"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Vibration" />

        <RadioButton
            android:id="@+id/silent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Silent" />

    </RadioGroup>

    <EditText
        android:id="@+id/notes"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Notes" />

    <LinearLayout
        android:id="@+id/buttons"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="10dp">

        <TextView
            android:id="@+id/cancel_button"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="Cancel" />

        <TextView
            android:id="@+id/submit_button"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="Submit" />
    </LinearLayout>
</LinearLayout>

Finally, the list_item_header.xml should have the following. 最后, list_item_header.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">

    <TextView
        android:id="@+id/sub_headline_literal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="10dp"
        android:text="Some long texts having a long size so that it takes multiple lines in the view to replicate the real-life app use case. This is important to have 3-4 lines this textview so that we can see if the views are being populated correctly. Hope this sentence is long enough to replicate the real-life scenario of this TextView content. Thank you."
        android:textSize="16sp"
        android:textStyle="normal" />

</LinearLayout>

Now you have divided the components of your original layout into parts. 现在,您已将原始布局的组件划分为多个部分。 Hence the main layout should look like the following. 因此,主要布局应如下所示。

<?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:orientation="vertical">

    <TextView
        android:id="@+id/headline_literal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:gravity="center"
        android:padding="10dp"
        android:text="Heading"
        android:textSize="20sp"
        android:textStyle="bold" />

    <View
        android:id="@+id/divider"
        android:layout_width="fill_parent"
        android:layout_height="2dp"
        android:layout_marginTop="5dp"
        android:background="#c0c0c0" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/dummy_rv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:padding="10dp" />
</LinearLayout>

Hence, I am sharing one sample Activity to run this code which will show the overall implementation. 因此,我正在共享一个示例Activity以运行此代码,该代码将显示整体实现。

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

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private RecyclerView mRecyclerView;
    private ArrayList<String> data = new ArrayList<String>();
    private RecyclerViewWithHeaderFooterAdapter adapter;

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

    private void initializeRecyclerView() {
        mRecyclerView = findViewById(R.id.dummy_rv);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        adapter = new RecyclerViewWithHeaderFooterAdapter(this, data);
        mRecyclerView.setAdapter(adapter);
    }

    private void initializeData() {
        for (int i = 0; i < 10; i++) data.add("Position :" + i);
    }
}

Hope that helps! 希望有所帮助!

Easy, 简单,

Use maxHeight attribute with specific height , and if it still doesn't work, then try creating a customrecyclerview extending RecyclerView and override height in OnMeasure method. 使用具有特定高度的 maxHeight属性,如果它仍然不起作用,则尝试创建一个扩展RecyclerViewcustomrecyclerview并覆盖OnMeasure方法中的高度。

Customize Recycler view to set maxHeight . 自定义Recycler视图以设置maxHeight

public class MaxHeightRecyclerView extends RecyclerView {
    private int mMaxHeight;

    public MaxHeightRecyclerView(Context context) {
        super(context);
    }

    public MaxHeightRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initialize(context, attrs);
    }

    public MaxHeightRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initialize(context, attrs);
    }

    private void initialize(Context context, AttributeSet attrs) {
        TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.MaxHeightScrollView);
        mMaxHeight = arr.getLayoutDimension(R.styleable.MaxHeightScrollView_maxHeight, mMaxHeight);
        arr.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mMaxHeight > 0) {
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxHeight, MeasureSpec.AT_MOST);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

n attrs.xml n attrs.xml

<declare-styleable name="MaxHeightScrollView">
        <attr name="maxHeight" format="dimension" />
    </declare-styleable>

set RecyclerView height wrap_content in xml and maxHeight to fixwidth in dp. 将xx和maxHeight中的RecyclerView height wrap_content设置为dp中的fixwidth。 The RecyclerView will consume height wrap_content till fixWidth which you set, after reaching to maxHeight, the RecyclerView will scrollable. RecyclerView将消耗高度wrap_content直到你设置的fixWidth,在达到maxHeight后,RecyclerView将可滚动。

If you also need to limit the size of your NestedScrollView by makeing a custom NestedScrollView: 如果您还需要通过创建自定义NestedScrollView来限制NestedScrollView的大小:

public class CustomNestedScrollView extends NestedScrollView {

private int maxHeight;
private final int defaultHeight = 200;

public CustomNestedScrollView(Context context) {
    super(context);
}

public CustomNestedScrollView(Context context, AttributeSet attrs) {
    super(context, attrs);
    if (!isInEditMode()) {
        init(context, attrs);
    }
}

public CustomNestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    if (!isInEditMode()) {
        init(context, attrs);
    }
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomNestedScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
    if (!isInEditMode()) {
        init(context, attrs);
    }
}

private void init(Context context, AttributeSet attrs) {
    if (attrs != null) {
        TypedArray styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.CustomNestedScrollView);
        //200 is a defualt value
        maxHeight = styledAttrs.getDimensionPixelSize(R.styleable.CustomNestedScrollView_maxHeight, defaultHeight);

        styledAttrs.recycle();
    }
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}

attr.xml attr.xml

<declare-styleable name="CustomNestedScrollView">
        <attr name="maxHeight" format="dimension" />
    </declare-styleable>

example layout for custom NestedScrollView: 自定义NestedScrollView的示例布局:

               <your.package.CustomNestedScrollView 
                android:layout_weight="1"
                app:maxHeight="90dp"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content">

                <!--Child view with RecyclerView here-->


                </your.package.CustomNestedScrollView>

Along with this custom NestedScrollView if you apply the customization of your RecyclerView then it will work exactly how you want. 与此自定义NestedScrollView一起,如果您应用RecyclerView的自定义,那么它将完全按照您的需要工作。 I hope this helps! 我希望这有帮助!

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

相关问题 NestedScrollView里面没有使用Recyclerview - NestedScrollView not fling with Recyclerview inside RecyclerView 不在 NestedScrollView 内滚动 - RecyclerView not scrolling inside NestedScrollView NestedScrollView 内的 RecyclerView 问题 - RecyclerView inside NestedScrollView issue LinearLayout 和 NestedScrollView 中的 RecyclerView - 如何滚动到某个位置的项目顶部? - RecyclerView inside LinearLayout and NestedScrollView - How to scroll to top of item on a certain position? Android RecyclerView 高度问题内视图 pager2 内 NestedScrollView 与 TabLayout - Android RecyclerView height problem inside view pager2 inside NestedScrollView with TabLayout NestedScrollView内的RecyclerView比RecyclerView滚动得更快 - RecyclerView inside NestedScrollView scroll faster than RecyclerView 在nestedscrollview的recyclerview内部的recyclerview的notifyDataChanged上,外部recyclerview滚动到顶部 - On notifyDataChanged of recyclerview inside a recyclerview in a nestedscrollview the outer recyclerview scrolls to top 检查nestedscrollview中的recyclelerview滚动状态 - Check recyclerview scroll state inside nestedscrollview 使用NestedScrollView中的Multiple Recyclerview不会发生回收 - View Recycling not happens with Multiple Recyclerview inside NestedScrollView 嵌套滚动视图中的 recyclerview addOnScrollListener (endless scrolllistener) - recyclerview inside nestedscrollview addOnScrollListener (endless scrolllistener)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM