Android ScrollView不是从顶部开始,而是在GridView的开头

时间:2023-01-12 21:13:08

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的问题,里面有一个个性化的GridView和其他视图。第一次启动Activity时,ScrollView从顶部开始,但如果我访问Activity其他时候ScrollView从头开始GridView.I使用了此链接中为我的GridView找到的类ExpandableHeightGridView。 Activity布局的xml代码是这样的:

<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.fullScroll(ScrollView.FOCUS_UP);但它没有用。甚至使用scrollView.scrollTo(0,0);我没有成功。唯一有效的代码是:

    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.

但是它会从GridView顶部到屏幕顶部进行快速动画,我不喜欢它。

Any suggestion??

有什么建议??

9 个解决方案

#1


142  

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:

好的抱歉,如果是迟到的回复,但我偶然发现同样的问题(只是我使用的是ListView)并且经过一些试验和错误我找到了解决方法:

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

基本上问题在于GridView / ListView子项在您“hack”并使用ExpandableHeightGridView调整其内容时会自动请求父焦点(ScrollView),这在渲染布局后会发生,因此您在尝试滚动时会看到该动画使用scrollTo()(在创建布局之后发生,并且在调整gridView后调整大小,因此任何通用回调都无法以编程方式处理此问题)。

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

那么,我发现最简单的解决方案是简单地在ListView / GridView上禁用focusable属性:

listView.setFocusable(false);

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

这样,当您第一次进入活动时,焦点将默认,而不是依赖于Listview / GridView。

And all working fine =)

一切正常=)

#2


19  

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

最简单的方法是在父ScrollView上添加以下xml属性:

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

这意味着当你启动活动时,ScrollView作为一个整体将成为焦点,而不是任何内部容器。这也很有用,例如,当您在布局中有编辑文本并且您不希望它立即获得焦点并在进入屏幕时弹出键盘(这是相同的原理)。

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

因此,您的布局顶部的ScrollView将如下所示:

<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" >

#3


3  

ScrollView

滚动型

android:fitsSystemWindows="false"

#4


3  

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); 
    } 
});

#5


2  

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

可悲的是,你所遵循的例子非常糟糕。不应将ListView和GridView放入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.

ScrollView的目的是为其子视图提供无限高度。 List / GridView的目的是获取一个可能非常大的数据集,并且只生成足够的项目视图以一次填充可用的空间以提高效率。两者都能够在没有其他内容的情况下滚动内容。

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.

在ScrollView中放置List / GridView就是说“不可阻挡的力量,遇见不可移动的对象”。要么你已经击败了List / GridView,要么你已经通过组合它们击败了ScrollView。

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.

在同一滚动区域中包含其他内容的更好方法包括使用带有ListView的页眉/页脚视图,或者将列表适配器的内容连接到一个适配器中。创建每行包含多个项目的ListView项目以形成适配器内容的一部分网格非常简单。

#6


2  

Simply add this line of code in the Child of ScrollView

只需在ScrollView的Child中添加这行代码即可

android:focusableInTouchMode="true"

#7


1  

Simply add this two line in your parent layout

只需在父布局中添加这两行即可

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

android:focusable =“true”android:focusableInTouchMode =“true”

Try this, Hope it will work for you

试试这个,希望它对你有用

#8


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.

我找到了一种在ScrollView中为GridView提供固定大小的方法,并启用滚动它。这允许您在不必滚动GridView的所有元素的情况下查看整个ScrollView,并且使用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.

为此,您必须实现一个扩展GridView的新类,并重写onTouchEvent()以调用requestDisallowInterceptTouchEvent(true)。因此,父视图将使网格拦截触摸事件。

GridViewScrollable.java:

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中,在您的布局中添加所需的特征和边距:

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

我希望它有帮助=)

#9


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中有一个RecyclerView:

<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:

当然,在打开活动时,页面会滚动到中间包含recyclerView。上面的答案都没有奏效,所以我提出了以下解决方案:

@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

HTH

#1


142  

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:

好的抱歉,如果是迟到的回复,但我偶然发现同样的问题(只是我使用的是ListView)并且经过一些试验和错误我找到了解决方法:

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

基本上问题在于GridView / ListView子项在您“hack”并使用ExpandableHeightGridView调整其内容时会自动请求父焦点(ScrollView),这在渲染布局后会发生,因此您在尝试滚动时会看到该动画使用scrollTo()(在创建布局之后发生,并且在调整gridView后调整大小,因此任何通用回调都无法以编程方式处理此问题)。

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

那么,我发现最简单的解决方案是简单地在ListView / GridView上禁用focusable属性:

listView.setFocusable(false);

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

这样,当您第一次进入活动时,焦点将默认,而不是依赖于Listview / GridView。

And all working fine =)

一切正常=)

#2


19  

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

最简单的方法是在父ScrollView上添加以下xml属性:

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

这意味着当你启动活动时,ScrollView作为一个整体将成为焦点,而不是任何内部容器。这也很有用,例如,当您在布局中有编辑文本并且您不希望它立即获得焦点并在进入屏幕时弹出键盘(这是相同的原理)。

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

因此,您的布局顶部的ScrollView将如下所示:

<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" >

#3


3  

ScrollView

滚动型

android:fitsSystemWindows="false"

#4


3  

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); 
    } 
});

#5


2  

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

可悲的是,你所遵循的例子非常糟糕。不应将ListView和GridView放入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.

ScrollView的目的是为其子视图提供无限高度。 List / GridView的目的是获取一个可能非常大的数据集,并且只生成足够的项目视图以一次填充可用的空间以提高效率。两者都能够在没有其他内容的情况下滚动内容。

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.

在ScrollView中放置List / GridView就是说“不可阻挡的力量,遇见不可移动的对象”。要么你已经击败了List / GridView,要么你已经通过组合它们击败了ScrollView。

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.

在同一滚动区域中包含其他内容的更好方法包括使用带有ListView的页眉/页脚视图,或者将列表适配器的内容连接到一个适配器中。创建每行包含多个项目的ListView项目以形成适配器内容的一部分网格非常简单。

#6


2  

Simply add this line of code in the Child of ScrollView

只需在ScrollView的Child中添加这行代码即可

android:focusableInTouchMode="true"

#7


1  

Simply add this two line in your parent layout

只需在父布局中添加这两行即可

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

android:focusable =“true”android:focusableInTouchMode =“true”

Try this, Hope it will work for you

试试这个,希望它对你有用

#8


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.

我找到了一种在ScrollView中为GridView提供固定大小的方法,并启用滚动它。这允许您在不必滚动GridView的所有元素的情况下查看整个ScrollView,并且使用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.

为此,您必须实现一个扩展GridView的新类,并重写onTouchEvent()以调用requestDisallowInterceptTouchEvent(true)。因此,父视图将使网格拦截触摸事件。

GridViewScrollable.java:

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中,在您的布局中添加所需的特征和边距:

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

我希望它有帮助=)

#9


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中有一个RecyclerView:

<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:

当然,在打开活动时,页面会滚动到中间包含recyclerView。上面的答案都没有奏效,所以我提出了以下解决方案:

@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

HTH