Scrollview แนวตั้งและแนวนอนใน Android


152

ฉันเหนื่อยมากที่กำลังมองหาวิธีแก้ไข Scrollview แนวตั้งและแนวนอน

ฉันอ่านว่าไม่มีมุมมอง / เค้าโครงในกรอบงานที่ใช้คุณสมบัตินี้ แต่ฉันต้องการสิ่งนี้:

ฉันจำเป็นต้องกำหนดเค้าโครงภายในอื่น ๆ เค้าโครงลูกต้องดำเนินการเลื่อนแนวตั้ง / แนวนอนสำหรับการย้าย

เริ่มแรกใช้รหัสที่ย้ายเค้าโครงพิกเซลทีละพิกเซล แต่ฉันคิดว่านั่นไม่ใช่วิธีที่ถูกต้อง ฉันลองใช้ ScrollView และ Horizontal ScrollView แต่ไม่มีอะไรที่ฉันต้องการเพราะมันใช้การเลื่อนแนวตั้งหรือแนวนอนเท่านั้น

Canvas ไม่ใช่วิธีแก้ปัญหาของฉันเพราะฉันจำเป็นต้องแนบผู้ฟังในองค์ประกอบย่อยของเด็ก

ฉันจะทำอย่างไร



14
มันไร้สาระอย่างแน่นอนว่านี่ไม่สามารถใช้งานได้กับ Android เราได้ทำงานร่วมกับเทคโนโลยี UI อื่น ๆ อีกครึ่งโหลและมันไม่เคยมีมาก่อนที่จะไม่มีสิ่งพื้นฐานเช่นคอนเทนเนอร์เลื่อนแนวนอน / แนวตั้ง
flexicious.com

ตกลงดังนั้นในที่สุดเราเขียนของเราเองสำหรับ DataGrid ของเรา: androidjetpack.com/Home/AndroidDataGrid ทำอะไรได้มากกว่าแค่การเลื่อนในแนวนอน / แนวตั้ง แต่แนวคิดก็คือห่อ HScroller ไว้ใน Vertical Scroller คุณต้องแทนที่การเพิ่ม / ลบวิธีการของเด็กเพื่อกำหนดเป้าหมายไปที่ scroller ด้านในและ shenanigans อื่น ๆ อีกสองสามตัว แต่ก็ใช้งานได้
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;
    }
}

CustomScrollView ที่กำหนดเอง:

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

เฮ้มาห์คุณช่วยบอกฉันหน่อยได้ไหมว่าตัวควบคุม / วิดเจ็ตตัวใดที่คุณได้ใส่ฟังการสัมผัสในชั้นเรียนกิจกรรมอีกครั้ง
Shachillies

ขออภัยสำหรับความล่าช้า @DeepakSharma ฉันไม่ได้ลงทะเบียนผู้ฟังแบบสัมผัสใด ๆ ในกิจกรรมฉันแค่แทนที่ onTouchEvent ของกิจกรรม
Mahdi Hijazi

@MahdiHijazi คุณช่วยกรุณาเพิ่มรหัสของการเลื่อนแนวนอนและการเลื่อนแนวตั้งในgithub.com/MikeOrtiz/TouchImage ดู นี่เป็นการซูมภาพเสร็จเมื่อคลิกปุ่ม แต่ไม่สามารถเลื่อนภาพได้
Erum

43

เนื่องจากนี่เป็นผลการค้นหาครั้งแรกใน Google สำหรับ "Android แนวตั้ง + แนวนอน ScrollView" ฉันคิดว่าฉันควรเพิ่มที่นี่ Matt Clark ได้สร้างมุมมองที่กำหนดเองตามแหล่งที่มาของ Android และดูเหมือนว่าจะทำงานได้อย่างสมบูรณ์แบบ: สองมิติ ScrollView

ระวังว่าคลาสในหน้านั้นมีข้อบกพร่องในการคำนวณความกว้างขอบฟ้าของมุมมอง การแก้ไขโดย Manuel Hilty อยู่ในความคิดเห็น:

การแก้ไข: แทนที่คำสั่งที่บรรทัด 808 ด้วยคำสั่งต่อไปนี้

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


แก้ไข:การเชื่อมโยงไม่ได้ทำงานอีกต่อไป แต่ที่นี่คือการเชื่อมโยงไปยังรุ่นเก่าบล็อกโพสต์ที่


3
ขอบคุณนั่นคือสิ่งที่ฉันกำลังมองหา
Andrey Agibalov

1
มันใช้งานได้อย่างมีเสน่ห์หลังจากได้รับการดัดแปลงเล็กน้อยสำหรับฉัน ฉันปรับใช้ใหม่fillViewportและonMeasureเหมือนกับแหล่งที่มาของ ScrollView (v2.1) ฉันยังมีการเปลี่ยนแปลงทั้งสองคอนสตรัคครั้งแรกกับ: และthis( context, null ); this(context, attrs, R.attr.scrollViewStyle);ด้วยสิ่งนี้ฉันสามารถใช้แถบเลื่อนโดยใช้setVerticalScrollBarEnabledและsetHorizontalScrollBarEnabledในรหัส
olivier_sdg

ใช้งานได้ดีมากสำหรับฉัน! ขอบคุณมาก!
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>

รหัส 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; 
  }
}

นั่นเป็นผลงาน !!!

ถ้าคุณต้องการโหลดเค้าโครงหรือตัวควบคุมอื่นโครงสร้างจะเหมือนกัน


เพิ่งลองสิ่งนี้และมันใช้งานได้ดีมาก! ไม่มีตัวบ่งชี้การเลื่อนหรือการเหวี่ยง แต่เนื่องจากนี่เป็นวิธีการระดับล่างกว่าสิ่งต่าง ๆ เหล่านี้สามารถสร้างขึ้นมาได้อย่างง่ายดาย ขอบคุณ!
ubzack

ทำงานได้ดีสำหรับฉันเช่นกันสำหรับการเขียนมุมมองแบบเลื่อนได้สองมิติที่แสดงเอาต์พุตแบบเรียลไทม์จากผลลัพธ์ของคำสั่งระยะไกล SSH ผ่านเครือข่าย
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 หลายคนที่ยอดเยี่ยมในหัวข้อเหล่านั้นพวกเขาบอกให้คุณเขียนใหม่จากการเกาองค์ประกอบของคุณเอง: นี่เป็นวิธีที่ดีที่สุด แน่นอนว่าเป็นความจริง แต่พวกเขาไม่ได้บอกว่าวิธีนี้ไม่ดีและถ้าใช้งานได้ ... บางทีพวกเขาอาจใช้เวลาสักครู่สำหรับฉันดีกว่าที่จะเขียน xml โดยใช้ส่วนประกอบที่มีอยู่ "เนื้อหาคงที่" หมายความว่าอะไร ฉันใช้ปุ่มและรูปภาพ
Redax

3
โดย "เนื้อหาสแตติก" ฉันหมายถึงสิ่งที่ไม่เกี่ยวข้องกับกิจกรรมการสัมผัสด้วยตนเอง ประเด็นของฉัน - สำหรับคนอื่น ๆ ที่อ่านคำตอบนี้ - ในขณะที่วิธีการแก้ปัญหาของคุณอาจใช้งานได้ImageViewกับเนื้อหาที่เลื่อนได้ แต่อาจหรืออาจไม่ทำงานสำหรับเนื้อหาอื่นโดยพลการและ Google เชื่อว่าจะมีปัญหากับแนวทางของคุณ ความคิดเห็นของ Google มีความสำคัญกับการพัฒนาในอนาคตเช่นกันพวกเขาอาจไม่พยายามทำให้แน่ใจว่าวิธีการของคุณใช้งานได้ดีในระยะยาวหากพวกเขากำลังให้คำแนะนำแก่ผู้คนที่จะไม่ใช้มันในตอนแรก
CommonsWare

นี่ใช้งานได้ดีสำหรับฉัน ... ฉันกำลังดูภาพใหม่ใน scrollview นี้โดยใช้เธรด
sonu thomas

19

โซลูชันของฉันตามคำตอบของ Mahdi Hijaziแต่ไม่มีมุมมองที่กำหนดเอง:

เค้าโครง:

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

คุณสามารถเปลี่ยนลำดับของ scrollviews เพียงแค่เปลี่ยนลำดับในเลย์เอาต์และในรหัส และแน่นอนแทนที่จะเป็น WethingViewYouWant คุณวางเค้าโครง / มุมมองที่คุณต้องการเลื่อนทั้งสองทิศทาง


ฉันขอแนะนำให้คืนค่าเท็จใน hScroll.setOnTouchListener เมื่อฉันเปลี่ยนเป็นเท็จรายการเริ่มเลื่อน "โดยอัตโนมัติอย่างราบรื่น" บนนิ้วทั้งสองทิศทาง
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

เนื่องจากลิงก์สองมิติ Scrollview นั้นตายไปแล้วฉันจึงไม่สามารถทำได้ดังนั้นฉันจึงสร้างองค์ประกอบของตัวเองขึ้นมา มันจัดการกับการเหวี่ยงและทำงานอย่างถูกต้องสำหรับฉัน ข้อ จำกัด เพียงอย่างเดียวคือ wrap_content อาจทำงานได้ไม่ถูกต้องสำหรับองค์ประกอบนั้น

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


2

ฉันมีทางออกสำหรับปัญหาของคุณ คุณสามารถตรวจสอบรหัส ScrollView ที่จัดการเฉพาะการเลื่อนแนวตั้งและละเว้นแนวนอนและแก้ไขสิ่งนี้ ฉันต้องการมุมมองเหมือน webview ดังนั้น ScrollView ที่ได้รับการแก้ไขจึงทำงานได้ดีสำหรับฉัน แต่นี่อาจไม่เหมาะกับความต้องการของคุณ

แจ้งให้เราทราบว่าคุณกำหนดเป้าหมาย UI ประเภทใด

ความนับถือ,

ราวีบัณฑิต


1

เล่นกับรหัสคุณสามารถวาง HorizontalScrollView ลงใน ScrollView ดังนั้นคุณสามารถมีสองวิธีเลื่อนในมุมมองเดียวกัน

ที่มา: http://androiddevblog.blogspot.com/2009/12/creating-two-dimensions-scroll-view.html

ฉันหวังว่านี่จะช่วยคุณได้


1
นี่ไม่ใช่ทางออกที่ดี หากคุณสังเกตเห็นเมื่อลากมุมมองคุณจะลากแนวตั้งหรือแนวนอนเสมอ คุณไม่สามารถลากในแนวทแยงโดยใช้มุมมองเลื่อนแบบซ้อน
Sky Kelsey

สิ่งที่แย่กว่าการขาดการเลื่อนในแนวทแยงคือการไม่มีแถบเลื่อนทั้งสองที่เหลืออยู่ที่ขอบมุมมองเนื่องจากมีแถบเลื่อนซ้อนอยู่ ScrollView ที่ดีกว่าคือคำตอบและลิงก์ของ Cachapa ไปยังโค้ดของ Matt Clark เป็นทางออกที่ดีที่สุดในขณะนี้เนื่องจากความสมบูรณ์และความเห็นโดยทั่วไป
Huperniketes

0

ใช้วิธีนี้ฉันพยายามทำสิ่งนี้แล้วฉันจะแก้ไข

วางเค้าโครง XML ทั้งหมดของคุณไว้ข้างใน

<android.support.v4.widget.NestedScrollView 

ฉันอธิบายสิ่งนี้ในลิงค์แนวตั้งนี้ recyclerView และแนวนอน recyclerview เลื่อนพร้อมกัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.