Scrollview vertical e horizontal no android


151

Estou realmente cansado de procurar uma solução para o Scrollview vertical e horizontal.

Eu li que não existem visualizações / layouts na estrutura que implementem esse recurso, mas preciso de algo como isto:

Eu preciso definir um layout dentro de outro, o layout filho deve implementar a rolagem vertical / horizontal para mover.

Inicialmente, implementei um código que movia o layout pixel por pixel, mas acho que não é o caminho certo. Eu tentei com ScrollView e Horizontal ScrollView, mas nada funciona como eu quero, porque implementa apenas a rolagem vertical ou horizontal.

A tela não é minha solução, pois preciso anexar ouvintes a elementos filho de alguém.

O que eu posso fazer?



14
É absolutamente ridículo que isso não esteja disponível imediatamente com o Android. Trabalhamos com meia dúzia de outras tecnologias de interface do usuário e é inédito que não haja algo tão básico quanto um contêiner de rolagem horizontal / vertical.
— Flexician.com

Ok, então finalmente escrevemos o nosso para o nosso datagrid: androidjetpack.com/Home/AndroidDataGrid . Faz muito mais do que apenas rolagem horizontal / vertical. Mas a ideia é basicamente envolver um HScroller dentro de um Scroller Vertical. Você também precisa substituir os métodos filho de adicionar / remover para atingir o scroller interno e algumas outras travessuras, mas funciona.
— flexicious.com

Respostas:


90

Misturando algumas das sugestões acima, foi possível obter uma boa solução:

ScrollView personalizado:

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

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

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

}

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

Esta é uma ótima resposta, eu tinha outros elementos de rolagem na exibição que também precisavam mudar. Tudo o que eu precisava fazer era acionar o deslocamento junto com os outros.
— T. Markle

1
Ótimo! Alguns ajustes para levar em conta a velocidade, e isso seria perfeito (também, você não precisa a inicial LinearLayoutpalpite I)
— Romuald Brunet

Hey Mahdi, você pode me informar sobre qual controle / widget você reigster o ouvinte de toque na classe de atividade.
— precisa saber é o seguinte

Desculpe pelo atraso @DeepakSharma. Não registrei nenhum ouvinte de toque na atividade, apenas substituí o onTouchEvent da atividade.
— Mahdi Hijazi

@MahdiHijazi pode u por favor adicionar seu código de rolagem horizontal e vertical de rolagem em github.com/MikeOrtiz/TouchImageView este está zumbindo com sucesso imagem no clique de botão, mas falhou imagem de rolagem para
— Erum

43

Como esse parece ser o primeiro resultado de pesquisa no Google para "Android Vertical + ScrollView horizontal", pensei em adicionar isso aqui. Matt Clark construiu uma exibição personalizada com base na fonte do Android e parece funcionar perfeitamente: Two Dimensional ScrollView

Lembre-se de que a classe nessa página possui um erro que calcula a largura horizontal da visualização. Uma correção de Manuel Hilty está nos comentários:

Solução: Substitua a instrução na linha 808 da seguinte maneira:

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


Edit: O link não funciona mais, mas aqui está um link para uma versão antiga do post do blog .


3
Obrigado, era o que eu estava procurando.
— Andrey Agibalov

1
Isso funciona como um encanto após algumas pequenas modificações para mim. Reimplementei fillViewporte onMeasureexatamente como a fonte ScrollView (v2.1). Também mudei os dois primeiros construtores com: this( context, null );e this(context, attrs, R.attr.scrollViewStyle);. Com isso, eu posso usar a barra de rolagem usando setVerticalScrollBarEnablede setHorizontalScrollBarEnabledno código.
— Olivier_sdg

Funciona muito bem para mim! Muito obrigado!
— Avio

mas como para obter o tamanho do polegar de rolagem e posição de rolagem da parte inferior vertical e da direita no modo horizontal
— Ravi

10
Parece que a postagem vinculada desapareceu.
— 24514 loeschg

28

Encontrei uma solução melhor.

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>

Código 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; 
  }
}

Isso funciona !!!

Se você deseja carregar outro layout ou controle, a estrutura é a mesma.


Apenas tentei isso, e funciona muito bem! Ele não possui indicadores de rolagem ou arremesso, mas como essa é uma abordagem de nível inferior, essas coisas podem ser construídas facilmente com facilidade. Obrigado!
— precisa

Também funcionou bem para mim ao escrever uma exibição rolável bidimensional que produzia saída em tempo real a partir do resultado de comandos remotos SSH na rede.
— Pilcrowpipe

@Kronos: E se eu quiser apenas rolar horizontalmente? Qual deve ser o meu parâmetro y em container.scrollBy (currentX - x2, currentY - y2). Existe alguma maneira de fazê-lo rolar horizontalmente?
— 21412 Ashwin

@Kronos: rola demais. Existe alguma maneira de parar o pergaminho quando o fim for alcançado?
— Ashwin

Os botões não podem ser rolados, por quê?
— salih Kallai

25

Eu uso e funciona bem:

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

O link da fonte está aqui: Android-spa


2
Funciona "muito bem" para conteúdo estático, na melhor das hipóteses. Vários engenheiros do Google disseram que o que você está tentando usar terá problemas ( groups.google.com/group/android-developers/browse_frm/thread/… e groups.google.com/group/android-beginners/browse_thread/thread/ ... )
— CommonsWare

4
@ CommmonsWare: com respeito pelos brilhantes engenheiros do Google, nesses tópicos eles pediram para você reescrever do zero o seu próprio componente: esse é o melhor caminho. Certamente é verdade. Mas eles não dizem que esta solução é ruim e, se funcionar ... Talvez para eles demore um minuto, para mim é melhor escrever um xml usando os componentes existentes. O que significa "conteúdo estático"? Eu uso botões e imagens.
— Redax

3
Por "conteúdo estático", quero dizer coisas que não envolvem eventos de toque. Meu ponto de vista - para outras pessoas que leem esta resposta - é que, embora sua solução funcione para um único ImageViewconteúdo rolável, ela pode ou não funcionar para outros conteúdos arbitrários e que o Google acredita que haverá problemas com sua abordagem. A opinião do Google também é importante em relação ao desenvolvimento futuro - eles podem não estar tentando garantir que sua abordagem funcione a longo prazo, se estiverem aconselhando as pessoas a não usá-la em primeiro lugar.
— CommonsWare

Isso está funcionando bem para mim ... Estou atualizando uma visualização de imagem neste scrollview usando um thread
— sonu thomas

19

Minha solução baseada na resposta de Mahdi Hijazi , mas sem visualizações personalizadas:

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>

Código (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;
        }
    });

Você pode alterar a ordem das visualizações de rolagem. Basta alterar a ordem no layout e no código. E, obviamente, em vez de WateverViewYouWant, você coloca o layout / vistas que deseja rolar nas duas direções.


Eu sugiro fortemente retornar false em hScroll.setOnTouchListener. Quando mudei para false, a lista começou a rolar "suavemente automático" no dedo para cima em ambas as direções.
— Greta Radisauskaite

1
Funciona ao rolar horizontalmente primeiro. Quando vertical é em primeiro lugar, vai apenas na vertical
— Irhala

6

Opção 1: você pode criar um novo design de interface do usuário que não exija rolagem horizontal e vertical simultânea.

Opção 2: você pode obter o código-fonte ScrollViewe HorizontalScrollViewaprender como a equipe principal do Android os implementou e criar sua própria BiDirectionalScrollViewimplementação.

Opção nº 3: você pode se livrar das dependências que exigem o uso do sistema de widgets e desenhar diretamente no Canvas.

Opção nº 4: se você se deparar com um aplicativo de código aberto que parece implementar o que você procura, veja como eles o fizeram.


6

Tente isto

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

como o link Two Dimensional Scrollview está inoperante, não consegui obtê-lo, então criei meu próprio componente. Ele lida com arremessar e funciona corretamente para mim. A única restrição é que wrap_content pode não funcionar corretamente para esse componente.

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


2

Eu tenho uma solução para o seu problema. Você pode verificar o código ScrollView, que trata apenas da rolagem vertical, ignora a horizontal e modifica-a. Eu queria uma visualização como uma webview, então modifiquei o ScrollView e funcionou bem para mim. Mas isso pode não atender às suas necessidades.

Deixe-me saber que tipo de interface do usuário você está segmentando.

Saudações,

Ravi Pandit


1

Jogando com o código, você pode colocar um HorizontalScrollView em um ScrollView. Dessa forma, você pode ter o método de duas rolagens na mesma exibição.

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

Espero que isso possa ajudá-lo.


1
Esta não é uma boa solução. Se você notar, ao arrastar a vista, você está sempre arrastando na vertical ou na horizontal. Você não pode arrastar ao longo de uma diagonal usando visualizações de rolagem aninhadas.
— Sky Kelsey

O que é pior do que a falta de rolagem diagonal é a falta de ambas as barras de rolagem restantes na borda da exibição porque uma está aninhada. Um ScrollView melhor é a resposta e o link da Cachapa com o código de Matt Clark é a melhor solução no momento devido à integridade e generalização.
— Huperniketes

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.