Двухсторонний вид прокрутки

Я хотел бы иметь linearlayout с заголовком сверху и веб-просмотром ниже. Заголовок будет коротким, а веб-просмотр может быть длиннее и шире экрана.

Каков наилучший способ получить горизонтальную и вертикальную прокрутку? Является ли ScrollView вложенным в HorizontalScrollView хорошей идеей?

Ответ 1

Является ли ScrollView вложенным в HorizontalScrollView хорошей идеей?

Да и нет.

Да, я понимаю, что ScrollView и HorizontalScrollView могут быть вложенными.

Нет, AFAIK, ни ScrollView, ни HorizontalScrollView не работают с WebView.

Я предлагаю вам установить WebView на экран.

Ответ 3

есть другой способ. moddified HorizontalScrollView в качестве оболочки для ScrollView. normal HorizontalScrollView, когда события прикосновения не пересылают их на 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);
    }

Ответ 4

Я очень долго искал эту работу и, наконец, нашел эту тему здесь. ответ wasikuss приблизился к решению, но все же он не работал должным образом. Вот как это работает очень хорошо (по крайней мере для меня (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;

... и как это работает. По крайней мере, для меня.

Ответ 5

Существует простой способ: В вашей деятельности вы получите ссылку на внешний 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;
}

Можно утверждать, что это решение немного хаки. Но он отлично поработал у меня в нескольких приложениях!

Ответ 6

Позднее ответить, но, надеюсь, может быть полезно кому-то. Вы можете проверить droid-uiscrollview. Это в значительной степени основано на ответе @MrCeeJ, но мне показалось, что у меня возникли проблемы с получением фактического содержимого. Поэтому я вытащил последний источник из HorizontalScrollView и ScrollView, чтобы создать droid-uiscrollview. Есть несколько оставшихся todo's, которые я не получил, чтобы закончить, но достаточно, чтобы контент мог прокручивать как по горизонтали, так и по вертикали в одно и то же время введите описание изображения здесь

Ответ 7

Я попробовал как wasikuss, так и user1684030, и мне пришлось адаптировать их из-за одного предупреждения log: 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);

Ответ 8

На какое-то время я пытался найти решения здесь, но тот, который работал лучше всего, все еще имел одну проблему: он съел все события, ни один из них не перешел к элементам в скроллере.

Итак, у меня есть... еще один ответ, в Github, и прокомментировал, по крайней мере, надежду: https://github.com/Wilm0r/giggity/blob/master/app/src/main/java/net/gaast/giggity/NestedScroller.java

Как и все решения, это вложенное HorizontalScrollview (внешнее) + 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 здесь - "InnerScroller", подклассифицированный из 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);
    }

Ответ 9

Я знаю, что вы приняли свой ответ, но может быть, это может дать вам некоторую идею.

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