안드로이드에서 스크롤보기 수직 및 수평


152

세로 및 가로 스크롤보기 솔루션을 찾는 데 정말 피곤합니다.

이 기능을 구현하는 프레임 워크에는 뷰 / 레이아웃이 없지만 다음과 같은 것이 필요하다는 것을 읽었습니다.

다른 레이아웃 내에서 레이아웃을 정의해야합니다. 자식 레이아웃은 이동을 위해 수직 / 수평 스크롤을 구현해야합니다.

처음에는 레이아웃을 픽셀 단위로 이동시키는 코드를 구현했지만 올바른 방법은 아니라고 생각합니다. ScrollView 및 Horizontal ScrollView로 시도했지만 세로 또는 가로 스크롤 만 구현하기 때문에 원하는대로 작동하지 않습니다.

Canvas는 누군가의 자식 요소에 리스너를 연결해야하기 때문에 내 솔루션이 아닙니다.

어떡해?



14
Android에서 즉시 사용할 수 없다는 것은 정말 우스운 일입니다. 우리는 십여 개의 다른 UI 기술로 작업 해 왔으며 가로 / 세로 스크롤 컨테이너와 같은 기본적인 것을 가지고 있지 않은 것은 아닙니다.
flexicious.com 1

자, 마침내 우리는 데이터 그리드를 위해 우리 자신을 작성했습니다 : androidjetpack.com/Home/AndroidDataGrid . 가로 / 세로 스크롤 이상의 기능을 수행합니다. 그러나 아이디어는 기본적으로 HScroller를 세로 스크롤러 안에 배치하는 것입니다. 내부 스크롤러와 다른 몇 가지 shenanigan을 대상으로하려면 add / remove 자식 메서드를 재정의해야하지만 작동합니다.
flexicious.com

답변:


91

위의 제안 중 일부를 혼합하여 좋은 해결책을 얻을 수있었습니다.

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

사용자 정의 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;
    }

}

배치:

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

이것은 훌륭한 답변입니다.보기에 변경해야 할 다른 스크롤 요소가 있습니다. 내가해야 할 일은 다른 사람들과 함께 스크롤을 트리거하는 것입니다.
T. Markle

1
큰! 속도를 고려한 몇 가지 조정이 완벽 할 것입니다 (초기에는 필요하지 않습니다 LinearLayout)
Romuald Brunet

이봐 Mahdi 당신은 활동 클래스의 터치 리스너를 reigster 한 컨트롤 / 위젯을 알려주세요.
Shachillies

@DeepakSharma 지연에 대해 죄송합니다. 활동에 터치 리스너를 등록하지 않았으며 활동의 onTouchEvent를 재정의했습니다.
Mahdi Hijazi

@MahdiHijazi github.com/MikeOrtiz/TouchImageView에 가로 스크롤 및 세로 스크롤 코드를 추가 할 수 있습니까? 버튼 클릭시 이미지를 확대 / 축소하지만 이미지를 스크롤하지 못했습니다
Erum

43

이것이 "Android vertical + horizontal ScrollView"에 대한 Google의 첫 번째 검색 결과 인 것 같으므로 여기에 추가해야한다고 생각했습니다. Matt Clark은 Android 소스를 기반으로 사용자 정의보기를 작성했으며 완벽하게 작동하는 것 같습니다 : Two Dimensional ScrollView

해당 페이지의 클래스에는 뷰의 가로 너비를 계산하는 버그가 있습니다. Manuel Hilty의 수정 사항은 다음과 같습니다.

솔루션 : 808 행의 명령문을 다음으로 바꾸십시오.

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


편집 : 링크가 더 이상 작동하지 않지만 여기에 이전 버전의 블로그 게시물에 대한 링크가 있습니다.


3
고마워, 내가 찾던거야.
Andrey Agibalov

1
이것은 나를 위해 약간의 수정을 한 후에 매력처럼 작동합니다. 나는 ScrollView 소스 (v2.1) fillViewportonMeasure똑같이 다시 구현했습니다 . 나는 또한 함께 두 첫번째 생성자를 변경 : this( context, null );this(context, attrs, R.attr.scrollViewStyle);. 이를 통해 코드에서 setVerticalScrollBarEnabledsetHorizontalScrollBarEnabled코드를 사용하여 스크롤 막대를 사용할 수 있습니다 .
olivier_sdg 7

나에게도 잘 작동합니다! 대단히 감사합니다!
Avio

그러나 수평 모드에서 수직 아래에서 오른쪽으로 스크롤 썸 크기와 스크롤 위치를 얻는 방법
Ravi

10
연결된 게시물이 사라진 것 같습니다.
loeschg

28

더 나은 해결책을 찾았습니다.

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>

자바 코드 :

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

작동합니다 !!!

다른 레이아웃이나 컨트롤을로드하려면 구조가 동일합니다.


방금 시도해 보았고 잘 작동합니다! 스크롤 인디케이터 나 플링은 없지만 이것은 하위 수준의 접근 방식이기 때문에 이러한 것들 위에 상당히 쉽게 구축 할 수 있습니다. 감사!
ubzack

네트워크를 통해 SSH 원격 명령의 결과에서 실시간 출력을 출력하는 2 차원 스크롤 가능보기를 작성하는 것도 나에게 효과적이었습니다.
pilcrowpipe

@Kronos : 가로로만 스크롤하려면 어떻게해야합니까? 내 y 매개 변수는 container.scrollBy (currentX-x2, currentY-y2)에 있어야합니다. 가로로만 스크롤 할 수있는 방법이 있습니까?
Ashwin

@Kronos : 너무 멀리 스크롤됩니다. 끝에 도달했을 때 스크롤을 멈추는 방법이 있습니까?
Ashwin

버튼을 스크롤 할 수없는 이유는 무엇입니까?
salih kallai

25

나는 그것을 사용하고 잘 작동합니다.

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

소스 링크는 다음과 같습니다. Android-spa


2
정적 콘텐츠에는 "정상"작동합니다. 여러 Google 엔지니어가 사용하려는 문제에 문제가 있다고 말했습니다 ( groups.google.com/group/android-developers/browse_frm/thread/…groups.google.com/group/android-beginners/browse_thread/thread/ … ).
CommonsWare

4
@CommonsWare : 여러 Google 엔지니어에게 존경의 인사를 전합니다.이 스레드에서 사용자 자신의 구성 요소를 처음부터 다시 작성하라는 메시지가 표시되었습니다. 이것이 가장 좋은 방법입니다. 확실합니다. 그러나 그들은이 솔루션이 나쁘다고 말하지 않으며 작동한다면 ... 아마 1 분이 걸릴 수 있습니다. 기존 구성 요소를 사용하여 XML을 작성하는 것이 좋습니다. "정적 내용"이란 무엇입니까? 버튼과 이미지를 사용합니다.
Redax

3
"정적 콘텐츠"란 터치 이벤트 자체와 관련이없는 것을 의미합니다. 내 요점은-이 답변을 읽는 다른 사람들에게-귀하의 솔루션은 ImageView스크롤 가능한 컨텐츠로 단일 에서 작동하지만 임의의 다른 컨텐츠에서는 작동하지 않을 수도 있으며 Google은 귀하의 접근 방식에 문제가 있다고 생각합니다. Google의 의견은 향후 개발과 관련해서도 중요합니다. 사람들이 처음부터 사용하지 말라고 조언하는 경우 장기적인 접근 방식으로 귀하의 접근 방식이 작동하는지 확인하지 않을 수 있습니다.
CommonsWare

이것은 나에게 잘 작동합니다 ... 스레드를 사용 하여이 스크롤보기에서 이미지 뷰를 새로 고칩니다.
sonu thomas

19

Mahdi Hijazi answer 기반의 솔루션 이지만 사용자 정의보기는 없습니다.

나열한 것:

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

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

스크롤보기 순서를 변경할 수 있습니다. 레이아웃과 코드에서 순서를 변경하십시오. 분명히 WateverViewYouWant 대신 두 방향으로 스크롤하려는 레이아웃 /보기를 넣습니다.


hScroll.setOnTouchListener에서 false를 반환하는 것이 좋습니다. false로 변경하면 목록이 손가락에서 "부드럽게 자동"으로 두 방향으로 스크롤하기 시작했습니다.
Greta Radisauskaite

1
먼저 가로로 스크롤 할 때 작동합니다. 수직이 처음 일 때는 수직으로 만 진행
Irhala

6

옵션 # 1 : 수평 및 수직 스크롤을 동시에 필요로하지 않는 새로운 UI 디자인을 제안 할 수 있습니다.

옵션 # 2 : ScrollView및에 소스 코드를 가져 와서 HorizontalScrollView핵심 Android 팀이이를 구현 한 방법을 배우고 고유 한 BiDirectionalScrollView구현을 만들 수 있습니다 .

옵션 # 3 : 위젯 시스템을 사용하고 Canvas로 곧바로 그려야하는 종속성을 제거 할 수 있습니다.

옵션 # 4 : 원하는 것을 구현하는 오픈 소스 응용 프로그램을 우연히 발견 한 경우 어떻게했는지 살펴보십시오.


6

이 시도

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

Two Dimensional Scrollview 링크가 죽었 기 때문에 얻을 수 없으므로 내 구성 요소를 만들었습니다. 플링을 처리하고 제대로 작동합니다. 유일한 제한 사항은 wrap_content가 해당 구성 요소에 대해 제대로 작동하지 않을 수 있다는 것입니다.

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


2

귀하의 문제에 대한 해결책이 있습니다. 세로 스크롤 만 처리하고 가로 스크롤은 무시하고 ScrollView 코드를 확인할 수 있습니다. 나는 웹뷰와 같은보기를 원했기 때문에 ScrollView를 수정하여 잘 작동했습니다. 그러나 이것은 귀하의 필요에 맞지 않을 수 있습니다.

어떤 종류의 UI를 타겟팅하고 있는지 알려주세요.

문안 인사,

라비 판 디트


1

코드를 사용하여 HorizontalScrollView를 ScrollView에 넣을 수 있습니다. 이에 의해, 동일한 뷰에서 2 개의 스크롤 방법을 가질 수있다.

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

이것이 도움이되기를 바랍니다.


1
이것은 좋은 해결책이 아닙니다. 보기를 끌 때 항상 세로 또는 가로로 끌고 있습니다. 중첩 된 스크롤보기를 사용하여 대각선을 따라 끌 수 없습니다.
Sky Kelsey

대각선 스크롤링이없는 것보다 더 나쁜 것은 하나의 스크롤바가 중첩되어 있기 때문에보기 가장자리에 남아있는 두 스크롤바가 없다는 것입니다. 더 나은 ScrollView가 정답이며 Cachapa의 Matt Clark 코드에 대한 링크는 완전성과 일반화로 인해 현재 최고의 솔루션입니다.
Huperniketes 2016 년

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.