web-dev-qa-db-fra.com

Scrollview vertical et horizontal dans Android

Je suis vraiment fatigué de chercher une solution pour Scrollview vertical et horizontal.

J'ai lu qu'il n'y avait pas de vues/mises en page dans le framework qui implémentent cette fonctionnalité, mais j'ai besoin de quelque chose comme ça:

Je dois définir une mise en page dans une autre, la mise en page enfant doit implémenter un défilement vertical/horizontal pour le déplacement.

Initialement implémenté un code qui déplaçait la mise en page pixel par pixel, mais je pense que ce n’est pas la bonne façon ... J'ai essayé avec ScrollView et Horizontal ScrollView mais rien ne fonctionne comme je le souhaite, car il ne met en œuvre que le défilement vertical ou horizontal .

Canvas n'est pas ma solution car je dois attacher des écouteurs dans des éléments enfants.

Que puis-je faire?

144
Kronos

En mélangeant certaines des suggestions ci-dessus, nous avons pu trouver une bonne solution:

ScrollView personnalisé:

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 personnalisé:

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

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

}

la disposition:

<?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>
86
Mahdi Hijazi

Comme cela semble être le premier résultat de recherche dans Google pour "Android vertical + horizontal ScrollView", j'ai pensé que je devrais ajouter ceci ici. Matt Clark a créé une vue personnalisée basée sur la source Android et semble fonctionner parfaitement: Two Dimensional ScrollView

Notez que la classe de cette page a un bogue qui calcule la largeur horizontale de la vue. Un correctif de Manuel Hilty est dans les commentaires:

Solution: remplacez l'énoncé de la ligne 808 par le suivant:

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


Edit: Le lien ne fonctionne plus mais voici un lien vers une ancienne version de blogpost .

41
Cachapa

J'ai trouvé une meilleure solution.

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>

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

Ça marche !!!

Si vous souhaitez charger une autre disposition ou un autre contrôle, la structure est la même.

28
Kronos

Je l'utilise et fonctionne bien:

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

Le lien source est ici: Android-spa

25
Redax

Ma solution basée sur Mahdi Hijazi répond , mais sans aucune vue personnalisée:

Disposition:

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

Vous pouvez changer l'ordre des vues de défilement. Il suffit de changer leur ordre dans la mise en page et dans le code. Et évidemment, au lieu de WateverViewYou, vous souhaitez placer la disposition/les vues pour lesquelles vous souhaitez faire défiler les deux directions.

16
Yan.Yurkin

Essaye ça

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

Option n ° 1: Vous pouvez créer une nouvelle conception d'interface utilisateur qui ne nécessite pas de défilement simultané horizontal et vertical.

Option n ° 2: vous pouvez obtenir le code source de ScrollView et HorizontalScrollView, apprendre comment l'équipe Android principale l'a implémentée et créer votre propre implémentation BiDirectionalScrollView.

Option n ° 3: vous pouvez vous débarrasser des dépendances qui vous obligent à utiliser le système de widgets et à dessiner directement sur le canevas.

Option n ° 4: si vous tombez sur une application open source qui semble mettre en œuvre ce que vous recherchez, regardez comment elle l'a fait.

6
CommonsWare

le lien Two Dimensional Scrollview étant mort, je n’ai pas pu l’obtenir. J'ai donc créé mon propre composant. Il gère le flinging et fonctionne correctement pour moi ... La seule restriction est que wrap_content peut ne pas fonctionner correctement pour ce composant.

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

5
androidseb

J'ai une solution à votre problème. Vous pouvez vérifier le code ScrollView qu’il gère uniquement le défilement vertical et ignore celui horizontal et le modifier. Je voulais une vue semblable à une vue Web. J'ai donc modifié ScrollView et cela a bien fonctionné pour moi. Mais cela peut ne pas convenir à vos besoins.

Faites-moi savoir quel type d'interface utilisateur que vous ciblez.

Cordialement,

Ravi Pandit

2
Ravi Pandit

En jouant avec le code, vous pouvez placer un HorizontalScrollView dans un ScrollView. Ainsi, vous pouvez avoir la méthode de deux défilement dans la même vue.

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

J'espère que cela pourrait vous aider.

1
Eriatolc

utiliser de cette façon J'ai essayé cela, je l'ai corrigé

Mettez toute votre mise en page XML à l'intérieur

<Android.support.v4.widget.NestedScrollView 

Je l'ai expliqué dans ce lien vertical recyclerView et Horizontal recyclerview scrolling scroll

0
Sirwan Rahimi