Androidの垂直および水平のScrollview


151

垂直および水平のScrollviewの解決策を探すのに本当に疲れています。

この機能を実装するフレームワークにはビュー/レイアウトがないと読みましたが、次のようなものが必要です。

レイアウトを他のレイアウト内に定義する必要があります。子レイアウトは、移動のための垂直/水平スクロールを実装する必要があります。

最初はピクセルごとにレイアウトを移動するコードを実装しましたが、それは正しい方法ではないと思います。ScrollViewとHorizo​​ntal ScrollViewで試してみましたが、垂直スクロールまたは水平スクロールしか実装していないため、期待どおりに機能しません。

Canvasは私のソリューションではありません。誰かの子要素にリスナーをアタッチする必要があるからです。

私に何ができる?



14
これがそのままAndroidで利用できないのは、とんでもないことです。私たちはこれまでに半ダースの他のUIテクノロジを使用してきましたが、水平/垂直スクロールコンテナーなどの基本的なものがないことは前例がありません。
flexicious.com 2014年

それでは最後に、データグリッド用に独自にandroidjetpack.com/Home/AndroidDataGridを作成しました。水平/垂直スクロールだけではありません。しかし、アイデアは基本的にHScrollerをVertical Scroller内にラップすることです。また、内側のスクローラーと他のいくつかの悪意のある対象をターゲットにするには、子の追加/削除メソッドをオーバーライドする必要がありますが、機能します。
flexicious.com 2014

回答:


90

上記の提案のいくつかを組み合わせると、良い解決策を得ることができました:

カスタム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;
    }
}

カスタム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_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.マークル2012

1
すごい!ベロシティを考慮に入れるためのいくつかの調整。これは完璧です(また、LinearLayout私が推測する最初は必要ありません)
Romuald Brunet

Mahdiさん、アクティビティクラスのタッチリスナーを再登録したコントロール/ウィジェットについて教えてください。
Shachillies 2013年

遅れて申し訳ありません@DeepakSharma。アクティビティにタッチリスナーを登録せず、アクティビティのonTouchEventをオーバーライドしただけです。
Mahdi Hijazi 2013

@MahdiHijazi uは、水平スクロールと垂直スクロールのコードを追加してくださいすることができますgithub.com/MikeOrtiz/TouchImageView これが正常にボタンのクリックで画像をズーム、スクロールが、画像に失敗した
Erum

43

これは「Android縦+横ScrollView」のGoogleでの最初の検索結果のようですので、ここに追加した方がいいと思いました。Matt ClarkがAndroidソースに基づいてカスタムビューを作成しましたが、完全に機能するようです:2次元のScrollView

そのページのクラスには、ビューの水平方向の幅を計算するバグがあることに注意してください。Manuel Hiltyによる修正がコメントにあります。

解決策:808行目のステートメントを次のように置き換えます。

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


編集:リンクは機能しなくなりましたが、こちらは古いバージョンのブログ投稿へのリンクです


3
ありがとう、それが私が探していたものです。
Andrey Agibalov、2011

1
これは、少し変更を加えた後の魅力のように機能します。私はScrollViewソース(v2.1)fillViewportonMeasureまったく同じように再実装しました。最初の2つのコンストラクタも:this( context, null );とで変更しましたthis(context, attrs, R.attr.scrollViewStyle);。そのことを、私が使用してスクロールバーを使用することができますsetVerticalScrollBarEnabledし、setHorizontalScrollBarEnabledコードインチ
olivier_sdg

私にとっても素晴らしい作品です!どうもありがとうございました!
Avio、2012年

しかし、スクロールつまみのサイズとスクロール位置を下から垂直に、水平モードで右から取得する方法
Ravi

10
リンクされた投稿が表示されなくなったようです。
loeschg 2014年

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リモートコマンドの結果からリアルタイム出力を出力する2次元のスクロール可能なビューを作成するためにも、私にとってはうまくいきました。
ピルクロパイプ

@Kronos:水平方向にのみスクロールしたい場合はどうなりますか?私のyパラメーターは、container.scrollBy(currentX-x2、currentY-y2)に何を含める必要がありますか?水平方向にのみスクロールさせる方法はありますか?
Ashwin 2012

@Kronos:スクロールしすぎます。終わりに達したときにスクロールを停止する方法はありますか?
アシュウィン

ボタンはスクロールできません、なぜですか?
salih kallai 2017年

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の回答に基づく私の解決策ですが、カスタムビューはありません。

レイアウト:

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

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コードを確認できます。Webビューのようなビューが必要だったので、ScrollViewを変更しました。しかし、これはあなたのニーズに合わないかもしれません。

対象とするUIの種類を教えてください。

よろしく、

ラビパンディット


1

コードをいじって、Horizo​​ntalScrollViewをScrollViewに入れることができます。これにより、2つのスクロール方法を同じビューで使用できます。

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

これがお役に立てば幸いです。


1
これは良い解決策ではありません。お気づきのように、ビューをドラッグするときは、常に垂直方向または水平方向にドラッグしています。ネストされたスクロールビューを使用して、対角線に沿ってドラッグすることはできません。
スカイケルシー

斜めのスクロールがないことより悪いのは、一方がネストされているため、ビューの端に両方のスクロールバーが残っていないことです。より良いScrollViewが答えであり、完全性と一般化により、現時点ではCachapaのMatt Clarkのコードへのリンクが最良のソリューションです。
Huperniketes

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.