Scrollview vertikal und horizontal in Android

151

Ich bin es wirklich leid, nach einer Lösung für vertikale und horizontale Bildlaufansichten zu suchen.

Ich habe gelesen, dass es im Framework keine Ansichten / Layouts gibt, die diese Funktion implementieren, aber ich brauche so etwas:

Ich muss ein Layout in einem anderen definieren, das untergeordnete Layout muss das vertikale / horizontale Scrollen zum Verschieben implementieren.

Anfangs wurde ein Code implementiert, der das Layout Pixel für Pixel verschob, aber ich denke, das ist nicht der richtige Weg. Ich habe es mit ScrollView und Horizontal ScrollView versucht, aber nichts funktioniert so, wie ich es möchte, da es nur vertikales oder horizontales Scrollen implementiert.

Canvas ist nicht meine Lösung, da ich Listener an untergeordnete Elemente anhängen muss.

Was kann ich tun?

Kronos
quelle
14
Es ist absolut lächerlich, dass dies mit Android nicht sofort verfügbar ist. Wir haben mit einem halben Dutzend anderer UI-Technologien gearbeitet, und es ist ungewöhnlich, dass es keinen so grundlegenden Gegenstand wie einen horizontalen / vertikalen Bildlaufcontainer gibt.
flexicious.com
Ok, also haben wir endlich unser eigenes für unser Datagrid geschrieben: androidjetpack.com/Home/AndroidDataGrid . Macht viel mehr als nur horizontales / vertikales Scrollen. Die Idee ist jedoch, einen HScroller in einen vertikalen Scroller zu wickeln. Sie müssen auch untergeordnete Methoden zum Hinzufügen / Entfernen überschreiben, um auf den inneren Scroller und einige andere Spielereien abzuzielen, aber es funktioniert.
flexicious.com

Antworten:

90

Mischen Sie einige der obigen Vorschläge und erhalten Sie eine gute Lösung:

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

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

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

}

das Layout:

<?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>
Mahdi Hijazi
quelle
Dies ist eine großartige Antwort. Ich hatte andere Bildlaufelemente in der Ansicht, die ebenfalls geändert werden mussten. Alles was ich tun musste, war ihren Scrollby zusammen mit den anderen auszulösen.
T. Markle
1
Toll! Ein paar Anpassungen, um die Geschwindigkeit zu berücksichtigen, und dies wäre perfekt (auch Sie brauchen nicht die Initiale, LinearLayoutdenke ich)
Romuald Brunet
Hey Mahdi, kannst du mir mitteilen, auf welchem ​​Steuerelement / Widget du den Touch Listener in der Aktivitätsklasse neu aktiviert hast?
Shachillies
Entschuldigung für die Verzögerung @DeepakSharma. Ich habe keinen Touch Listener in der Aktivität registriert, sondern nur das onTouchEvent der Aktivität überschrieben.
Mahdi Hijazi
@MahdiHijazi kann u bitte Ihren Code von Horizontales Scrollen und vertikale Scrollen in hinzufügen github.com/MikeOrtiz/TouchImageView dieser erfolgreich Bild auf Tastenklick zoomt aber auf Scroll - Bild fehlgeschlagen
Erum
43

Da dies das erste Suchergebnis in Google für "Android vertikal + horizontal ScrollView" zu sein scheint, dachte ich, ich sollte dies hier hinzufügen. Matt Clark hat eine benutzerdefinierte Ansicht erstellt, die auf der Android-Quelle basiert, und sie scheint perfekt zu funktionieren: Two Dimensional ScrollView

Beachten Sie, dass die Klasse auf dieser Seite einen Fehler beim Berechnen der horizontalen Breite der Ansicht aufweist. Ein Fix von Manuel Hilty ist in den Kommentaren:

Lösung: Ersetzen Sie die Anweisung in Zeile 808 durch Folgendes:

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


Bearbeiten: Der Link funktioniert nicht mehr, aber hier ist ein Link zu einer alten Version des Blogposts .

Cachapa
quelle
3
Danke, das habe ich gesucht.
Andrey Agibalov
1
Dies funktioniert nach einigen geringfügigen Änderungen für mich wie ein Zauber. Ich habe die ScrollView-Quelle (v2.1) neu implementiert fillViewportund onMeasuregenau wie sie. Ich habe auch die beiden ersten Konstruktoren geändert mit: this( context, null );und this(context, attrs, R.attr.scrollViewStyle);. Damit kann ich die Bildlaufleiste mit setVerticalScrollBarEnabledund setHorizontalScrollBarEnabledim Code verwenden.
olivier_sdg
Funktioniert auch bei mir super! Vielen Dank!
Avio
aber wie man die Scroll-Daumengröße und die Scroll-Position von unten vertikal und von rechts im horizontalen Modus erhält
Ravi
10
Der verlinkte Beitrag ist anscheinend verschwunden.
Loeschg
28

Ich habe eine bessere Lösung gefunden.

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

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

Das funktioniert !!!

Wenn Sie ein anderes Layout oder Steuerelement laden möchten, ist die Struktur dieselbe.

Kronos
quelle
Ich habe es gerade versucht und es funktioniert sehr gut! Es gibt keine Bildlaufanzeigen oder Schleudern, aber da dies ein Ansatz auf niedrigerer Ebene ist, könnten diese Dinge ziemlich einfach darauf aufgebaut werden. Vielen Dank!
Ubzack
Hat auch für mich gut funktioniert, um eine zweidimensionale scrollbare Ansicht zu schreiben, die Echtzeitausgaben aus dem Ergebnis von SSH-Fernbefehlen über das Netzwerk ausgibt.
Pilcrowpipe
@Kronos: Was ist, wenn ich nur horizontal scrollen möchte? Was soll mein y-Parameter in container.scrollBy sein (currentX - x2, currentY - y2)? Gibt es eine Möglichkeit, wie wir es nur horizontal scrollen lassen können?
Ashwin
@Kronos: Es scrollt zu weit. Gibt es eine Möglichkeit, die Schriftrolle zu stoppen, wenn das Ende erreicht ist?
Ashwin
Schaltflächen können nicht gescrollt werden. Warum?
Salih Kallai
25

Ich benutze es und funktioniert gut:

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

Der Quelllink ist hier: Android-Spa

Redax
quelle
2
Es funktioniert bestenfalls "gut" für statische Inhalte. Mehrere Google-Ingenieure haben angegeben, dass das, was Sie verwenden möchten, Probleme verursachen wird ( groups.google.com/group/android-developers/browse_frm/thread/… und groups.google.com/group/android-beginners/browse_thread/thread/). … ).
CommonsWare
4
@CommonsWare: Mit Respekt vor den brillanten mehreren Google-Ingenieuren haben sie in diesen Threads gesagt, dass Sie Ihre eigene Komponente von Grund auf neu schreiben sollen: Dies ist der beste Weg. Sicher ist wahr. Aber sie sagen nicht, dass diese Lösung schlecht ist, und wenn sie funktioniert ... Vielleicht dauert es für sie eine Minute, für mich ist es besser, etwas XML mit den vorhandenen Komponenten zu schreiben. Was bedeutet "statischer Inhalt"? Ich benutze Knopf und Bilder.
Redax
3
Mit "statischem Inhalt" meine ich Dinge, die selbst keine Berührungsereignisse beinhalten. Mein Punkt - für andere, die diese Antwort lesen - ist, dass Ihre Lösung zwar für einen einzelnen ImageViewals scrollbaren Inhalt funktioniert, aber möglicherweise für beliebige andere Inhalte funktioniert oder nicht, und dass Google glaubt, dass es Probleme mit Ihrem Ansatz geben wird. Die Meinung von Google ist auch im Hinblick auf die zukünftige Entwicklung von Bedeutung. Möglicherweise versuchen sie nicht, sicherzustellen, dass Ihr Ansatz auf lange Sicht funktioniert, wenn sie den Nutzern raten, ihn überhaupt nicht zu verwenden.
CommonsWare
Das funktioniert gut für mich ... Ich aktualisiere eine Bildansicht in dieser Bildlaufansicht mit einem Thread
Sonu Thomas
19

Meine Lösung basiert auf der Antwort von Mahdi Hijazi , jedoch ohne benutzerdefinierte Ansichten:

Layout:

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

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

Sie können die Reihenfolge der Bildlaufansichten ändern. Ändern Sie einfach ihre Reihenfolge im Layout und im Code. Und natürlich setzen Sie anstelle von WateverViewYouWant das Layout / die Ansichten ein, mit denen Sie in beide Richtungen scrollen möchten.

Yan.Yurkin
quelle
Ich empfehle dringend, false in hScroll.setOnTouchListener zurückzugeben. Als ich zu false wechselte, begann die Liste mit dem Finger in beide Richtungen "reibungslos automatisch" zu scrollen.
Greta Radisauskaite
1
Funktioniert, wenn zuerst horizontal gescrollt wird. Wenn vertikal zuerst ist, geht nur vertikal
Irhala
6

Option 1: Sie können ein neues UI-Design entwickeln, das kein gleichzeitiges horizontales und vertikales Scrollen erfordert.

Option 2: Sie können den Quellcode für ScrollViewund erhalten HorizontalScrollView, erfahren, wie das Android-Kernteam diese implementiert hat, und Ihre eigene BiDirectionalScrollViewImplementierung erstellen .

Option 3: Sie können die Abhängigkeiten beseitigen, für die Sie das Widget-System verwenden müssen, und direkt auf die Leinwand zeichnen.

Option 4: Wenn Sie auf eine Open-Source-Anwendung stoßen, die das zu implementieren scheint, wonach Sie suchen, schauen Sie nach, wie sie es getan haben.

CommonsWare
quelle
6

Versuche dies

<?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>
MBMJ
quelle
6

Da der Link "Zweidimensionale Bildlaufansicht" nicht mehr verfügbar ist, konnte ich ihn nicht erhalten und habe meine eigene Komponente erstellt. Es handhabt Schleudern und funktioniert richtig für mich. Die einzige Einschränkung besteht darin, dass wrap_content für diese Komponente möglicherweise nicht ordnungsgemäß funktioniert.

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

androidseb
quelle
2

Ich habe eine Lösung für Ihr Problem. Sie können den ScrollView-Code überprüfen, der nur vertikales Scrollen verarbeitet, den horizontalen ignoriert und diesen ändert. Ich wollte eine Ansicht wie eine Webansicht, also modifizierte ich ScrollView und es funktionierte gut für mich. Dies entspricht jedoch möglicherweise nicht Ihren Anforderungen.

Lassen Sie mich wissen, auf welche Art von Benutzeroberfläche Sie abzielen.

Grüße,

Ravi Pandit

Ravi Pandit
quelle
1

Wenn Sie mit dem Code spielen, können Sie eine HorizontalScrollView in eine ScrollView einfügen. Dadurch können Sie die beiden Bildlaufmethoden in derselben Ansicht haben.

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

Ich hoffe das könnte dir helfen.

Eriatolc
quelle
1
Dies ist keine gute Lösung. Wenn Sie bemerken, dass Sie beim Ziehen der Ansicht immer entweder vertikal oder horizontal ziehen. Sie können mit verschachtelten Bildlaufansichten nicht entlang einer Diagonale ziehen.
Sky Kelsey
Was schlimmer ist als das Fehlen eines diagonalen Bildlaufs, ist das Fehlen beider Bildlaufleisten am Ansichtsrand, da eine verschachtelt ist. Eine bessere ScrollView ist die Antwort und Cachapas Link zu Matt Clarks Code ist aufgrund der Vollständigkeit und Verallgemeinerung derzeit die beste Lösung.
Huperniketes