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.