android中的垂直和水平滚动视图

Posted

技术标签:

【中文标题】android中的垂直和水平滚动视图【英文标题】:Scrollview vertical and horizontal in android 【发布时间】:2010-01-11 20:44:04 【问题描述】:

我真的很厌倦寻找垂直和水平滚动视图的解决方案。

我读到框架中没有任何视图/布局实现此功能,但我需要这样的东西:

我需要在其他中定义一个布局,子布局必须实现垂直/水平滚动才能移动。

最初实现了一个逐像素移动布局的代码,但我认为这不是正确的方法。 我用 ScrollView 和 Horizo​​ntal ScrollView 进行了尝试,但没有像我想要的那样工作,因为它只实现垂直或水平滚动。

Canvas 不是我的解决方案,因为我需要在某人的子元素中附加监听器。

我能做什么?

【问题讨论】:

***.com/questions/11775677/… android 没有开箱即用,这绝对是荒谬的。我们已经使用了六种其他 UI 技术,没有像水平/垂直滚动容器这样基本的东西是闻所未闻的。 好的,所以最后我们为我们的数据网格编写了自己的:androidjetpack.com/Home/AndroidDataGrid。不仅仅是水平/垂直滚动。但这个想法基本上是将 HScroller 包装在 Vertical Scroller 中。您还必须覆盖添加/删除子方法以定位内部滚动条和其他一些恶作剧,但它有效。 你可能会发现这个答案很有帮助***.com/a/62733625/311445 【参考方案1】:

混合了上面的一些建议,并且能够得到一个很好的解决方案:

自定义滚动视图:

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;
    

自定义 Horizo​​ntalScrollView:

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_
    android:layout_>
    <com.scrollable.view.VScroll android:layout_
        android:layout_ android:id="@+id/vScroll">
        <com.scrollable.view.HScroll android:id="@+id/hScroll"
            android:layout_ android:layout_>
            <ImageView android:layout_ android:layout_ android:src="@drawable/bg"></ImageView>
        </com.scrollable.view.HScroll>
    </com.scrollable.view.VScroll>

</LinearLayout>

【讨论】:

这是一个很好的答案,我在视图中有其他滚动元素也需要更改。我所要做的就是和其他人一起触发他们的滚动。 太棒了!考虑到速度的一些调整,这将是完美的(另外,我猜你不需要最初的LinearLayout 嘿,Mahdi,你能告诉我吗,你在哪个控件/小部件上重新注册了活动类中的触摸监听器。 抱歉耽搁了@DeepakSharma。我没有在活动中注册任何触摸监听器,我只是覆盖了活动的onTouchEvent。 @MahdiHijazi 你能在github.com/MikeOrtiz/TouchImageView 中添加你的水平滚动和垂直滚动代码吗?这是在按钮单击时成功缩放图像但无法滚动图像【参考方案2】:

由于这似乎是 Google 中“Android 垂直+水平 ScrollView”的第一个搜索结果,我想我应该在这里添加它。 Matt Clark 已经基于 Android 源码构建了一个自定义视图,它似乎可以完美运行:Two Dimensional ScrollView

请注意,该页面中的类在计算视图的水平宽度时存在错误。 Manuel Hilty 在 cmets 中进行了修复:

解决方案:将第 808 行的语句替换为以下内容:

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

编辑:链接不再起作用,但here is a link to an old version of the blogpost。

【讨论】:

谢谢,这正是我想要的。 在对我进行一些小的修改后,这就像一个魅力。我重新实现了fillViewportonMeasure,就像 ScrollView 源代码 (v2.1)。我还将第一个构造函数更改为:this( context, null );this(context, attrs, R.attr.scrollViewStyle);。这样,我可以在代码中使用setVerticalScrollBarEnabledsetHorizontalScrollBarEnabled 使用滚动条。 对我来说也很棒!非常感谢! 但是如何在水平模式下从底部垂直和从右侧获取滚动拇指大小和滚动位置 链接的帖子好像消失了。【参考方案3】:

我找到了更好的解决方案。

XML:(设计.xml)

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_ android:layout_>
  <FrameLayout android:layout_ android:layout_>
    <RelativeLayout android:id="@+id/container" android:layout_ android:layout_>        
    </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; 
  

这行得通!!!

如果要加载其他布局或控件,结构相同。

【讨论】:

刚试过这个,效果很好!它没有滚动指示器或弹跳,但由于这是一种较低级别的方法,因此可以很容易地在此基础上构建这些东西。谢谢! 对我来说也很有效,因为它可以编写一个二维可滚动视图,从网络上的 SSH 远程命令的结果输出实时输出。 @Kronos :如果我只想水平滚动怎么办?我的 y 参数应该在 container.scrollBy(currentX - x2 , currentY - y2) 中。有什么方法可以让它只水平滚动? @Kronos :它滚动得太远了。有没有办法在到达终点时停止滚动? 按钮不可滚动,为什么?【参考方案4】:

我使用它并且工作正常:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView android:id="@+id/ScrollView02" 
            android:layout_ 
            android:layout_
            xmlns:android="http://schemas.android.com/apk/res/android">
<HorizontalScrollView android:id="@+id/HorizontalScrollView01" 
                      android:layout_ 
                      android:layout_>
<ImageView android:id="@+id/ImageView01"
           android:src="@drawable/pic" 
           android:isScrollContainer="true" 
           android:layout_ 
           android:layout_ 
           android:adjustViewBounds="true">
</ImageView>
</HorizontalScrollView>
</ScrollView>

源链接在这里:Android-spa

【讨论】:

它充其量对静态内容“很好”。多位 Google 工程师表示,您尝试使用的内容会出现问题(groups.google.com/group/android-developers/browse_frm/thread/… 和 groups.google.com/group/android-beginners/browse_thread/thread/…)。 @CommonsWare:对优秀的多位 Google 工程师表示敬意,在他们告诉你从头开始重写你自己的组件的那些线程中:这是最好的方法。肯定是真的。但是他们并没有说这个解决方案不好,如果它有效......也许对他们来说需要一分钟,对我来说最好使用现有组件编写一些xml。 “静态内容”是什么意思?我使用按钮和图像。 “静态内容”是指不涉及触摸事件本身的事物。我的观点 - 对于其他阅读此答案的人 - 是虽然您的解决方案可能适用于单个 ImageView 作为可滚动内容,但它可能适用于也可能不适用于任意其他内容,并且谷歌认为会有问题你的方法。 Google 的意见对于未来的发展也很重要——如果他们建议人们一开始不要使用它,他们可能不会试图确保您的方法长期有效。 这对我来说很好......我正在使用线程刷新此滚动视图中的图像视图【参考方案5】:

我的解决方案基于Mahdi Hijazi answer,但没有任何自定义视图:

布局:

<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/scrollHorizontal"
    android:layout_
    android:layout_>

    <ScrollView 
        android:id="@+id/scrollVertical"
        android:layout_
        android:layout_ >

        <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 时,列表开始在手指上双向滚动“自动”。 首先水平滚动时有效。当垂直为第一时,仅垂直移动 还在吗?真的吗? 7年?我已经很多年没有接触 Android 开发了。哇。感谢您的反馈:)【参考方案6】:

选项 #1:您可以提出一种不需要同时水平和垂直滚动的新 UI 设计。

选项#2:您可以获取ScrollViewHorizontalScrollView 的源代码,了解核心Android 团队如何实现这些,并创建您自己的BiDirectionalScrollView 实现。

选项 #3:您可以摆脱需要您使用小部件系统并直接绘制到 Canvas 的依赖项。

选项 #4:如果您偶然发现一个似乎实现了您所寻求的开源应用程序,请看看他们是如何做到的。

【讨论】:

【参考方案7】:

试试这个

<?xml version="1.0" encoding="utf-8"?>
<ScrollView android:id="@+id/Sview" 
        android:layout_ 
        android:layout_
        xmlns:android="http://schemas.android.com/apk/res/android">
<HorizontalScrollView 
   android:id="@+id/hview" 
    android:layout_ 
    android:layout_>
<ImageView .......
 [here xml code for image]
</ImageView>
</HorizontalScrollView>
</ScrollView>

【讨论】:

【参考方案8】:

由于二维滚动视图链接已失效,我无法获取它,因此我创建了自己的组件。它可以处理投掷并为我正常工作。 唯一的限制是 wrap_content 可能不适用于该组件。

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

【讨论】:

【参考方案9】:

我有解决您问题的方法。您可以检查 ScrollView 代码它只处理垂直滚动并忽略水平滚动并修改它。我想要一个像 webview 这样的视图,所以修改了 ScrollView,它对我来说效果很好。但这可能不适合您的需求。

让我知道您的目标用户界面类型。

问候,

拉维·潘迪特

【讨论】:

【参考方案10】:

玩代码,你可以把一个Horizo​​ntalScrollView放到一个ScrollView中。因此,您可以在同一个视图中拥有两个滚动方法。

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

希望对你有帮助。

【讨论】:

这不是一个好的解决方案。如果您注意到,在拖动视图时,您总是在垂直或水平拖动。您不能使用嵌套滚动视图沿对角线拖动。 比缺少对角滚动更糟糕的是缺少两个滚动条保留在视图边缘,因为一个是嵌套的。更好的 ScrollView 是答案,由于完整性和泛化性,Cachapa 与 Matt Clark 代码的链接是目前最好的解决方案。 我使用了一种技巧,可以同时显示垂直和水平滚动条,你可以参考这个***.com/a/69925768/5426065【参考方案11】:

这样使用 我试过了,我修好了

把你所有的 XML 布局放进去

<android.support.v4.widget.NestedScrollView 

我在此链接中对此进行了解释 vertical recyclerView and Horizontal recyclerview scrolling together

【讨论】:

【参考方案12】:

如果你想垂直和水平滚动这是一个例子:

垂直滚动内的水平滚动并且两者都有效:

<ScrollView
        android:id="@+id/verticalScroll"
        android:layout_
        android:layout_>

...
     <HorizontalScrollView
            android:id="@+id/horizontalScroll"
            android:layout_
            android:layout_
            *android:overScrollMode="never"*>
...
     </HorizontalScrollView>
...
</ScrollView>

【讨论】:

以上是关于android中的垂直和水平滚动视图的主要内容,如果未能解决你的问题,请参考以下文章

如何获得列表视图的水平滚动和垂直滚动?

Android学习笔记技巧之垂直和水平滚动视图

水平和垂直回收器视图中的滚动问题

UITableview ios中的水平和垂直滚动视图

Android 2D滚动列表视图?

UIScrollView 子视图中的水平 UISwipeGestureRecognizer ? (UIScrollView 只需要识别垂直滚动)