Scrollview vertikal dan horizontal di android

151

Saya sangat lelah mencari solusi untuk Scrollview vertikal dan horizontal.

Saya membaca bahwa tidak ada tampilan / tata letak dalam kerangka kerja yang menerapkan fitur ini, tetapi saya perlu sesuatu seperti ini:

Saya perlu mendefinisikan tata letak di dalam yang lain, tata letak anak harus menerapkan pengguliran vertikal / horizontal untuk bergerak.

Awalnya menerapkan kode yang memindahkan tata letak pixel demi pixel, tapi saya pikir itu bukan cara yang benar. Saya mencobanya dengan ScrollView dan Horizontal ScrollView tetapi tidak ada yang berfungsi seperti yang saya inginkan, karena hanya menerapkan pengguliran vertikal atau horizontal.

Kanvas bukanlah solusi saya karena saya harus melampirkan pendengar dalam elemen anak seseorang.

Apa yang dapat saya?

Kronos
sumber
14
Ini benar-benar konyol bahwa ini tidak tersedia di luar kotak dengan Android. Kami telah bekerja dengan setengah lusin teknologi UI lainnya dan tidak pernah terdengar tidak memiliki hal mendasar seperti wadah pengguliran horizontal / vertikal.
flexicious.com
Ok, akhirnya kami menulis sendiri datagrid kami: androidjetpack.com/Home/AndroidDataGrid . Apakah lebih dari sekadar gulir horizontal / vertikal. Tapi idenya pada dasarnya adalah membungkus HScroller di dalam Vertical Scroller. Anda juga harus mengganti menambahkan / menghapus metode anak untuk menargetkan penggulung dalam dan beberapa gangguan lainnya, tetapi berhasil.
flexicious.com

Jawaban:

90

Menggabungkan beberapa saran di atas, dan bisa mendapatkan solusi yang baik:

ScrollView Kustom:

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

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

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

}

tata letak:

<?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
sumber
Ini adalah jawaban yang bagus, saya memiliki elemen gulir lain dalam tampilan yang juga perlu diubah. Yang harus saya lakukan adalah memicu scrollby mereka bersama yang lain.
T. Markle
1
Bagus! Beberapa penyesuaian untuk memperhitungkan kecepatan, dan ini akan sempurna (juga, Anda tidak perlu inisial LinearLayoutsaya kira)
Romuald Brunet
Hai Mahdi, bisakah Anda memberi tahu saya, tentang kontrol / widget mana yang telah Anda daftarkan ulang pendengar sentuh di kelas aktivitas.
Shachillies
Maaf atas keterlambatan @DeepakSharma. Saya tidak mendaftarkan pendengar sentuh apa pun dalam aktivitas, saya baru saja menimpa onTouchEvent dari aktivitas tersebut.
Mahdi Hijazi
@MahdiHijazi dapatkah Anda menambahkan kode Horizontal Scrolling dan Vertical Scrolling di github.com/MikeOrtiz/TouchImageView ini berhasil memperbesar gambar pada klik tombol tetapi gagal menggulir gambar
Erum
43

Karena ini tampaknya menjadi hasil pencarian pertama di Google untuk "Android vertical + horizontal ScrollView", saya pikir saya harus menambahkan ini di sini. Matt Clark telah membangun tampilan khusus berdasarkan sumber Android, dan tampaknya berfungsi dengan baik: ScrollView Dua Dimensi

Berhati-hatilah karena kelas di halaman tersebut memiliki bug yang menghitung lebar horizonal tampilan. Perbaikan oleh Manuel Hilty ada di komentar:

Solusi: Ganti pernyataan pada saluran 808 dengan yang berikut ini:

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


Sunting: Tautan tidak berfungsi lagi tetapi ini adalah tautan ke versi blogpost yang lama .

Cachapa
sumber
3
Terima kasih, itulah yang saya cari.
Andrey Agibalov
1
Ini berfungsi seperti pesona setelah beberapa modifikasi kecil untuk saya. Saya menerapkan kembali fillViewportdan onMeasurepersis seperti sumber ScrollView (v2.1). Saya juga mengubah dua konstruktor pertama dengan: this( context, null );dan this(context, attrs, R.attr.scrollViewStyle);. Dengan itu, saya bisa menggunakan scrollbar menggunakan setVerticalScrollBarEnableddan setHorizontalScrollBarEnableddalam kode.
olivier_sdg
Bekerja dengan baik juga untuk saya! Terima kasih banyak!
Avio
tetapi cara mendapatkan ukuran ibu jari gulir dan posisi gulir dari bawah vertikal dan dari kanan dalam mode horizontal
Ravi
10
Sepertinya pos yang ditautkan telah menghilang.
loeschg
28

Saya menemukan solusi yang lebih baik.

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>

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

Itu berhasil !!!

Jika Anda ingin memuat tata letak atau kontrol lain, strukturnya sama.

Kronos
sumber
Baru saja mencoba ini, dan itu bekerja dengan sangat baik! Itu tidak memiliki indikator gulir, atau melemparkan, tetapi karena ini adalah pendekatan tingkat yang lebih rendah hal-hal itu dapat dibangun di atas ini dengan cukup mudah. Terima kasih!
ubzack
Bekerja dengan baik untuk saya juga untuk menulis tampilan dua dimensi yang dapat digulirkan yang menghasilkan keluaran waktu nyata dari hasil perintah jarak jauh SSH melalui jaringan.
pilcrowpipe
@ Kronos: Bagaimana jika saya hanya ingin menggulir secara horizontal? Apa yang harus saya parameter y berada di container.scrollBy (currentX - x2, currentY - y2). Apakah ada cara di mana kita dapat membuatnya hanya menggulir secara horizontal?
Ashwin
@ Kronos: Menggulir terlalu jauh. Apakah ada cara untuk menghentikan gulir ketika akhirnya tercapai?
Ashwin
Tombol tidak dapat digulir, Mengapa?
salih kallai
25

Saya menggunakannya dan berfungsi dengan baik:

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

Tautan sumber ada di sini: Android-spa

Redax
sumber
2
Ini berfungsi "baik-baik saja" untuk konten statis, paling banter. Beberapa insinyur Google mengatakan bahwa apa yang Anda coba gunakan akan mengalami masalah ( groups.google.com/group/android-developers/browse_frm/thread/… dan groups.google.com/group/android-beginners/browse_thread/thread/ ... )
CommonsWare
4
@CommonsWare: sangat menghormati beberapa insinyur Google yang brilian, di utas yang mereka katakan untuk menulis ulang dari awal komponen Anda sendiri: ini adalah cara terbaik. Tentunya itu benar. Tetapi mereka tidak mengatakan bahwa solusi ini buruk, dan jika berhasil ... Mungkin bagi mereka butuh satu menit, bagi saya lebih baik menulis beberapa xml menggunakan komponen yang ada. Apa artinya "konten statis"? Saya menggunakan tombol dan gambar.
Redax
3
Yang saya maksud dengan "konten statis" adalah hal-hal yang tidak melibatkan peristiwa sentuhan sendiri. Maksud saya - bagi orang lain yang membaca jawaban ini - adalah bahwa sementara solusi Anda dapat berfungsi untuk satu ImageViewsebagai konten yang dapat digulir, itu mungkin atau mungkin tidak berfungsi untuk konten lain yang sewenang-wenang, dan bahwa Google percaya bahwa akan ada masalah dengan pendekatan Anda. Pendapat Google juga penting sehubungan dengan pengembangan di masa depan - mereka mungkin tidak mencoba untuk memastikan bahwa pendekatan Anda berhasil dalam jangka panjang, jika mereka menyarankan orang untuk tidak menggunakannya di tempat pertama.
CommonsWare
Ini berfungsi dengan baik untuk saya ... Saya sedang menyegarkan tampilan gambar di scrollview ini menggunakan utas
sonu thomas
19

Solusi saya berdasarkan jawaban Mahdi Hijazi , tetapi tanpa tampilan khusus:

Tata letak:

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

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

Anda dapat mengubah urutan tampilan gulir. Ubah saja urutannya dalam tata letak dan kode. Dan jelas bukan WHATViewYouWant Anda ingin meletakkan tata letak / tampilan yang ingin Anda gulir kedua arah.

Yan.Yurkin
sumber
Saya sangat menyarankan untuk mengembalikan false di hScroll.setOnTouchListener. Ketika saya berubah menjadi false, daftar mulai menggulir "otomatis lancar" di jari ke atas di kedua arah.
Greta Radisauskaite
1
Bekerja saat menggulir secara horizontal terlebih dahulu. Saat vertikal pertama kali, berjalan hanya secara vertikal
Irhala
6

Opsi # 1: Anda dapat membuat desain UI baru yang tidak memerlukan pengguliran horizontal dan vertikal secara simultan.

Opsi # 2: Anda dapat memperoleh kode sumber untuk ScrollViewdan HorizontalScrollView, mempelajari bagaimana tim inti Android menerapkannya, dan membuat BiDirectionalScrollViewimplementasi Anda sendiri .

Opsi # 3: Anda dapat menyingkirkan dependensi yang mengharuskan Anda untuk menggunakan sistem widget dan menarik langsung ke kanvas.

Opsi # 4: Jika Anda menemukan aplikasi open source yang tampaknya mengimplementasikan apa yang Anda cari, lihat bagaimana mereka melakukannya.

CommonsWare
sumber
6

Coba ini

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

karena tautan Dua Dimensi Scrollview sudah mati saya tidak bisa mendapatkannya sehingga saya membuat komponen saya sendiri. Ini menangani melemparkan dan berfungsi dengan baik untuk saya. Satu-satunya batasan adalah wrap_content mungkin tidak berfungsi dengan baik untuk komponen itu.

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

androidseb
sumber
2

Saya punya solusi untuk masalah Anda. Anda dapat memeriksa kode ScrollView yang hanya menangani pengguliran vertikal dan mengabaikan kode horizontal dan mengubahnya. Saya ingin tampilan seperti tampilan web, sehingga diubah ScrollView dan itu bekerja dengan baik untuk saya. Tetapi ini mungkin tidak sesuai dengan kebutuhan Anda.

Beri tahu saya jenis UI apa yang Anda targetkan.

Salam,

Ravi Pandit

Ravi Pandit
sumber
1

Bermain dengan kode tersebut, Anda dapat menempatkan HorizontalScrollView ke dalam ScrollView. Dengan demikian, Anda dapat memiliki dua metode gulir dalam tampilan yang sama.

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

Saya harap ini bisa membantu Anda.

Eriatolc
sumber
1
Ini bukan solusi yang baik. Jika Anda perhatikan, saat menyeret tampilan, Anda selalu menyeret baik secara vertikal maupun horizontal. Anda tidak dapat menyeret diagonal menggunakan tampilan gulir bersarang.
Sky Kelsey
Yang lebih buruk daripada tidak adanya scroll diagonal adalah tidak adanya kedua scrollbar yang tersisa di sisi tampilan karena satu bersarang. ScrollView yang lebih baik adalah jawabannya dan tautan Cachapa ke kode Matt Clark adalah solusi terbaik saat ini karena kelengkapan dan generalisasi.
Huperniketes