Search code examples
androidgridviewandroid-gridviewvertical-scrolling

How to make a gridview scrolling exactly the same as another gridview?


To display data in table layout with the first column frozen, the others can scroll horizontally, I have used 2 gridviews as below XML file content:

<RelativeLayout 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"   
    tools:context="com.example.gridviews.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <GridView
            android:id="@+id/gridViewID"
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            android:columnWidth="50dp"
            android:horizontalSpacing="1dp"
            android:numColumns="1"
            android:paddingTop="5dp"
            android:stretchMode="columnWidth"
            android:verticalSpacing="1dp" />

        <HorizontalScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <GridView
                    android:id="@+id/gridViewDays"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:columnWidth="100dp"
                    android:horizontalSpacing="1dp"
                    android:numColumns="5"
                    android:paddingTop="5dp"
                    android:scrollbarStyle="outsideOverlay"
                    android:stretchMode="columnWidth"
                    android:verticalSpacing="1dp" />

            </LinearLayout>

        </HorizontalScrollView>

    </LinearLayout>

</RelativeLayout>

and my code:

...
BaseAdapter adapterId = new BaseAdapter() {
    @Override
    public int getCount() {
        return dataList.size();
    }

    @Override
    public Object getItem(int position) {
        return dataList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        TextView textView = new TextView(context);
        textView.setText(String.valueOf(dataList.get(position).Id));
        textView.setLayoutParams(new GridView.LayoutParams(GridView.AUTO_FIT, 70));
        return textView;
    }
};

BaseAdapter adapterDays = new BaseAdapter() {
    @Override
    public int getCount() {
        return dataList.size() * 5;
    }

    @Override
    public Object getItem(int position) {
        return dataList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        TextView textView = new TextView(context);
        int mod = position % 5;
        int idx = position / 5;
        switch (mod) {
            case 0:
                textView.setText(String.valueOf(dataList.get(idx).Mon));
                break;
            case 1:
                textView.setText(String.valueOf(dataList.get(idx).Tue));
                break;
            case 2:
                textView.setText(String.valueOf(dataList.get(idx).Wed));
                break;
            case 3:
                textView.setText(String.valueOf(dataList.get(idx).Thu));
                break;
            case 4:
                textView.setText(String.valueOf(dataList.get(idx).Fri));
                break;
        }
        textView.setLayoutParams(new GridView.LayoutParams(GridView.AUTO_FIT, 70));
        return textView;
    }
};

gridViewID.setAdapter(adapterId);
gridViewDays.setAdapter(adapterDays);

// Horizontal scrolling of gridViewDays
LinearLayout.LayoutParams linearParams = (LinearLayout.LayoutParams) gridViewDays.getLayoutParams();
linearParams.width = 300;
gridViewDays.setLayoutParams(linearParams);

// Vertical scrolling...
gridViewID.setOnScrollListener(new AbsListView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        int firstVisibleItem = view.getFirstVisiblePosition();
        gridViewID.setSelection(firstVisibleItem);
        gridViewDays.setSelection(firstVisibleItem * 5);
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    }
});
...

With these code, of course when I move my hand out of the screen, the gridViewDays gridview will have the same selection with the gridViewID as the image below:

enter image description here

However, when I keep my hand touch on the screen to scroll the gridViewID, the gridViewDays will look like the image below (it does not scroll while gridViewID is scrolling)

enter image description here

So, how can I make the gridViewDays gridview vertically scrolling the same as the gridViewID? I have also tried some methods such as smoothScrollToPosition, smoothScrollByOffset, smoothScrollToPositionFromTop... inside onScroll but they do not work


Update: although the answers (@vrundpurohit's and mine) below are working, however, I have found that the app has poor performance with big data (I mean when the data list has too many items).


Solution

  • use NonScrollGridView. and wrap both in single ScrollView

    Here is code for NonScrollGridView..

    public class NonScrollGridView extends GridView {
        public NonScrollGridView(Context context) {
            super(context);
        }
    
        public NonScrollGridView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public NonScrollGridView(Context context, AttributeSet attrs,
                int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(
                    Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
            super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
            ViewGroup.LayoutParams params = getLayoutParams();
            params.height = getMeasuredHeight();
        }
    }
    

    EDIT: Here is how you can do this.

    <ScrollView android:id="@+id/scroll" ... >
    
        <NonScrollGridView />
    
        <RelativeLayout android:id="@+id/head1" ... > 
            <!-- header for 1st GridView --> 
        </RelativeLayout> 
    
        <HorizontalScrollView ... >
    
            <HorizontalScrollView ... >
    
                <NonScrollGridView />
    
                 <RelativeLayout android:id="@+id/head2" ... > 
                    <!-- header for 1st GridView --> 
                </RelativeLayout> 
    
            </HorizontalScrollView>
    
        </HorizontalScrollView>
    
    </ScrollView>
    

    Now for making both headers sticky.. do following.

    scroll.getViewTreeObserver().addOnScrollChangedListener(
                    new ViewTreeObserver.OnScrollChangedListener() {
                        public void onScrollChanged() {
                            ViewCompat.setTranslationY(head1, who.getScrollY());
                            ViewCompat.setTranslationY(head2, who.getScrollY());
                        }
                    });
    

    Happy Coding..