Scrollview вертикальный и горизонтальный в Android


152

Я очень устал искать решение для вертикальной и горизонтальной прокрутки.

Я прочитал, что в фреймворке нет никаких представлений / макетов, которые реализуют эту функцию, но мне нужно что-то вроде этого:

Мне нужно определить макет внутри другого, дочерний макет должен реализовывать прокрутку по вертикали / горизонтали для перемещения.

Изначально реализован код, который перемещал макет пиксель за пикселем, но я думаю, что это не правильный путь. Я попробовал это с ScrollView и Horizontal ScrollView, но ничего не работает так, как я хочу, потому что он реализует только вертикальную или горизонтальную прокрутку.

Canvas - это не мое решение, потому что мне нужно присоединить слушателей к чьим-то дочерним элементам.

Что я могу сделать?



14
Абсолютно смешно, что это не доступно из коробки с Android. Мы работали с полдюжины других технологий пользовательского интерфейса, и неслыханно не иметь такой базовой вещи, как горизонтальная / вертикальная прокрутка контейнера.
— flexicious.com

Итак, наконец, мы написали свой собственный для нашей таблицы данных: androidjetpack.com/Home/AndroidDataGrid . Делает намного больше, чем просто горизонтальная / вертикальная прокрутка. Но идея заключается в том, чтобы обернуть HScroller в вертикальный скроллер. Вы также должны переопределить методы добавления / удаления дочерних методов, чтобы указать внутренний скроллер и некоторые другие махинации, но это работает.
— flexicious.com

Ответы:


91

Смешав некоторые из предложенных выше предложений, удалось найти хорошее решение:

Пользовательский ScrollView:

package com.scrollable.view;

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

public class VScroll extends ScrollView {

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

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

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

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return false;
    }
}

Пользовательский HorizontalScrollView:

package com.scrollable.view;

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

public class HScroll extends HorizontalScrollView {

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

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

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

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return false;
    }
}

ScrollableImageActivity:

package com.scrollable.view;

import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.widget.HorizontalScrollView;
import android.widget.ScrollView;

public class ScrollableImageActivity extends Activity {

    private float mx, my;
    private float curX, curY;

    private ScrollView vScroll;
    private HorizontalScrollView hScroll;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        vScroll = (ScrollView) findViewById(R.id.vScroll);
        hScroll = (HorizontalScrollView) findViewById(R.id.hScroll);

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float curX, curY;

        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN:
                mx = event.getX();
                my = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                curX = event.getX();
                curY = event.getY();
                vScroll.scrollBy((int) (mx - curX), (int) (my - curY));
                hScroll.scrollBy((int) (mx - curX), (int) (my - curY));
                mx = curX;
                my = curY;
                break;
            case MotionEvent.ACTION_UP:
                curX = event.getX();
                curY = event.getY();
                vScroll.scrollBy((int) (mx - curX), (int) (my - curY));
                hScroll.scrollBy((int) (mx - curX), (int) (my - curY));
                break;
        }

        return true;
    }

}

расположение:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <com.scrollable.view.VScroll android:layout_height="fill_parent"
        android:layout_width="fill_parent" android:id="@+id/vScroll">
        <com.scrollable.view.HScroll android:id="@+id/hScroll"
            android:layout_width="fill_parent" android:layout_height="fill_parent">
            <ImageView android:layout_width="fill_parent" android:layout_height="fill_parent" android:src="@drawable/bg"></ImageView>
        </com.scrollable.view.HScroll>
    </com.scrollable.view.VScroll>

</LinearLayout>

Это отличный ответ, у меня были другие элементы прокрутки в представлении, которые также нужно было изменить. Все, что мне нужно было сделать, это вызвать их прокрутку вместе с остальными.
— Т. Маркл

1
Большой! Несколько корректировок, чтобы учесть скорость, и это было бы идеально (кроме того, LinearLayoutя думаю , вам не нужно начальное значение )
— Ромуальд Брюне

Привет, Махди, дай мне знать, на каком элементе управления / виджете ты переустанавливал сенсорный слушатель в классе активности.
— Шахили

Извините за задержку @DeepakSharma. Я не зарегистрировал слушателя касания в действии, я просто переопределил onTouchEvent действия.
— Махди Хиджази

@MahdiHijazi Можете ли вы добавить свой код горизонтальной прокрутки и вертикальной прокрутки в github.com/MikeOrtiz/TouchImageView, это успешно масштабирует изображение при нажатии кнопки, но не
— удается

43

Поскольку это, кажется, первый результат поиска в Google для "Android вертикальный + горизонтальный ScrollView", я подумал, что должен добавить это здесь. Мэтт Кларк создал собственный вид на основе источника Android, и он, кажется, отлично работает: двумерный ScrollView

Помните, что у класса на этой странице есть ошибка, вычисляющая горизонтальную ширину представления. Исправление Мануэля Хилти в комментариях:

Решение. Замените оператор в строке 808 следующим текстом:

final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.leftMargin + lp.rightMargin, MeasureSpec.UNSPECIFIED);


Изменить: Ссылка больше не работает, но вот ссылка на старую версию поста .


3
Спасибо, это то, что я искал.
— Андрей Агибалов

1
Это работает как шарм после некоторой незначительной модификации для меня. Я переопределен fillViewportи onMeasureточно так же, как источник ScrollView (v2.1). Я также изменил два первых конструктора с помощью: this( context, null );и this(context, attrs, R.attr.scrollViewStyle);. При этом я могу использовать полосу прокрутки, используя setVerticalScrollBarEnabledи setHorizontalScrollBarEnabledв коде.
— olivier_sdg

Прекрасно работает и для меня! Большое спасибо!
— Авио

но как получить размер большого пальца и положение прокрутки снизу по вертикали и справа в горизонтальном режиме
— Ravi

10
Похоже, ссылка исчезла.
— loeschg

28

Я нашел лучшее решение.

XML: (design.xml)

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent">
  <FrameLayout android:layout_width="90px" android:layout_height="90px">
    <RelativeLayout android:id="@+id/container" android:layout_width="fill_parent" android:layout_height="fill_parent">        
    </RelativeLayout>
</FrameLayout>
</FrameLayout>

Java-код:

public class Example extends Activity {
  private RelativeLayout container;
  private int currentX;
  private int currentY;

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.design);

    container = (RelativeLayout)findViewById(R.id.container);

    int top = 0;
    int left = 0;

    ImageView image1 = ...
    RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
    layoutParams.setMargins(left, top, 0, 0);               
    container.addView(image1, layoutParams);

    ImageView image2 = ...
    left+= 100;
    RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
    layoutParams.setMargins(left, top, 0, 0);               
    container.addView(image2, layoutParams);

    ImageView image3 = ...
    left= 0;
    top+= 100;
    RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
    layoutParams.setMargins(left, top, 0, 0);               
    container.addView(image3, layoutParams);

    ImageView image4 = ...
    left+= 100;     
    RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
    layoutParams.setMargins(left, top, 0, 0);               
    container.addView(image4, layoutParams);
  }     

  @Override 
  public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            currentX = (int) event.getRawX();
            currentY = (int) event.getRawY();
            break;
        }

        case MotionEvent.ACTION_MOVE: {
            int x2 = (int) event.getRawX();
            int y2 = (int) event.getRawY();
            container.scrollBy(currentX - x2 , currentY - y2);
            currentX = x2;
            currentY = y2;
            break;
        }   
        case MotionEvent.ACTION_UP: {
            break;
        }
    }
      return true; 
  }
}

Это работает !!!

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


Только что попробовал, и это работает очень хорошо! У него нет индикаторов прокрутки или броска, но поскольку это низкоуровневый подход, эти вещи могут быть построены поверх этого довольно легко. Спасибо!
— ubzack

У меня тоже получалось хорошо для написания двухмерного прокручиваемого представления, которое выводило вывод в реальном времени из результата удаленных команд SSH по сети.
— Pilcrowpipe

@Kronos: Что делать, если я хочу только прокрутить горизонтально? Каким должен быть мой параметр y в container.scrollBy (currentX - x2, currentY - y2). Есть ли способ, где мы можем сделать это прокручивать только горизонтально?
— Эшвин

@Kronos: он прокручивается слишком далеко. Есть ли способ остановить прокрутку, когда достигнут конец?
— Эшвин

Кнопки не прокручиваются, почему?
— Салих Каллай

25

Я им пользуюсь и отлично работает

<?xml version="1.0" encoding="utf-8"?>
<ScrollView android:id="@+id/ScrollView02" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content"
            xmlns:android="http://schemas.android.com/apk/res/android">
<HorizontalScrollView android:id="@+id/HorizontalScrollView01" 
                      android:layout_width="wrap_content" 
                      android:layout_height="wrap_content">
<ImageView android:id="@+id/ImageView01"
           android:src="@drawable/pic" 
           android:isScrollContainer="true" 
           android:layout_height="fill_parent" 
           android:layout_width="fill_parent" 
           android:adjustViewBounds="true">
</ImageView>
</HorizontalScrollView>
</ScrollView>

Ссылка на источник находится здесь: Android-spa


2
В лучшем случае работает "просто отлично" для статического содержимого. Несколько инженеров Google сказали, что у того, что вы пытаетесь использовать, будут проблемы ( groups.google.com/group/android-developers/browse_frm/thread/… и groups.google.com/group/android-beginners/browse_thread/thread/ … )
— CommonsWare

4
@CommonsWare: при всем уважении к блестящим инженерам Google, в тех темах, которые они сказали вам переписать с нуля свой собственный компонент: это лучший способ. Конечно, это правда. Но они не говорят, что это решение плохое, и если оно работает ... Может быть, для них это займет минуту, для меня лучше написать какой-нибудь xml с использованием существующих компонентов. Что значит «статический контент»? Я использую кнопку и изображения.
— Redax

3
Под «статическим контентом» я подразумеваю вещи, которые сами по себе не связаны с сенсорными событиями. Моя точка зрения - для других, читающих этот ответ - заключается в том, что, хотя ваше решение может работать для одного ImageViewконтента в качестве прокручиваемого контента, оно может работать или не работать для произвольного другого контента, и что Google считает, что с вашим подходом будут проблемы. Мнение Google также имеет значение в отношении будущего развития - они, возможно, не пытаются убедиться, что ваш подход работает в долгосрочной перспективе, если они советуют людям не использовать его в первую очередь.
— CommonsWare

Это работает нормально для меня ... Я освежать в ImageView в этом Scrollview используя нить
— Сону томас

19

Мое решение основано на ответе Махди Хиджази , но без каких-либо пользовательских представлений:

Планировка:

<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/scrollHorizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ScrollView 
        android:id="@+id/scrollVertical"
        android:layout_width="wrap_content"
        android:layout_height="match_parent" >

        <WateverViewYouWant/>

    </ScrollView>
</HorizontalScrollView>

Код (onCreate / onCreateView):

    final HorizontalScrollView hScroll = (HorizontalScrollView) value.findViewById(R.id.scrollHorizontal);
    final ScrollView vScroll = (ScrollView) value.findViewById(R.id.scrollVertical);
    vScroll.setOnTouchListener(new View.OnTouchListener() { //inner scroll listener         
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            return false;
        }
    });
    hScroll.setOnTouchListener(new View.OnTouchListener() { //outer scroll listener         
        private float mx, my, curX, curY;
        private boolean started = false;

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            curX = event.getX();
            curY = event.getY();
            int dx = (int) (mx - curX);
            int dy = (int) (my - curY);
            switch (event.getAction()) {
                case MotionEvent.ACTION_MOVE:
                    if (started) {
                        vScroll.scrollBy(0, dy);
                        hScroll.scrollBy(dx, 0);
                    } else {
                        started = true;
                    }
                    mx = curX;
                    my = curY;
                    break;
                case MotionEvent.ACTION_UP: 
                    vScroll.scrollBy(0, dy);
                    hScroll.scrollBy(dx, 0);
                    started = false;
                    break;
            }
            return true;
        }
    });

Вы можете изменить порядок просмотра прокрутки. Просто измените их порядок в макете и в коде. И, очевидно, вместо WeverViewYouWant вы помещаете макет / представления, которые вы хотите прокрутить в обоих направлениях.


Я настоятельно рекомендую вернуть false в hScroll.setOnTouchListener. Когда я изменил на false, список начал прокручиваться «плавно автоматически» на палец вверх в обоих направлениях.
— Грета Радисаускайте

1
Работает при первой горизонтальной прокрутке. Когда вертикаль первая, идет только вертикально
— Ирхала

6

Вариант № 1: Вы можете придумать новый дизайн пользовательского интерфейса, который не требует одновременной горизонтальной и вертикальной прокрутки.

Вариант № 2: Вы можете получить исходный код ScrollViewи HorizontalScrollViewузнать, как его реализовала основная команда Android, и создать свою собственную BiDirectionalScrollViewреализацию.

Вариант № 3: Вы можете избавиться от зависимостей, которые требуют от вас использовать систему виджетов и рисовать прямо на холсте.

Вариант № 4: Если вы наткнулись на приложение с открытым исходным кодом, которое, кажется, реализует то, что вы ищете, посмотрите, как они это сделали.


6

Попробуй это

<?xml version="1.0" encoding="utf-8"?>
<ScrollView android:id="@+id/Sview" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content"
        xmlns:android="http://schemas.android.com/apk/res/android">
<HorizontalScrollView 
   android:id="@+id/hview" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content">
<ImageView .......
 [here xml code for image]
</ImageView>
</HorizontalScrollView>
</ScrollView>

6

Поскольку ссылка на двухмерное прокручивание не работает, я не смог ее получить, поэтому создал свой собственный компонент. Он справляется со сбрасыванием и работает правильно для меня. Единственным ограничением является то, что wrap_content может не работать должным образом для этого компонента.

https://gist.github.com/androidseb/9902093


2

У меня есть решение вашей проблемы. Вы можете проверить код ScrollView, он обрабатывает только вертикальную прокрутку, игнорирует горизонтальную и изменить это. Мне нужен был вид, похожий на веб-просмотр, поэтому я модифицировал ScrollView, и он хорошо работал для меня. Но это может не соответствовать вашим потребностям.

Дайте мне знать, на какой пользовательский интерфейс вы ориентируетесь.

С Уважением,

Рави Пандит


1

Играя с кодом, вы можете поместить HorizontalScrollView в ScrollView. Таким образом, вы можете использовать два метода прокрутки в одном представлении.

Источник: http://androiddevblog.blogspot.com/2009/12/creating-two-dimensions-scroll-view.html

Я надеюсь, что это может помочь вам.


1
Это не хорошее решение. Если вы замечаете, что при перетаскивании вида вы всегда перетаскиваете либо вертикально, либо горизонтально. Вы не можете перемещаться по диагонали, используя вложенные представления прокрутки.
— Скай Келси

Что хуже, чем отсутствие диагональной прокрутки, так это отсутствие обеих полос прокрутки, остающихся на краю обзора, потому что одна вложена. Лучшее ScrollView - это ответ, а ссылка Cachapa на код Мэтта Кларка - лучшее решение в настоящее время благодаря полноте и обобщению.
— Хуперникетес

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.