两个方向滚动视图

时间:2010-10-05 18:18:08

标签: android scroll android-layout

我想有一个linearoutout,顶部有一个标题部分,下面是一个webview。标题会很短,webview可能比屏幕更长更宽。

获得水平和垂直滚动的最佳方法是什么? ScrollView嵌套在Horizo​​ntalScrollView中是个好主意吗?

9 个答案:

答案 0 :(得分:8)

  

ScrollView嵌套在Horizo​​ntalScrollView中是个好主意吗?

是的,没有。

是的,我的理解是ScrollViewHorizontalScrollView可以嵌套。

不,AFAIK,ScrollViewHorizontalScrollView都不与WebView合作。

我建议您在屏幕上显示WebView

答案 1 :(得分:8)

两年后,我认为开源社区可能需要你的救援: 2D Scroll View

编辑:链接不再有效,只有here is a link to an old version of the blogpost;

答案 2 :(得分:7)

还有另一种方式。 moddified Horizo​​ntalScrollView 作为 ScrollView 的包装器。普通的 Horizo​​ntalScrollView 当抓住触摸事件时不将它们转发到 ScrollView ,你只能在一次滚动。这是解决方案:

package your.package;

import android.widget.HorizontalScrollView;
import android.widget.ScrollView;
import android.view.MotionEvent;
import android.content.Context;
import android.util.AttributeSet;

public class WScrollView extends HorizontalScrollView
{
    public ScrollView sv;
    public WScrollView(Context context)
    {
        super(context);
    }

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

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

    @Override public boolean onTouchEvent(MotionEvent event)
    {
        boolean ret = super.onTouchEvent(event);
        ret = ret | sv.onTouchEvent(event);
        return ret;
  }

    @Override public boolean onInterceptTouchEvent(MotionEvent event)
    {
        boolean ret = super.onInterceptTouchEvent(event);
        ret = ret | sv.onInterceptTouchEvent(event);
        return ret;
    }
}

使用:

    @Override public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

/*BIDIRECTIONAL SCROLLVIEW*/
        ScrollView sv = new ScrollView(this);
        WScrollView hsv = new WScrollView(this);
        hsv.sv = sv;
/*END OF BIDIRECTIONAL SCROLLVIEW*/

        RelativeLayout rl = new RelativeLayout(this);
        rl.setBackgroundColor(0xFF0000FF);
        sv.addView(rl, new LayoutParams(500, 500));
        hsv.addView(sv, new LayoutParams(WRAP_CONTENT, MATCH_PARENT /*or FILL_PARENT if API < 8*/));
        setContentView(hsv);
    }

答案 3 :(得分:3)

我搜索了很长时间才完成这项工作,最后在这里找到了这个帖子。 wasikuss' answer非常接近解决方案,但仍然无法正常工作。这是它如何工作得很好(至少对我来说(Android 2.3.7))。我希望,它也适用于任何其他Android版本。

创建一个名为 VScrollView 的类:

package your.package.name;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.HorizontalScrollView;
import android.widget.ScrollView;


public class VScrollView extends ScrollView {
    public HorizontalScrollView sv;

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

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

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        sv.dispatchTouchEvent(event);
        return true;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        super.onInterceptTouchEvent(event);
        sv.onInterceptTouchEvent(event);
        return true;
    }
}

您的布局应如下所示:

<your.package.name.VScrollView
    android:id="@+id/scrollVertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <HorizontalScrollView
        android:id="@+id/scrollHorizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

        <TableLayout
            android:id="@+id/table"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:clickable="false"
            android:stretchColumns="*" >
        </TableLayout>
    </HorizontalScrollView>
</your.package.name.VScrollView>

在您的活动中,您应该执行以下操作:

hScroll = (HorizontalScrollView) findViewById(R.id.scrollHorizontal);
vScroll = (VScrollView) findViewById(R.id.scrollVertical);
vScroll.sv = hScroll;

......以及它是如何运作的。至少对我而言。

答案 4 :(得分:2)

有一个简单的解决方法: 在你的活动中获得对外部scrollView的引用(我将假设一个垂直的scrollview)和对该滚动视图的第一个子节点的引用。

Scrollview scrollY = (ScrollView)findViewById(R.id.scrollY);
LinearLayout scrollYChild = (LinearLayout)findViewById(R.id.scrollYChild);

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    scrollYChild.dispatchTouchEvent(event);
    scrollY.onTouchEvent(event);
    return true;
}

有人可能会说这个解决方案有点笨拙。但它在几个应用程序中对我很有用!

答案 5 :(得分:1)

迟到回答,但希望对某人有所帮助。 你可以看看droid-uiscrollview。这很大程度上基于@MrCeeJ's答案,但我似乎在获取实际内容时遇到了很多麻烦。因此,我从HorizontalScrollView&amp; ScrollView创建droid-uiscrollview。还剩下一些todo's,我还没有完成,但它足以让内容在水平和水平方向滚动。垂直同时 enter image description here

答案 6 :(得分:1)

我尝试了wasikussuser1684030解决方案,由于一个警告日志,我不得不调整它们:HorizontalScrollView: Invalid pointerId=-1 in onTouchEvent,因为我不喜欢这需要创建2个滚动视图。

所以这是我的班级:

public class ScrollView2D extends ScrollView {

    private HorizontalScrollView innerScrollView;

    public ScrollView2D(Context context) {
        super(context);

        addInnerScrollView(context);
    }

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


    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        if (getChildCount() == 1) {
            View subView = getChildAt(0);
            removeViewAt(0);
            addInnerScrollView(getContext());
            this.innerScrollView.addView(subView);
        } else {
            addInnerScrollView(getContext());
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean handled = super.onTouchEvent(event);
        handled |= this.innerScrollView.dispatchTouchEvent(event);
        return handled;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        super.onInterceptTouchEvent(event);
        return true;
    }


    public void setContent(View content) {
        if (content != null) {
            this.innerScrollView.addView(content);
        }
    }


    private void addInnerScrollView(Context context) {
        this.innerScrollView = new HorizontalScrollView(context);
        this.innerScrollView.setHorizontalScrollBarEnabled(false);
        addView(this.innerScrollView);
    }

}

在XML中使用它时,如果在此处设置了此滚动视图的内容,则无需执行任何操作。否则,您只需调用方法setContent(View content)即可让ScrollView2D知道其内容是什么。

例如:

// Get or create a ScrollView2D.
ScrollView2D scrollView2D = new ScrollView2D(getContext());
scrollView2D.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
addView(scrollView2D);

// Set the content of scrollView2D.
RelativeLayout testView = new RelativeLayout(getContext());
testView.setBackgroundColor(0xff0000ff);
testView.setLayoutParams(new ViewGroup.LayoutParams(2000, 2000));
scrollView2D.setContent(testView);

答案 7 :(得分:0)

有一段时间我一直在尝试这里的解决方案,但最好的解决方案仍有一个问题:它吃了所有事件,没有人通过滚动条中的元素。

所以我在Github得到了另一个答案,至少有希望得到好评:https://github.com/Wilm0r/giggity/blob/master/app/src/main/java/net/gaast/giggity/NestedScroller.java

与所有解决方案一样,它是一个嵌套的Horizo​​ntalScrollview(外部)+ ScrollView(内部),外部接收来自Android的触摸事件,而内部只在内部从外部视图接收它们。

然而,我依靠ScrollViews来决定触摸事件是否有趣并且直到他们接受它为止,不做任何事情(例如点击打开链接等)仍然可以使它成为子元素。

(此外,视图还支持缩放到我需要的缩放。)

在外部滚动条中:

@Override
public boolean onInterceptTouchEvent(MotionEvent event)
{
    if (super.onInterceptTouchEvent(event) || vscroll.onInterceptTouchEventInt(event)) {
        onTouchEvent(event);
        return true;
    }
    return false;
}

@Override
public boolean onTouchEvent(MotionEvent event)
{
    super.onTouchEvent(event);
    /* Beware: One ugliness of passing on events like this is that normally a ScrollView will
       do transformation of the event coordinates which we're not doing here, mostly because
       things work well enough without doing that.
       For events that we pass through to the child view, transformation *will* happen (because
       we're completely ignoring those and let the (H)ScrollView do the transformation for us).
     */
    vscroll.onTouchEventInt(event);
    return true;
}

这里的vscroll是&#34; InnerScroller&#34;,来自ScrollView的子类,对事件处理做了一些更改:我做了一些可怕的事情来确保直接从Android传入的触摸事件被丢弃,而是它只会从外部类中获取它们 - 然后才将它们传递给超类:

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        /* All touch events should come in via the outer horizontal scroller (using the Int
           functions below). If Android tries to send them here directly, reject. */
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        /* It will still try to send them anyway if it can't find any interested child elements.
           Reject it harder (but pretend that we took it). */
        return true;
    }

    public boolean onInterceptTouchEventInt(MotionEvent event) {
        return super.onInterceptTouchEvent(event);
    }

    public boolean onTouchEventInt(MotionEvent event) {
        super.onTouchEvent(event);
    }

答案 8 :(得分:-3)

我知道你接受了你的答案,但可能会让你有所了解。

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

<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>

<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<HorizontalScrollView
    android:layout_alignParentBottom="true"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
>
<ImageView

android:src="@drawable/device_wall"
android:scaleType="center"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</HorizontalScrollView>
</RelativeLayout>
</LinearLayout>
</ScrollView>