Scrollview вертикальний і горизонтальний в android


152

Я дуже втомився шукати рішення для вертикального та горизонтального Scrollview.

Я читав, що в рамках не існує жодних поглядів / макетів, які б реалізували цю функцію, але мені потрібно щось подібне:

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

Спочатку реалізований код, який переміщав макет пікселя за пікселем, але я думаю, що це не правильний шлях. Я спробував це з ScrollView та Horizontal ScrollView, але нічого не працює так, як я хочу, тому що він реалізує лише вертикальну або горизонтальну прокрутку.

Полотно - це не моє рішення, тому що мені потрібно приєднувати слухачів до деяких дочірніх елементів.

Що я можу зробити?



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

Гаразд, таким чином, нарешті, ми написали власну для своєї datagrid: 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;
    }
}

The 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напевно , вам не потрібно )
Romuald Brunet

Ей, Махді, чи можете ви мені повідомити, на якому контрольному / віджеті ви ввійшли сенсорний слухач у класі активності.
Шахіллі

Вибачте за затримку @DeepakSharma. Я не зареєстрував жодного слухача дотику до активності, я просто перекрив onTouchEvent діяльності.
Махді Хіджазі

@MahdiHijazi Ви можете, будь ласка, додати свій код горизонтальної прокрутки та вертикальної прокрутки в github.com/MikeOrtiz/TouchImageView це успішно масштабування зображення при натисканні кнопки, але не вдалося прокрутити зображення
Erum

43

Оскільки це, здається, є першим результатом пошуку в Google для "Android вертикальний + горизонтальний ScrollView", я подумав, що слід додати це тут. Метт Кларк створив спеціальний вигляд на основі джерела Android, і він, здається, працює чудово: Двомірний ScrollView

Слідкуйте, що клас на цій сторінці має помилку, що обчислює горизонтальну ширину перегляду. Виправлення Мануеля Hilty - у коментарях:

Рішення: Замініть оператор у рядку 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

Чудово працює і для мене! Велике спасибі!
Авіо

але як отримати розмір великого пальця прокрутки та положення прокрутки знизу вертикально та праворуч у горизонтальному режимі
Раві

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 у контейнері.scrollBy (currentX - x2, currentY - y2). Чи є спосіб, коли ми можемо змусити його прокручувати лише горизонтально?
Ешвін

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

Кнопки не прокручуються, чому?
salih kallai

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

Це добре працює для мене ... Я оновлюю перегляд зображень у цьому прокручуванні, використовуючи нитку
Sonu thomas

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

Ви можете змінити порядок прокрутки. Просто змініть їх порядок у макеті та в коді. І очевидно, замість WateverViewYouWant ви ставите макет / представлення, які хочете прокрутити обидва напрямки.


Я настійно пропоную повернути помилку в 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

оскільки двовимірне посилання Scrollview мертве, я не міг його отримати, тому я створив власний компонент. Він поводиться з льотом і працює належним чином для мене. Єдине обмеження полягає в тому, що 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 - це відповідь, а посилання Качапи на код Метта Кларка - найкраще рішення на даний момент завдяки повноті та узагальненості.
Huperniketes

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.