69

I have to design layout such that whole layout should scroll and inside layout I have to display related content in Grid form so I decided to use GridView.But problem is that I’m unable to use GridView inside ScrollView I have read documentation(Documentation also saying that we should not use GridView inside ScrollView) also.But I have to do this,so can any please give me some idea about this.Thanks

Dilip
  • 2,271
  • 5
  • 32
  • 52
  • don't. you can't. (ok, you can, but it is painful (doesn't quite fit), so try to avoid it). – njzk2 Oct 12 '12 at 12:39
  • 2
    Thanks @njzk2 But i have to desing layout like this so can u please tell me some other way that should look like this. – Dilip Oct 12 '12 at 12:41
  • @Dipu: You mention just the `GridView` in a `ScrollView`. In the comment to the first answer, you mention adding more `Views` to the, I assume, `ScrollView`. Considering rephrasing your question to include as many details as possible. – Siddharth Lele Oct 12 '12 at 12:47
  • Actually, you can, and there are some good applications for it. I will update with an answer. – dennisdrew Oct 12 '12 at 12:47
  • @SiddharthLele i mean i have to put ImageView,TextView,Button and GridView inside listview.At button have to put GridView that's why it's necessary to use scrollview – Dilip Oct 12 '12 at 12:54
  • have you tried encapsulating gridview with a linear layout ? – Charan Pai Oct 12 '12 at 12:58
  • here is exact ans for toy i think.. http://stackoverflow.com/questions/4523609/grid-of-images-inside-scrollview/4536955#4536955 – Charan Pai Oct 12 '12 at 13:05
  • Do not do this, that will cause all items even if those items which are not visible in current screen call getview() when gridview initialize,that certainly cost a lot of time. – DzMonster Feb 02 '15 at 12:47

11 Answers11

117

There are definitely benefits to a GridView beside the inherent scrolling. For example, a consistent, dynamic layout of cells that will expand and contract based on the data you pass into it. So, when people say it's not good to desire such a functionality, I think that's wrong because you could want the dynamic grid of images (views) inside of a scrolling view, but want that entire scrolling view to contain other things than just the grid cells.

Now, here is how you can do this. Check the answer here. It is an expandable height GridView, which you will want to import / create in your project. What that basically means is that as more items are added to the GridView, it will just expand its height, as opposed to keeping its height set and using scrolling. This is exactly what you want.

Once you have the ExpandableHeightGridView in your project, go to your XML layout where you want the GridView to be. You can then do something like this (paraphrasing):

<ScrollView ...>
    <RelativeLayout ...>
        <com.example.ExpandableHeightGridView ... />
        <other view items />
    </RelativeLayout>
</ScrollView>

Then, in your activity where you set the GridView's adapter, you want to make sure you set it to expand. So:

ExpandableHeightGridView gridView = (ExpandableHeightGridView) findViewById(R.id.myId);
gridView.setAdapter(yourAdapter);
gridView.setExpanded(true);

The reason you want this expandable GridView is because, the fact that a standard GridView doesn't expand is what causes it to scroll. It sticks to a certain height, and then as more items fill it past its view bounds, it becomes scrollable. Now, with this, your GridView will always expand its height to fit the content within it, thus never allowing it to enter its scrolling mode. This enables you to use it inside of the ScrollView and use other view elements above or below it within the ScrollView, and have them all scroll.

This should give you the result you're looking for. Let me know if you have any questions.

Community
  • 1
  • 1
dennisdrew
  • 4,399
  • 1
  • 25
  • 25
  • 1
    one thing,when i launch app with different view then layout automatically scroll at bottom how to fit it please suggest me – Dilip Oct 12 '12 at 13:31
  • Can you clarify a bit? I'm not exactly sure what you mean by "launch app with different view." I haven't run into this issue. – dennisdrew Oct 12 '12 at 13:32
  • This GridView is taking focus and placing at top position of the screen doing scroll while changing the view's visibility like visible->gone->visible. With this, scroll is applying by default. Actually, It shouldn't happen. Can you help me on this? – Noundla Sandeep Jan 28 '13 at 10:04
  • Jaan please how did you fixed the issue: "when i launch app with different view then layout automatically scroll at bottom" – haythem souissi Feb 21 '13 at 09:14
  • http://stackoverflow.com/questions/14998819/display-positioned-at-the-bottom-of-the-list – haythem souissi Feb 21 '13 at 10:20
  • 7
    The problem with this approach is that you lose the performance optimizations that the grid view gives you, ie. recycling of grid items. – howettl Apr 11 '13 at 00:16
  • That's a good point. However, I'm not quite sure there would be any other approach to solve that, outside of extending GridView and/or creating a custom view to handle recycling. – dennisdrew Apr 22 '13 at 16:12
  • 9
    What you suggest in this answer defeats the point of a `GridView`. Your app could run out of memory and the performance would suffer greatly. The solution is using header or footer views just like with a normal `ListView`. The native `GridView` may not support header or footer views, but there are [**custom implementations like this one**](https://github.com/maurycyw/HeaderGridView) that do. Use one of those and you get the same effect without loosing view recycling. Hacks like you propose may solve the problem, but they will just cause more serious problems later on. – Xaver Kapeller Jun 28 '14 at 19:54
  • Or you could just use a `GridLayout` in the `ScrollView`. There would again be no view recycling, but it would at least be a lot better than using a `GridView`. [**Link to documentation.**](http://developer.android.com/reference/android/widget/GridLayout.html) – Xaver Kapeller Jun 28 '14 at 19:59
  • @XaverKapeller will your GridLayout in a scrollview will work for more than one gridviews i want in actual sections gridview but every section should contain header and footer so i did not get any lib thats why i m using two gridviews do u have any sol in your mind ? – Erum Aug 29 '15 at 16:21
  • put gridView.setFocusable(false); before setting expanded true to prevent scrollview scrolls automatically till the GridView end. – Shahab Rauf Aug 23 '16 at 10:16
  • 1
    Wanted to tell about a behaviour that happened when implementing this is that the expanded gridview took the scrollview to the bottom, so just wanted to point that doing this : //moving back the ScrollLayout to the top, as expanding the gridview will move the scrollview to the bottom ScrollView mainScrollView = (ScrollView) rootView.findViewById (R.id.scrollView); mainScrollView.smoothScrollTo(0,0); will put everything in place. Hope it helps someone! – PayToPwn Jul 10 '17 at 12:18
  • Genius! Thank you! – Deve Dec 29 '17 at 21:06
  • Like @XaverKapeller said this solution is not suitable in terms of performance, especially with a large number of elements, which is the main objective of using GridView or listview . There are several ways to solve this problem more performance while respecting the main goal of gridview! – anas darai Feb 24 '22 at 11:18
36

I know I'm late but I have another solution which I must share and which works flawlessly. Here, the method calculates the GridView height based on the number of items it contains and sets the height to the GridView at run time.

public void setGridViewHeightBasedOnChildren(GridView gridView, int columns) {
        ListAdapter listAdapter = gridView.getAdapter(); 
        if (listAdapter == null) {
            // pre-condition
            return;
        }

        int totalHeight = 0;
        int items = listAdapter.getCount();
        int rows = 0;

        View listItem = listAdapter.getView(0, null, gridView);
        listItem.measure(0, 0);
        totalHeight = listItem.getMeasuredHeight();

        float x = 1;
        if( items > columns ){
            x = items/columns;
            rows = (int) (x + 1);
            totalHeight *= rows;
        }

        ViewGroup.LayoutParams params = gridView.getLayoutParams();
        params.height = totalHeight;
        gridView.setLayoutParams(params);

}

After you have called setAdapter on your gridview, just call

setGridViewHeightBasedOnChildren( <yourGridView> , <no of grid view columns> )

and it'll work.

You can the define the gridview in your xml as you normally do

and let the code take care of it. :)

Shweta Nandha
  • 728
  • 1
  • 8
  • 19
Vikram Gupta
  • 6,496
  • 5
  • 34
  • 47
  • did you call setAdapter on your gridview before calling the setGridViewHeightBasedOnChildren method? If the gridview has items then it should work. – Vikram Gupta Mar 28 '14 at 09:39
  • 2
    @vickey if the number of items completely fills the number of rows, the height you calculated has 1 extra row – andrei Jul 24 '15 at 13:20
  • 1
    If your GridView has vertical spacing, you have to add it to totalHeight. – ElYeante Aug 17 '15 at 20:42
  • There is a bug in this code. replace if block with: if( items > columns ){ x = ((double) items ) /columns; rows =(int) Math.ceil(x); totalHeight *= rows; } and declare x veriable as double istead of float. – aligur Dec 16 '15 at 13:36
  • If you don't want to use custom grid view with this code its working – Manju Aug 15 '18 at 17:11
  • How do I get the columns number if gridview is set to android:numColumns="auto_fit" ? – Daniele Muscetta Oct 10 '20 at 11:11
10

I found a way to give the GridView a fixed size inside ScrollView, and enable scrolling it. That allows you to see the entire ScrollView without having to scroll all elements of the GridView, and it makes more sense to me that using an ExpandableHeightGridView.

To do so, you would have to implement a new class extending GridView and override onTouchEvent() to call requestDisallowInterceptTouchEvent(true). Thus, the parent view will leave the Grid intercept touch events.

GridViewScrollable.java:

package com.example;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.GridView;

public class GridViewScrollable extends GridView {

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

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

    public GridViewAdjuntos(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev){
        // Called when a child does not want this parent and its ancestors to intercept touch events.
        requestDisallowInterceptTouchEvent(true);
        return super.onTouchEvent(ev);
    }
}

Add it in your layout with the characteristics and margins you want, inside a ScrollView:

<ScrollView 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:isScrollContainer="true" >

    <com.example.GridViewScrollable
    android:id="@+id/myGVS"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:numColumns="auto_fit"
    android:stretchMode="columnWidth" />

</ScrollView>

And just get it in your activity:

GridViewScrollable myGridView = (GridViewScrollable) findViewById(R.id.myGVS);

I hope it helps =)

In Kotlin:

class GridViewScrollable @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : GridView(context, attrs, defStyleAttr) {

    override fun onTouchEvent(ev: MotionEvent?): Boolean {
        // Called when a child does not want this parent and its ancestors to intercept touch events.
        requestDisallowInterceptTouchEvent(true)
        return super.onTouchEvent(ev)
    }
}

There is an annoying warning: "Custom view GridViewScrollable overrides onTouchEvent but not performClick". Please, resolve yourself if you want.

CoolMind
  • 26,736
  • 15
  • 188
  • 224
Lionel T.
  • 1,194
  • 1
  • 13
  • 17
10

I was also trying to make a GridView scrollable inside a NestedScrollView.

This answer on another question helped me: https://stackoverflow.com/a/38612612

Enable your GridView Property as

android:nestedScrollingEnabled="true"

Original poster: Droid_Dev

Stephen Emery
  • 162
  • 1
  • 8
5

To use gridview inside scrollview, set scrollview android:fillViewport="true"

I know its late to answer here but may be useful to some new developers.

2

If you have used dennisdrew's answer and you have content above your grid and it gets hidden when you open your view simply add the last line below

ExpandableHeightGridView gridView = (ExpandableHeightGridView) findViewById(R.id.myId);
gridView.setAdapter(yourAdapter);
gridView.setExpanded(true);
gridView.setFocusable(false);
1

It's better to use ExpandableHeightlistView or ExpandableHeightGridView inside scrollview.

Logic

Calculate entire height by providing a very large height hint. But do not use the highest 2 bits of this integer; those are reserved for the MeasureSpec mode.

int expandSpec = MeasureSpec.makeMeasureSpec(
 Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
 super.onMeasure(widthMeasureSpec, expandSpec);

 ViewGroup.LayoutParams params = getLayoutParams();
 params.height = getMeasuredHeight();

for further information explore the example below with demo

http://www.londatiga.net/it/programming/android/make-android-listview-gridview-expandable-inside-scrollview/

Fazal Hussain
  • 1,129
  • 12
  • 28
Kishore Reddy
  • 2,394
  • 1
  • 19
  • 15
1

i would not force the issue. GridView does not work well in a scrollView. instead convert your gridview into a recyclerview and use a gridLayoutManager to resolve the issue.

j2emanue
  • 60,549
  • 65
  • 286
  • 456
1

Trust this solution worked for me!

android:nestedScrollingEnabled="true"

The code above inside grid view enables to scroll gridview and set width and height android:layout_width="match_parent" android:layout_height="200sp"

This will work even inside scrollview Thanks for the previous answers I figured out this for the previous answers. Thanks to them

0
 gridView.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    v.getParent().requestDisallowInterceptTouchEvent(true);
                    return false;
                }

            });

// Setting on Touch Listener for handling the touch inside ScrollView

  • Here are some guidelines for [How do I write a good answer?](https://stackoverflow.com/help/how-to-answer). This provided answer may be correct, but it could benefit from an explanation. Code only answers are not considered "good" answers. From [review](https://stackoverflow.com/review). – Trenton McKinney Sep 27 '19 at 00:51
0

If you want to have GridView inside ScrollView / RecyclerView, there are 2 cases:

  1. If you want to have GridView with all rows visible, see https://stackoverflow.com/a/63724794/2914140.

  2. If you want to have one row visible in GridView, but be able to scroll it (to reveal other rows), see https://stackoverflow.com/a/26852498/2914140.

CoolMind
  • 26,736
  • 15
  • 188
  • 224