Scrollview verticale e orizzontale in Android


152

Sono davvero stanco di cercare una soluzione per Scrollview verticale e orizzontale.

Ho letto che non ci sono viste / layout nel framework che implementano questa funzione, ma ho bisogno di qualcosa del genere:

Devo definire un layout all'interno di altri, il layout figlio deve implementare lo scorrimento verticale / orizzontale per lo spostamento.

Inizialmente implementato un codice che spostava il layout pixel per pixel, ma penso che non sia il modo giusto. L'ho provato con ScrollView e ScrollView orizzontale ma niente funziona come voglio, perché implementa solo lo scorrimento verticale o orizzontale.

La tela non è la mia soluzione perché devo collegare gli ascoltatori a elementi figlio di qualcuno.

Cosa posso fare?



14
È assolutamente ridicolo che questo non sia disponibile immediatamente con Android. Abbiamo lavorato con mezza dozzina di altre tecnologie dell'interfaccia utente ed è inaudito non avere una cosa di base come un contenitore a scorrimento orizzontale / verticale.
— flexicious.com,

Ok, quindi finalmente abbiamo scritto il nostro per il nostro datagrid: androidjetpack.com/Home/AndroidDataGrid . Fa molto di più di un semplice scorrimento orizzontale / verticale. Ma l'idea è sostanzialmente quella di avvolgere un HScroller all'interno di uno Scroller verticale. Devi anche sostituire i metodi figlio aggiungi / rimuovi per indirizzare lo scroller interno e alcuni altri shenanigan, ma funziona.
— flexicious.com,

Risposte:


91

Mescolando alcuni dei suggerimenti precedenti, è stato possibile ottenere una buona soluzione:

ScrollView personalizzato:

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 personalizzato:

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

}

lo schema:

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

Questa è un'ottima risposta, avevo altri elementi di scorrimento nella vista che dovevano anche cambiare. Tutto quello che dovevo fare era innescare il loro scrollby insieme agli altri.
— T. Markle

1
Grande! Alcuni aggiustamenti per tenere conto della velocità, e questo sarebbe perfetto (inoltre, non hai bisogno dell'iniziale LinearLayoutsuppongo)
— Romuald Brunet,

Ehi Mahdi, puoi farmi sapere su quale controllo / widget hai riordinato l'ascoltatore touch nella classe di attività.
— Shachillies,

Ci scusiamo per il ritardo @DeepakSharma. Non ho registrato alcun listener di tocco nell'attività, ho appena ignorato onTouchEvent dell'attività.
— Mahdi Hijazi,

@MahdiHijazi puoi per favore aggiungere il tuo codice di Scorrimento orizzontale e Scorrimento verticale in github.com/MikeOrtiz/TouchImageView Questo è lo zoom con successo dell'immagine sul clic del pulsante ma non è riuscito a scorrere l'immagine
— Erum

43

Poiché questo sembra essere il primo risultato di ricerca in Google per "Android verticale + orizzontale ScrollView", ho pensato di aggiungere questo qui. Matt Clark ha creato una vista personalizzata basata sulla fonte Android e sembra funzionare perfettamente: ScrollView bidimensionale

Attenzione che la classe in quella pagina ha un bug che calcola la larghezza orizzontale della vista. Una correzione di Manuel Hilty è nei commenti:

Soluzione: sostituire l'istruzione sulla riga 808 con il seguente:

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


Modifica: il link non funziona più, ma ecco un link a una vecchia versione del post sul blog .


3
Grazie, è quello che stavo cercando.
— Andrey Agibalov,

1
Questo funziona come un incantesimo dopo alcune piccole modifiche per me. Ho reimplementato fillViewported onMeasureesattamente come la fonte ScrollView (v2.1). Ho anche cambiato il primo costruttore con due: this( context, null );e this(context, attrs, R.attr.scrollViewStyle);. Con ciò, posso usare la barra di scorrimento usando setVerticalScrollBarEnablede setHorizontalScrollBarEnablednel codice.
— olivier_sdg,

Funziona alla grande anche per me! Grazie mille!
— Avio,

ma come ottenere la dimensione del pollice e la posizione di scorrimento dal basso verticale e da destra in modalità orizzontale
— Ravi

10
Sembra che il post collegato sia scomparso.
— loeschg

28

Ho trovato una soluzione migliore.

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>

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

Funziona così !!!

Se si desidera caricare altri layout o controlli, la struttura è la stessa.


Ho appena provato questo e funziona molto bene! Non ha indicatori di scorrimento o di lancio, ma poiché si tratta di un approccio di livello inferiore, queste cose potrebbero essere costruite sopra abbastanza facilmente. Grazie!
— ubzack,

Ha funzionato bene anche per me per la scrittura di una vista scorrevole bidimensionale che ha prodotto l'output in tempo reale dal risultato dei comandi remoti SSH sulla rete.
— pilcrowpipe,

@Kronos: cosa succede se voglio solo scorrere in orizzontale? Quale dovrebbe essere il mio parametro y in container.scrollBy (currentX - x2, currentY - y2). Esiste un modo in cui possiamo farlo scorrere solo in orizzontale?
— Ashwin,

@Kronos: scorre troppo lontano. C'è un modo per fermare lo scorrimento quando viene raggiunta la fine?
— Ashwin,

I pulsanti non sono scorrevoli, perché?
— salih kallai

25

Lo uso e funziona benissimo:

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

Il link di origine è qui: Android-spa


2
Funziona "bene" per contenuti statici, nella migliore delle ipotesi. Numerosi ingegneri di Google hanno affermato che ciò che si sta tentando di utilizzare avrà problemi ( groups.google.com/group/android-developers/browse_frm/thread/… e groups.google.com/group/android-beginners/browse_thread/thread/ ... ).
— CommonsWare,

4
@CommonsWare: con rispetto per i geniali ingegneri multipli di Google, in quei thread ti hanno detto di riscrivere da zero il tuo componente: questo è il modo migliore. Sicuramente è vero. Ma non dicono che questa soluzione sia male, e se funziona ... Forse per loro ci vuole un minuto, per me è meglio scrivere un po 'di XML usando i componenti esistenti. Cosa significa "contenuto statico"? Uso pulsante e immagini.
— Redax,

3
Per "contenuto statico" intendo cose che non implicano eventi tattili stessi. Il mio punto - per gli altri che leggono questa risposta - è che mentre la tua soluzione può funzionare per un singolo ImageViewcome contenuto scorrevole, può o meno funzionare per altri contenuti arbitrari e Google ritiene che ci saranno problemi con il tuo approccio. L'opinione di Google è importante anche per lo sviluppo futuro: potrebbero non cercare di assicurarsi che il tuo approccio funzioni a lungo termine, se consigliano alle persone di non utilizzarlo in primo luogo.
— CommonsWare

Questo sta funzionando bene per me ... Sto aggiornando un'immagine visualizzata in questa vista di scorrimento usando un thread
— Sonu Thomas

19

La mia soluzione basata sulla risposta Mahdi Hijazi , ma senza visualizzazioni personalizzate:

Disposizione:

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

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

È possibile modificare l'ordine delle visualizzazioni di scorrimento. Basta cambiare il loro ordine nel layout e nel codice. E ovviamente invece di WateverViewYouVuoi mettere il layout / le viste che vuoi scorrere in entrambe le direzioni.


Consiglio vivamente di restituire false in hScroll.setOnTouchListener. Quando sono passato a false, l'elenco ha iniziato a scorrere "auto uniformemente" sul dito verso l'alto in entrambe le direzioni.
— Greta Radisauskaite,

1
Funziona quando si scorre prima in orizzontale. Quando il primo è verticale, va solo in verticale
— Irhala,

6

Opzione n. 1: puoi creare un nuovo design dell'interfaccia utente che non richiede lo scorrimento orizzontale e verticale simultaneo.

Opzione n. 2: è possibile ottenere il codice sorgente per ScrollViewe HorizontalScrollView, imparare come il team Android centrale ha implementato quelli e creare la propria BiDirectionalScrollViewimplementazione.

Opzione n. 3: è possibile eliminare le dipendenze che richiedono l'utilizzo del sistema di widget e disegnare direttamente nell'area di disegno.

Opzione n. 4: se ti imbatti in un'applicazione open source che sembra implementare ciò che cerchi, guarda come hanno fatto.


6

Prova questo

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

poiché il collegamento Scrollview bidimensionale è morto, non sono riuscito a ottenerlo, quindi ho creato il mio componente. Gestisce il lancio e funziona correttamente per me. L'unica limitazione è che wrap_content potrebbe non funzionare correttamente per quel componente.

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


2

Ho una soluzione per il tuo problema. Puoi controllare il codice ScrollView che gestisce solo lo scorrimento verticale e ignora quello orizzontale e modificarlo. Volevo una vista come una webview, così ScrollView modificato e ha funzionato bene per me. Ma questo potrebbe non soddisfare le tue esigenze.

Fammi sapere per quale tipo di UI stai prendendo di mira.

Saluti,

Ravi Pandit


1

Giocando con il codice, puoi inserire un HorizontalScrollView in uno ScrollView. Pertanto, è possibile avere il metodo di scorrimento due nella stessa vista.

Fonte: http://androiddevblog.blogspot.com/2009/12/creating-two-dimensions-scroll-view.html

Spero che questo possa aiutarti.


1
Questa non è una buona soluzione. Se noti, quando trascini la vista, trascini sempre in verticale o in orizzontale. Non è possibile trascinare lungo una diagonale utilizzando le viste di scorrimento nidificate.
— Sky Kelsey,

Ciò che è peggio della mancanza di scorrimento diagonale è la mancanza di entrambe le barre di scorrimento che rimangono sul bordo della vista perché una è nidificata. Una migliore ScrollView è la risposta e il link di Cachapa al codice di Matt Clark è la migliore soluzione in questo momento per completezza e generalizzazione.
— Huperniketes,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.