Scrollview dọc và ngang trong Android


151

Tôi thực sự mệt mỏi khi tìm kiếm một giải pháp cho Scrollview dọc và ngang.

Tôi đọc rằng không có bất kỳ khung nhìn / bố cục nào trong khung triển khai tính năng này, nhưng tôi cần một cái gì đó như thế này:

Tôi cần xác định bố cục bên trong khác, bố trí con phải thực hiện cuộn dọc / ngang để di chuyển.

Ban đầu triển khai một mã di chuyển pixel bố cục theo pixel, nhưng tôi nghĩ đó không phải là cách đúng đắn. Tôi đã thử nó với ScrollView và ngang ScrollView nhưng không có gì hoạt động như tôi muốn, bởi vì nó chỉ thực hiện cuộn dọc hoặc cuộn ngang.

Canvas không phải là giải pháp của tôi vì tôi cần gắn người nghe vào một số yếu tố con.

Tôi có thể làm gì?



14
Điều hoàn toàn vô lý là điều này không có sẵn với Android. Chúng tôi đã làm việc với một nửa tá công nghệ UI khác và thật không thể tin được là không có thứ cơ bản như thùng chứa cuộn ngang / dọc.
flexicy.com

Ok, cuối cùng chúng tôi đã viết riêng cho datagrid của mình: androidjetpack.com/Home/AndroidDataGrid . Có nhiều hơn chỉ là cuộn ngang / dọc. Nhưng ý tưởng về cơ bản là bọc một HScroller bên trong một Máy chà sàn dọc. Bạn cũng phải ghi đè thêm / loại bỏ các phương thức con để nhắm mục tiêu bộ lọc bên trong và một vài shenanigans khác, nhưng nó hoạt động.
flexicy.com

Câu trả lời:


90

Trộn một số gợi ý ở trên và có thể nhận được một giải pháp tốt:

ScrollView tùy chỉnh:

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

Ngang tùy chỉnh Xem:

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

}

cách bố trí:

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

Đây là một câu trả lời tuyệt vời, tôi đã có các yếu tố cuộn khác trong chế độ xem cũng cần phải thay đổi. Tất cả những gì tôi phải làm là kích hoạt cuộn giấy của họ cùng với những người khác.
T. Markle

1
Tuyệt quá! Một vài điều chỉnh để tính đến vận tốc và điều này sẽ rất hoàn hảo (đồng thời, bạn không cần điều LinearLayouttôi đoán ban đầu )
Romuald Brunet

Này Mahdi, bạn có thể cho tôi biết, trên điều khiển / widget nào bạn đã giới thiệu lại trình nghe cảm ứng trong lớp hoạt động.
Shachillies

Xin lỗi vì sự chậm trễ @DeepakSharma. Tôi đã không đăng ký bất kỳ trình nghe cảm ứng nào trong hoạt động, tôi chỉ ghi đè onTouchEvent của hoạt động.
Mahdi Hijazi

@MahdiHijazi bạn có thể vui lòng thêm mã cuộn ngang và cuộn dọc của bạn trong github.com/MikeOrtiz/TouchImageView đây là thu phóng hình ảnh thành công khi nhấp vào nút nhưng không thể cuộn hình ảnh
Erum

43

Vì đây dường như là kết quả tìm kiếm đầu tiên trong Google cho "Android vertical + vertical ScrollView", tôi nghĩ tôi nên thêm nó vào đây. Matt Clark đã xây dựng chế độ xem tùy chỉnh dựa trên nguồn Android và dường như nó hoạt động hoàn hảo: ScrollView hai chiều

Coi chừng rằng lớp trong trang đó có lỗi tính toán chiều rộng đường chân trời của khung nhìn. Một sửa chữa của Manuel Hilty là trong các ý kiến:

Giải pháp: Thay thế câu lệnh trên dòng 808 bằng cách sau:

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


Chỉnh sửa: Liên kết không hoạt động nữa nhưng đây là liên kết đến một phiên bản cũ của blogpost .


3
Cảm ơn, đó là những gì tôi đang tìm kiếm.
Andrey Agibalov

1
Điều này hoạt động như một nét duyên dáng sau một vài sửa đổi nhỏ đối với tôi. Tôi đã thực hiện lại fillViewportonMeasurechính xác như nguồn ScrollView (v2.1). Tôi cũng đã thay đổi hai hàm tạo đầu tiên bằng: this( context, null );this(context, attrs, R.attr.scrollViewStyle);. Với điều đó, tôi có thể sử dụng thanh cuộn bằng cách sử dụng setVerticalScrollBarEnabledsetHorizontalScrollBarEnabledtrong mã.
olivier_sdg

Công trình tuyệt vời cũng cho tôi! Cảm ơn rât nhiều!
Avio

nhưng làm thế nào để có được kích thước ngón tay cái cuộn và vị trí cuộn từ dưới dọc và từ phải sang chế độ nằm ngang
Ravi

10
Có vẻ như bài đăng được liên kết đã biến mất.
loeschg

28

Tôi tìm thấy một giải pháp tốt hơn.

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>

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

Đó là công việc !!!

Nếu bạn muốn tải bố cục hoặc điều khiển khác, cấu trúc là như nhau.


Chỉ cần thử điều này, và nó hoạt động rất tốt! Nó không có các chỉ số cuộn, hoặc fling, nhưng bởi vì đây là một cách tiếp cận cấp thấp hơn nên những thứ đó có thể được xây dựng trên đầu trang này khá dễ dàng. Cảm ơn!
ubzack

Tôi cũng làm việc rất tốt khi viết một chế độ xem có thể cuộn hai chiều, xuất ra đầu ra thời gian thực từ kết quả của các lệnh từ xa SSH qua mạng.
pilcrowpipe

@Kronos: Nếu tôi chỉ muốn cuộn theo chiều ngang thì sao? Tham số y của tôi phải là gì trong container.scrollBy (currentX - x2, currentY - y2). Có cách nào để chúng ta có thể làm cho nó cuộn theo chiều ngang không?
Ashwin

@Kronos: Nó cuộn quá xa. Có cách nào để dừng cuộn khi kết thúc không?
Ashwin

Các nút không thể cuộn, Tại sao?
salih kallai

25

Tôi sử dụng nó và hoạt động tốt:

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

Liên kết nguồn ở đây: Android-spa


2
Nó hoạt động "tốt" cho nội dung tĩnh, tốt nhất. Nhiều kỹ sư của Google đã nói rằng những gì bạn đang cố sử dụng sẽ có vấn đề ( Groups.google.com/group/android-developers/browse_frm/thread/ Kẻnhóm.google.com/group/android-beginners/browse_thread/thread/ Cẩu ).
CommonsWare

4
@CommonsWare: sự tôn trọng dành cho nhiều kỹ sư xuất sắc của Google, trong các chủ đề họ đã bảo bạn viết lại từ đầu thành phần của chính bạn: đây là cách tốt nhất. Chắc chắn là đúng. Nhưng họ không nói rằng giải pháp này là xấu, và nếu nó hoạt động ... Có thể đối với họ phải mất một phút, đối với tôi, tốt hơn là viết một số xml bằng cách sử dụng các thành phần hiện có. "Nội dung tĩnh" nghĩa là gì? Tôi sử dụng nút và hình ảnh.
Redax

3
Theo "nội dung tĩnh", ý tôi là những thứ không liên quan đến các sự kiện chạm. Quan điểm của tôi - đối với những người khác đang đọc câu trả lời này - là trong khi giải pháp của bạn có thể hoạt động với một ImageViewnội dung có thể cuộn được, thì nó có thể hoặc không hoạt động đối với các nội dung khác tùy ý và Google tin rằng sẽ có vấn đề với cách tiếp cận của bạn. Ý kiến ​​của Google cũng liên quan đến sự phát triển trong tương lai - họ có thể không cố gắng đảm bảo rằng phương pháp của bạn hoạt động trong một thời gian dài, nếu họ khuyên mọi người không nên sử dụng nó ngay từ đầu.
CommonsWare

Điều này đang hoạt động tốt đối với tôi ... Tôi đang làm mới một lượt xem hình ảnh trong cuộn xem này bằng một chủ đề
sonu thomas

19

Giải pháp của tôi dựa trên câu trả lời của Mahdi Hijazi , nhưng không có bất kỳ chế độ xem tùy chỉnh nào:

Bố trí:

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

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

Bạn có thể thay đổi thứ tự của các cuộn xem. Chỉ cần thay đổi thứ tự của chúng trong bố trí và trong mã. Và rõ ràng thay vì WthingViewYouWant bạn đặt bố cục / chế độ xem bạn muốn cuộn cả hai hướng.


Tôi thực sự khuyên bạn nên trả về false trong hScroll.setOnTouchListener. Khi tôi đổi thành false, danh sách bắt đầu cuộn "tự động trơn tru" trên ngón tay lên theo cả hai hướng.
Greta Radisauskaite

1
Hoạt động khi cuộn theo chiều ngang đầu tiên. Khi dọc là đầu tiên, chỉ đi theo chiều dọc
Irhala

6

Tùy chọn # 1: Bạn có thể đưa ra một thiết kế UI mới không yêu cầu cuộn ngang và cuộn dọc.

Tùy chọn # 2: Bạn có thể lấy mã nguồn ScrollViewHorizontalScrollView, tìm hiểu cách nhóm Android cốt lõi triển khai các mã đó và tạo BiDirectionalScrollViewtriển khai của riêng bạn .

Tùy chọn # 3: Bạn có thể thoát khỏi các phụ thuộc yêu cầu bạn sử dụng hệ thống tiện ích và vẽ thẳng vào Canvas.

Tùy chọn # 4: Nếu bạn vấp phải một ứng dụng nguồn mở dường như thực hiện những gì bạn tìm kiếm, hãy nhìn xem họ đã làm như thế nào.


6

Thử cái này

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

do liên kết Two Dimensional Scrollview đã chết nên tôi không thể lấy được nên tôi đã tạo thành phần của riêng mình. Nó xử lý flelling và hoạt động đúng cho tôi. Hạn chế duy nhất là quấn_content có thể không hoạt động đúng cho thành phần đó.

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


2

Tôi có một giải pháp cho vấn đề của bạn. Bạn có thể kiểm tra mã ScrollView mà nó chỉ xử lý cuộn dọc và bỏ qua mã ngang và sửa đổi điều này. Tôi muốn có một chế độ xem như một webview, vì vậy ScrollView đã được sửa đổi và nó hoạt động tốt với tôi. Nhưng điều này có thể không phù hợp với nhu cầu của bạn.

Hãy cho tôi biết bạn đang nhắm mục tiêu loại UI nào.

Trân trọng,

Ravi Pandit


1

Chơi với mã, bạn có thể đặt ngang ngang vào ScrollView. Qua đó, bạn có thể có hai phương thức cuộn trong cùng một chế độ xem.

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

Tôi hy vọng điều này có thể giúp bạn.


1
Đây không phải là một giải pháp tốt. Nếu bạn nhận thấy, khi kéo chế độ xem, bạn luôn kéo theo chiều dọc hoặc chiều ngang. Bạn không thể kéo dọc theo đường chéo bằng cách sử dụng chế độ xem cuộn lồng nhau.
Sky Kelsey

Điều tồi tệ hơn việc thiếu cuộn chéo là thiếu cả hai thanh cuộn còn lại ở cạnh xem vì một thanh được lồng vào nhau. Một ScrollView tốt hơn là câu trả lời và liên kết của Cachapa với mã của Matt Clark là giải pháp tốt nhất tại thời điểm này do tính đầy đủ và khái quát.
Huperniketes

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.