1

I'm trying to add multiple gridviews to a scrollview in linear layout at runtime. But only first row displays. Pls help

user1283358
  • 21
  • 1
  • 3

3 Answers3

10

You can't nest scrollable Views in Android - i.e. ListView, GridView, ScrollView.

You could give a look at the following code:

import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.GridView;

public class ScrollableGridView extends GridView {
    boolean expanded = true;

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

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

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

    public boolean isExpanded()
    {
        return expanded;
    }


    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        // HACK! TAKE THAT ANDROID!
        if (isExpanded())
        {
            // 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();
        }
        else
        {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    public void setExpanded(boolean expanded)
    {
        this.expanded = expanded;
    }
}

It is a little better version of GridView allowing it to almost work when nested in ScrollView. I said "almost work" since I found it sometimes being 20-30 pixels too short or too long. You should note that this would prevent the views from being reuse, so it is way heavier than normal GridView.

In my case I ended up extending LinearLayout and using it to align its children in columns. It wasn't really hard - I can give you examples if you want. :)

I got the GridView example from this answer.

Here is an example for a GridView based on LinearLayout:

import java.util.List;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

public class GridLikeLayout extends LinearLayout {
    private static final int DEFAULT_ITEMS_PER_ROW = 1;
    private final int DEFAULT_COLUMN_WIDTH = (int) TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP, 150, getContext().getResources()
                    .getDisplayMetrics());

    private int itemsPerRow = DEFAULT_ITEMS_PER_ROW;
    private List<View> innerViews = null;

    private int columnWidth;

    public GridLikeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.GridLikeLayout);

        // itemsPerRow = a.getInt( R.styleable.GridLikeLayout_columns,
        // DEFAULT_ITEMS_PER_ROW);

        try {
            columnWidth = (int) a.getDimension(
                    R.styleable.GridLikeLayout_column_width, DEFAULT_COLUMN_WIDTH);
        } catch (UnsupportedOperationException uoe) {
            columnWidth = (int) a.getInt(
                    R.styleable.GridLikeLayout_column_width, DEFAULT_COLUMN_WIDTH);
        }

        setOrientation(LinearLayout.VERTICAL);
    }

    public GridLikeLayout(Context context) {
        super(context);
        setOrientation(LinearLayout.VERTICAL);
    }

    public void setInnerViews(List<View> innerViews) {
        this.innerViews = innerViews;
        processViews();
    }

    public List<View> getInnerViews() {
        return innerViews;
    }

    protected void processViews() {
        if (null != innerViews) {
            LinearLayout innerContainer = null;
            innerContainer = generateInnerContainer();
            int childrenCount = innerViews.size();
            for (int index = 0; index < childrenCount; ++index) {
                if (isFull(innerContainer)) {
                    addInnerContainer(innerContainer);
                    innerContainer = generateInnerContainer();
                }
                View child = innerViews.get(index);
                if (null != child.getParent()) {
                    ((ViewGroup) child.getParent()).removeView(child);
                }
                addInnerView(innerContainer, child);
            }

            addInnerContainer(innerContainer);
        }
    }

    protected boolean isFull(LinearLayout innerContainer) {
        return 0 == (innerContainer.getChildCount() % itemsPerRow)
                && 0 < innerContainer.getChildCount();
    }

    protected void addInnerView(LinearLayout innerContainer, View child) {
        int width = LayoutParams.WRAP_CONTENT;
        int height = LayoutParams.WRAP_CONTENT;
        LayoutParams innerParams = new LayoutParams(width, height);
        innerParams.weight = 1;
        innerParams.gravity = Gravity.CENTER;
        innerContainer.addView(child, innerParams);
    }

    protected void addInnerContainer(LinearLayout innerContainer) {
        LayoutParams params = generateDefaultLayoutParams();
        params.width = LayoutParams.MATCH_PARENT;
        addView(innerContainer, params);
    }

    protected LinearLayout generateInnerContainer() {
        LinearLayout innerContainer;
        innerContainer = new LinearLayout(getContext());
        innerContainer.setGravity(Gravity.CENTER);
        return innerContainer;
    }

    public void setOnInnerViewClickListener(OnClickListener listener) {
        for (View innerView : innerViews) {
            innerView.setOnClickListener(listener);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // Sets up mListPadding
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

        if (widthMode == MeasureSpec.UNSPECIFIED) {
            if (columnWidth > 0) {
                widthSize = columnWidth + getPaddingLeft() + getPaddingRight();
            } else {
                widthSize = getPaddingLeft() + getPaddingRight();
            }
            widthSize += getVerticalScrollbarWidth();
        }

        int childWidth = widthSize - getPaddingLeft() - getPaddingRight();
        int columnsNumber = determineColumns(childWidth);
        if (columnsNumber > 0 && columnsNumber != itemsPerRow) {
            itemsPerRow = columnsNumber;
            removeAllViews();
            processViews();
        }
    }

    protected int  determineColumns(int availableSpace) {
        int columnsNumber = itemsPerRow;
        if (0 < columnWidth) {
            columnsNumber = availableSpace / columnWidth;
        } 
        return columnsNumber;
    }

}

Here is my resource file for the custom attributes:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <attr name="column_width" format="dimension|integer">
        <enum name="single_column" value="-1" />
    </attr>
    <declare-styleable name="GridLikeLayout">
        <attr name="column_width" />
    </declare-styleable>

</resources>

Here is an example usage:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layoutContainer"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <ScrollView
        android:id="@+id/itemsScroller"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >

        <LinearLayout
            android:id="@+id/itemsLayout"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

            <your_package.view.GridLikeLayout
                xmlns:my="YOUR APPLICATION PACKAGE"
                android:id="@+id/MyGrid"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                my:column_width="260dp"
                android:focusable="true"
                android:gravity="center_horizontal"
                android:padding="5dp" >
            </your_package.GridLikeLayout>

            <View
                android:id="@+id/viewSeparator"
                android:layout_width="fill_parent"
                android:layout_height="2dp" />

            <your_package.GridLikeLayout
                xmlns:my="YOUR APPLICATION PACKAGE"
                android:id="@+id/list"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                my:column_width="single_column" >
            </your_package.GridLikeLayout>

        </LinearLayout>
    </ScrollView>

</FrameLayout>

Please tell me if you have any troubles running the example - I'm not able to try it at the moment. So if you have issues, I'll check it up later. Don't forget to change the package names - "your_package" should be the the package where you store the GridLikeLayout and "YOUR APPLICATION PACKAGE" is your app's package - the one specified in the application manifest. :)

Community
  • 1
  • 1
Samuil Yanovski
  • 2,837
  • 17
  • 19
0

Your first solution does work but does not take into account that GridViews often imply image caching such as in my case. Here your first solution leads to OutOfMemoryError every time I start to scroll. Has anybody already got an example solution which does take into account image caching?

tsemann
  • 121
  • 1
  • 9
0

Until you put here the layout I could suppose only that you have set wrong horizontal/vertical parameter in your linear layout.

Gangnus
  • 24,044
  • 16
  • 90
  • 149