60

I have a problem with a ScrollView that has inside of it a personalized GridView and other tipe of views.The first time I start the Activity, the ScrollView starts at its top, but if I visit the Activity other times the ScrollView starts at the beginning of the GridView.I used the class ExpandableHeightGridView found in this link for my GridView. The xml code for the Activity layout is this one:

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scrollViewLuogo"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#fff" >

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#fff" >

    <LinearLayout
        android:id="@+id/linearLayout2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >

        <ImageView
            android:id="@+id/imageView1"
            android:layout_width="150dp"
            android:layout_height="100dp"
            android:layout_marginLeft="14dp"
            android:layout_marginRight="10dp"
            android:layout_marginTop="10dp"
            android:adjustViewBounds="true"
            android:maxHeight="200dp"
            android:maxWidth="200dp"
            android:scaleType="fitXY"
            android:src="@android:drawable/ic_menu_gallery" />

        <LinearLayout
            android:id="@+id/linearLayout1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/nomeTVActivityLuogo"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textStyle="bold"
                android:textSize="17sp"
                android:textColor="#005788" />

            <TextView
                android:id="@+id/indirizzoTVActivityLuogo"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </LinearLayout>
    </LinearLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/linearLayout2"
        android:layout_marginTop="10dp"
        android:orientation="vertical" >

        <ImageView
            android:id="@+id/imageViewMappaLuogo"
            android:layout_width="wrap_content"
            android:layout_height="60dp"
            android:layout_marginBottom="10dp"
            android:layout_marginLeft="14dp"
            android:layout_marginRight="14dp"
            android:layout_marginTop="5dp"
            android:scaleType="fitXY"
            android:src="@drawable/sfondo_mappa" />

        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="14dp"
            android:text="Immagini"
            android:textColor="#97d6f9" />

        <View
            android:layout_width="fill_parent"
            android:layout_height="2dp"
            android:layout_marginLeft="14dp"
            android:layout_marginRight="14dp"
            android:layout_marginTop="5dp"
            android:background="#97d6f9" />

        <com.example.mappine.ExpandableHeightGridView
            android:id="@+id/gridViewLuogo"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_marginBottom="10dp"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:layout_marginTop="10dp"
            android:numColumns="3" >
        </com.example.mappine.ExpandableHeightGridView>

    </LinearLayout>

</RelativeLayout>

I've tried using the code scrollView.fullScroll(ScrollView.FOCUS_UP); but it didn't work.And even with scrollView.scrollTo(0, 0); I didn't have success. The only code that worked was:

    scrollView.post(new Runnable() 
      { 
         public void run() { 
             scrollViewLuogo.fullScroll(ScrollView.FOCUS_UP); 
         } 
      });

but it makes a fast animation from the top of the GridView to the top of the screen and i don't like it.

Any suggestion??

Community
  • 1
  • 1
user2328149
  • 737
  • 1
  • 8
  • 14

12 Answers12

182

Solution:

Ok sorry if it is a late reply but I stumbled upon the same issue (only that I was using ListView instead) and with a bit of trial and error I found the solution to this:

Basically the problem lies in the fact that the GridView/ListView child automatically requests parent focus (ScrollView) when you "hack" and resize its content with ExpandableHeightGridView, which happens after layout is rendered, and hence you see that animation when trying to scroll it up with scrollTo() (happens AFTER layout is created and AFTER gridView is resized so any generic callback is useless to handle this programatically).

So then, the simplest solution I found was to simply disable focusable property on the ListView/GridView with:

listView.setFocusable(false);

That way when you first enter the activity, focus will default and not rely on Listview/GridView.

And all working fine =)

SuppressWarnings
  • 4,134
  • 4
  • 25
  • 33
22

The easiest way is to add on the parent ScrollView the following xml attributes:

android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true"

That means that the ScrollView as a whole will be the one getting focus instead of any inner container when you launch the activity. This is also useful, for example, when you have an edit text inside your layout and you don't want it to get focus immediately and popup the keyboard when entering a screen (It's the same principle).

So, your ScrollView on top of your layout would look like this:

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scrollViewLuogo"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:descendantFocusability="beforeDescendants"
    android:focusableInTouchMode="true"
    android:background="#fff" >
Disruption
  • 357
  • 2
  • 11
  • 4
    not working for me, i have a recyclerview inside scrollview – Kai Wang Feb 05 '16 at 19:06
  • 3
    RecyclerView is a special case, same as ListView. In general, having 2 scrollable containers one inside of each other (ListView or RecyclerView inside a ScrollView) is a nest of problems and issues that lead to no easy solution. If you have an EditText, inside a RecyclerView, inside a ScrollView, things get very nasty, and the easy solution (Descendant focusability) doesn't apply easily, in part because of how scrolling containers themselves are implemented. – Disruption Mar 25 '16 at 11:00
  • Note that the **ScrollView** needs those attribute only if it is the direct parent of the other scrollable widget. Otherwise just get the parent and put the same attributes. I had **LinearLayout** as the direct parent of the **ExpandableHeightGridView** and this solution work as desired. – mimoralea Nov 16 '16 at 02:37
  • I have `ExpandableListView` inside `ScrollView` and your approach only works on Android 8 (Sony G8441), but doesn't on Android 5 (Huawei Honor 7), Android 6 (Samsung Galaxy J5), Android 7 (Huawei P9 Lite). – soshial Jun 22 '18 at 15:21
8

Simply add this line of code in the Child of ScrollView

android:focusableInTouchMode="true"
Bilal Ahmad
  • 111
  • 3
  • 4
7

Simply add this two line in your parent layout

android:focusable ="true" android:focusableInTouchMode ="true"

Try this, Hope it will work for you

5

If you want to scroll it to top/bottom programmatically, you can do this(it's from here):

For Focus to Down:

((ScrollView) findViewById(R.id.scrollView)).post(new Runnable() { 
    public void run() { 
        ((ScrollView) findViewById(R.id.scrollView)).fullScroll(View.FOCUS_DOWN); 
    } 
});

For Focus to Top

((ScrollView) findViewById(R.id.scrollView)).post(new Runnable() { 
    public void run() { 
        ((ScrollView) findViewById(R.id.scrollView)).fullScroll(View.FOCUS_UP); 
    } 
});
Naveed Ahmad
  • 6,627
  • 2
  • 58
  • 83
Piasy
  • 989
  • 13
  • 35
4

ScrollView

android:fitsSystemWindows="false"
2

Sadly the example you followed is extremely poor. ListViews and GridViews should never be placed into ScrollViews.

ScrollView's purpose is to give infinite height to its child view. List/GridView's purpose is to take a potentially very large data set and only generate enough item views to fill the space made available to it at a time for efficiency. Both are capable of scrolling their content without the other.

Putting a List/GridView in a ScrollView is saying, "unstoppable force, meet immovable object." Either you've defeated the point of List/GridView or you've defeated the point of ScrollView by combining them.

Better ways to include other content within the same scrolling region include using header/footer views with a ListView, or otherwise concatenating the contents of list adapters together into a single adapter. Creating ListView items that contain several items per row to form a grid for part of an adapter's content is straightforward.

adamp
  • 28,862
  • 9
  • 81
  • 69
  • In this activity the user can add how many textviews(for information, comments, etc.) and imageviews(his own photos) he wants.I want the entire view to be scrollable and the dinamicity of the gridview is perfect for managing images, so I like this solution. – user2328149 Jun 02 '13 at 23:38
2

Simple way to achieve this is to set up an ID to your ScrollView in XML ... let's say

scrollView_main

After that you just need to go to your java file and declare it like a normal variable above the procedure onCreate to be a global one like this

ScrollView scrollView_main;

ok, now is time to go to onCreate procedure and declare this, to get connection with your ID from XML file

scrollView_main = (ScrollView)findViewById(R.id.scrollView_main);

and finally the code that will do the trick for your ScrollView to be scrolled on top, this is your answer

scrollView_main.smoothScrollTo(0,0); //set it on top
Cristian Babarusi
  • 1,455
  • 14
  • 19
0

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 =)

Lionel T.
  • 1,194
  • 1
  • 13
  • 17
0

As a follow-up I'll share my pain as well. In my app I have a RecyclerView inside a NestedScrollView inside a CoordinatorLayout:

<CoordinatorLayout>
  <NestedScrollView id="@+id/content">

     .......

     <TextView android:id="@+id/header"/>

     <RecyclerView android:id="@+id/recyclerView"/>       

  </NestedScrollView>
</CoordinatorLayout>

Of course upon opening the activity, the page scrolled to include the recyclerView in the middle. None of the answers above worked, so I came up with the following solution:

@Override
protected void onCreate( Bundle savedInstanceState ) {
  ....
  content.setOnScrollChangeListener( new NestedScrollView.OnScrollChangeListener() {
    @Override
    public void onScrollChange( NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY ) {
      Rect scrollBounds = new Rect();
      v.getHitRect( scrollBounds );
      if( header.getLocalVisibleRect( scrollBounds ) ){
        if( View.VISIBLE != recyclerView.getVisibility() ){
          recyclerView.setVisibility( View.VISIBLE );
          fillRecyclerViewSomehow();
        }
      }
    }
  }
}

@Override
protected void onResume() {
  ...
  recyclerView.setVisibility( View.GONE ); // this effectively suppresses the focusability
}

HTH

injecteer
  • 20,038
  • 4
  • 45
  • 89
0

If nothing works, just put this as the first element of your scrollView's single child:

<EditText
   android:layout_width="0dp"
   android:layout_height="0dp"/>

example:

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

        <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
            <EditText
                    android:layout_width="0dp"
                    android:layout_height="0dp"/>
            <!-- your other views  -->
        </LinearLayout>
</ScrollView>
Kishita Variya
  • 810
  • 9
  • 19
0

I had a similar issue and something totally different worked for me. I had the following sort of view hierarchy -

<LinearLayout>
    <ImageView />
    <ScrollView>
        <TextView android:id="header_tv"/>
        <TabLayout />
        <ViewPager2 />
    </ScrollView>
</LinearLayout>

And the view used to open already scrolled till the view pager. I simply added android:focusable="true" to the first element of scroll view to fix this, as follows -

<LinearLayout>
    <ImageView />
    <ScrollView>
        <TextView 
            android:id="header_tv"
            android:focusable="true" />
        <TabLayout />
        <ViewPager2 />
    </ScrollView>
</LinearLayout>