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

60

我有一个ScrollView,里面包含了个性化的GridView和其他类型的视图。第一次启动 Activity 时,ScrollView 从顶部开始,但如果我再次访问 Activity,ScrollView 就会从 GridView 的开头开始。我在我的 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>

我曾经尝试使用代码 scrollView.fullScroll(ScrollView.FOCUS_UP);,但它没有起作用。即使使用 scrollView.scrollTo(0, 0); 我也没有成功。 唯一有效的代码是:

    scrollView.post(new Runnable() 
      { 
         public void run() { 
             scrollViewLuogo.fullScroll(ScrollView.FOCUS_UP); 
         } 
      });
但是它会快速从GridView的顶部到屏幕顶部进行动画,我不喜欢这样。 有什么建议吗?
12个回答

182

解决方案:

好的,如果我的回复晚了,很抱歉。但我遇到了同样的问题(只是我使用的是ListView),经过一些尝试和错误,我找到了解决方案

基本上问题在于GridView/ListView子项在使用ExpandableHeightGridView调整大小后,会自动请求父元素焦点(滚动视图),这发生在布局渲染之后,因此当您尝试使用scrollTo()向上滚动时会看到该动画(在布局创建之后和GridView调整大小之后发生,因此任何通用回调程序都无法对其进行编程处理)。

因此,我找到的最简单的解决方法就是简单地禁用ListView/GridView中可聚焦属性:

listView.setFocusable(false);

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

而且一切正常=)


2
也适用于Webview。 - sagus_helgy
4
太棒了,感谢你的修复。我曾想知道为什么如果我在xml中设置它,它就无法工作。 - Emanuel
1
也想知道为什么它在XML中不起作用。默认行为相当奇怪,为什么有人会想要那样的行为? - greatergoodguy

22

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

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

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

因此,您位于布局顶部的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" >

4
不起作用,我在ScrollView中有一个RecyclerView。 - Kai Wang
3
RecyclerView与ListView一样,都是特殊情况。通常,将两个可滚动的容器嵌套(例如ScrollView中的ListView或RecyclerView)会导致一系列问题和难以解决的困难。 如果你在RecyclerView中嵌套了一个EditText,并且再将其放在ScrollView中,情况会非常麻烦。简单的解决方案(Descendant focusability)不易适用,部分原因是由于滚动容器本身的实现方式造成的。 - Disruption
请注意,如果ScrollView是其他可滚动小部件的直接父级,则需要这些属性。否则,只需获取父级并放置相同的属性即可。我将LinearLayout作为ExpandableHeightGridView的直接父级,并且此解决方案按预期工作。 - mimoralea
我在 ScrollView 中使用了 ExpandableListView,你的方法只能在Android 8(索尼G8441)上运行,但在Android 5(华为荣耀7)、Android 6(三星Galaxy J5)和Android 7(华为P9 Lite)上无法运行。 - soshial

8
只需在ScrollView的Child中添加以下代码即可:
android:focusableInTouchMode="true"

7

只需在您的父级布局中添加以下两行代码:

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

尝试一下,希望对您有帮助。


5

如果您想通过程序将其滚动到顶部/底部,可以这样做(来源于这里):

向下聚焦:

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

回到页面顶部

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

4

滚动视图

android:fitsSystemWindows="false"

2
简单的实现方法是在XML中为您的ScrollView设置一个ID,比如说:
scrollView_main

然后你只需要在Java文件中像声明普通变量一样,在onCreate过程之前将其声明为全局变量,如下所示:

ScrollView scrollView_main;

好的,现在是时候进入onCreate过程并声明这个变量,以便从XML文件中获取您的ID并建立连接。

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

最后,这段代码可以让你的ScrollView滚动到顶部,这就是你需要的答案

scrollView_main.smoothScrollTo(0,0); //set it on top

在所有上面的答案中,只有这个对我起作用了,谢谢兄弟。 - Siddharth Bhattacharjee

2
很遗憾,你跟随的示例非常糟糕。ListView和GridView不应该放置在ScrollView中。
ScrollView的目的是给其子视图无限高度。 List/GridView的目的是获取可能非常大的数据集,并仅生成足够的条目视图以填充每次为效率提供的可用空间。两者都能够滚动其内容而不需要彼此。
将List/GridView放在ScrollView中就像在说:“不可阻挡的力量,见到了不可移动的物体。”你要么打败了List/GridView的目的,要么通过结合它们来打败了ScrollView的目的。
在同一滚动区域中包含其他内容的更好方法包括使用ListView的header/footer视图,或将列表适配器的内容串联在一起形成单个适配器。创建每行包含多个项目以形成适配器的一部分网格的ListView项目很简单。

在这个活动中,用户可以添加任意数量的文本视图(用于信息、评论等)和图像视图(他自己的照片)。我希望整个视图都可以滚动,并且GridView的动态性非常适合管理图像,因此我喜欢这个解决方案。 - user2328149

0
作为后续,我也会分享我的痛苦。在我的应用程序中,我有一个RecyclerView,它位于NestedScrollView内部,而NestedScrollView又位于CoordinatorLayout内部:
<CoordinatorLayout>
  <NestedScrollView id="@+id/content">

     .......

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

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

  </NestedScrollView>
</CoordinatorLayout>

当然,在打开活动时,页面滚动以在中间包含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


0

我找到了一种方法,可以在ScrollView中给GridView设置固定的大小,并且启用滚动。这样,您就可以看到整个ScrollView,而不必滚动GridView的所有元素,对我来说比使用ExpandableHeightGridView更有意义。

为此,您需要实现一个扩展GridView的新类,并覆盖onTouchEvent()以调用requestDisallowInterceptTouchEvent(true)。因此,父视图将停止拦截Grid的触摸事件。

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

将其添加到具有所需特性和边距的布局中,在 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>

只需在您的活动中获取它:

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

希望能对你有所帮助 =)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接