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.