Googleの人々が、スクロール可能なビューを別のスクロール可能なビューの中に入れないように要求していることを知っていますが、そうしないように指示する公式の声明はありますか?
これで十分ですか?
ListViewが独自のスクロールを処理するため、HorizontalScrollViewをListViewで使用しないでください。最も重要なことは、これを行うと、ListViewが項目のリスト全体を表示して、HorizontalScrollViewが提供する無限コンテナをいっぱいにするため、大きなリストを処理するためのListViewのすべての重要な最適化を無効にします。
http://developer.Android.com/reference/Android/widget/HorizontalScrollView.html
更新:
2次元のスクロールビューの使用を余儀なくされる可能性があるため、これを使用することを検討できます。 インターネットアーカイブof blog.gorges.us/2010/06/Android-two-dimensional-scrollview /
私はこれを使用していませんが、合理的なアプローチかもしれません。
これを試してみてください
注:ここでparentScrollView
はOuter ScrollViewを意味し、childScrollView
はInnner ScrollViewを意味します
parentScrollView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.v(TAG, "PARENT TOUCH");
findViewById(R.id.child_scroll).getParent()
.requestDisallowInterceptTouchEvent(false);
return false;
}
});
childScrollView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.v(TAG, "CHILD TOUCH");
// Disallow the touch request for parent scroll on touch of child view
v.getParent().requestDisallowInterceptTouchEvent(true);
return false;
}
});
上記のAtul Bhardwajの答え が正しい方法です。しかし、親をあまり制御できないScrollViewに誰かがそれを適用する必要がある場合、これは十分に柔軟性があり、動作するはずの方法だと思います。
private void makeMyScrollSmart() {
myScroll.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View __v, MotionEvent __event) {
if (__event.getAction() == MotionEvent.ACTION_DOWN) {
// Disallow the touch request for parent scroll on touch of child view
requestDisallowParentInterceptTouchEvent(__v, true);
} else if (__event.getAction() == MotionEvent.ACTION_UP || __event.getAction() == MotionEvent.ACTION_CANCEL) {
// Re-allows parent events
requestDisallowParentInterceptTouchEvent(__v, false);
}
return false;
}
});
}
private void requestDisallowParentInterceptTouchEvent(View __v, Boolean __disallowIntercept) {
while (__v.getParent() != null && __v.getParent() instanceof View) {
if (__v.getParent() instanceof ScrollView) {
__v.getParent().requestDisallowInterceptTouchEvent(__disallowIntercept);
}
__v = (View) __v.getParent();
}
}
この関数が行うことは、タッチリスナーをmyScroll
に追加することです。これは、子でタッチが開始されると親のタッチインターセプトを無効にし、実際にタッチが終了すると有効になります。親ScrollView
への参照は必要なく、直接の親である必要はありません...それが見つかるまで表示リストを移動します。
私の意見では、両方の長所があります。
childScrollView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// Disallow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
// Allow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(false);
break;
}
return false;
}
});
v.getParent()=親scrollView。
これが解決策です。子のScrollViewの最後に到達すると、コントロールを親のScrollViewに渡してスクロールします。 ScrollViewおよびScrollView内のListViewで動作します。
ステップ1-親OnTouchListenerを設定する
parentScroll.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
v.getParent().requestDisallowInterceptTouchEvent(false);
return false;
}
});
ステップ2-子のOnTouchListener(ScrollViewまたはListView)を設定します
aChildScrollView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event)
{
v.getParent().requestDisallowInterceptTouchEvent(shouldRequestDisallowIntercept((ViewGroup) v, event));
return false;
}
});
aListView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
v.getParent().requestDisallowInterceptTouchEvent(shouldRequestDisallowIntercept((ViewGroup) v, event));
return false;
}
});
手順3-正しい機能を実現するために必要な魔法の方法を次に示します
protected boolean shouldRequestDisallowIntercept(ViewGroup scrollView, MotionEvent event) {
boolean disallowIntercept = true;
float yOffset = getYOffset(event);
if (scrollView instanceof ListView) {
ListView listView = (ListView) scrollView;
if (yOffset < 0 && listView.getFirstVisiblePosition() == 0 && listView.getChildAt(0).getTop() >= 0) {
disallowIntercept = false;
}
else if (yOffset > 0 && listView.getLastVisiblePosition() == listView.getAdapter().getCount() - 1 && listView.getChildAt(listView.getChildCount() - 1).getBottom() <= listView.getHeight()) {
disallowIntercept = false;
}
}
else {
float scrollY = scrollView.getScrollY();
disallowIntercept = !((scrollY == 0 && yOffset < 0) || (scrollView.getHeight() + scrollY == scrollView.getChildAt(0).getHeight() && yOffset >= 0));
}
return disallowIntercept;
}
protected float getYOffset(MotionEvent ev) {
final int historySize = ev.getHistorySize();
final int pointerCount = ev.getPointerCount();
if (historySize > 0 && pointerCount > 0) {
float lastYOffset = ev.getHistoricalY(pointerCount - 1, historySize - 1);
float currentYOffset = ev.getY(pointerCount - 1);
float dY = lastYOffset - currentYOffset;
return dY;
}
return 0;
}
非常に良い解決策を見つけました。このコードを使用してください。
parentScrollView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
Utils.showLog("PARENT TOUCH");
findViewById(R.id.activity_mesh_child_scrollView).getParent().requestDisallowInterceptTouchEvent(false);
return false;
}
});
childScrollView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
Utils.showLog("CHILD TOUCH");
// Disallow the touch request for parent scroll on touch of child view
v.getParent().requestDisallowInterceptTouchEvent(true);
return false;
}
});
これは確実に機能します。動作していない場合は試してみてください。
[...]彼らから、そうしないようにとの公式声明はありますか?
私はメモにそれを見つけることができないようですが、あると思います。リストアクティビティでスクロールビューを表示しようとしたときに、このようなステートメントを見つけたことがわかります。実際、Android UIシステムがネストされたスクロール可能オブジェクトを処理する方法に論理的なフォーカス "バグ"があると思います。
最終的には、ユーザーのために単一のスクロール可能なビューを検討することをお勧めします。これは、HTMLページのスクロールバーの中にスクロールバーがあるようなものです。それは合法かもしれませんが、ひどいユーザーエクスペリエンスです。
Androidサポートv4ライブラリには、NestedScrollViewというクラスがあります。
ネストされたスクロールビューを試す: http://ivankocijan.xyz/Android-nestedscrollview/
実際には、「 the ListView 」という非常に古いビデオに、公式の声明があります。彼らは、スクロール可能なビューを別のビュー内に配置しないように言っています(両方が同じ方向にある場合)。
ただし、今では両方のビューを同時にスクロールできる新しいビューがあり、おそらくクールな効果を示しています。
https://developer.Android.com/reference/Android/support/v4/widget/NestedScrollView.html
この例は見つかりませんでしたので、私が書いたのは、それが何をするのか、何のために使われているのかの推測にすぎません。
誰かがこれに対する答えを探しているなら、私はわずかに異なる実装を持っていました。 ScrollViewクラスを拡張し、子にonTouchListenerを実装し、コンストラクターでselfに設定しました。
OnTouchコールバックで、モーションイベントオブジェクトのポインターカウントの値が2の場合、trueを返し、それ以外の場合はfalseを返しました。このように、2本の指が画面上を移動している場合、ズームするピンチと見なされ、そうでなければ通常のスクロールと見なされます。親タッチの無効化などをリクエストしませんでした。
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if(motionEvent.getPointerCount() == 2){
mCallbacks.onPinchZoomAction(motionEvent);
return true;
}
return false;
}
ここでは、ScrollView内にScrollViewに関連するサンプルプロジェクトを作成しました。 1つのビューは両方の方法でスクロール可能です。見てみな :-
MainActivity.Java-
package com.example.dev_task_193_scrollview;
import com.example.dev_task_196_scrollview.R;
import Android.app.Activity;
import Android.os.Bundle;
import Android.view.Menu;
import Android.view.MotionEvent;
import Android.view.View;
import Android.widget.AdapterView;
import Android.widget.ArrayAdapter;
import Android.widget.HorizontalScrollView;
import Android.widget.ImageView;
import Android.widget.ListView;
import Android.widget.RelativeLayout;
import Android.widget.ScrollView;
import Android.widget.Toast;
public class MainActivity extends Activity implements View.OnClickListener{
ImageView imageView1,imageView2,imageView3,IVimage1,IVimage2,IVimage3,IVimage4,IVimage5,IVimage6;
ListView listView1,listView2;
HorizontalScrollView horizontalScrollView1,horizontalScrollView2;
ScrollView parentScrollView, scrollView1;
RelativeLayout relativeLayout1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String[] values = new String[] { "Android", "iPhone", "WindowsMobile",
"Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X",
"Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X", "Linux",
"OS/2", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2",
"Android", "iPhone", "WindowsMobileWindowsMobileWindowsMobileWindowsMobile" };
relativeLayout1 = (RelativeLayout) findViewById(R.id.relativeLayout1);
imageView1 = (ImageView) findViewById(R.id.imageView1);
imageView1.setBackgroundResource(R.drawable.info);
imageView2 = (ImageView) findViewById(R.id.imageView2);
imageView2.setBackgroundResource(R.drawable.info);
imageView3 = (ImageView) findViewById(R.id.imageView3);
imageView3.setBackgroundResource(R.drawable.info);
listView1 = (ListView) findViewById(R.id.listView1);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
R.layout.list_item, values);
listView1.setAdapter(adapter);
listView2 = (ListView) findViewById(R.id.listView2);
ArrayAdapter<String> adapter1 = new ArrayAdapter<String>(this,
R.layout.list_item, values);
listView2.setAdapter(adapter1);
parentScrollView = (ScrollView) findViewById(R.id.parentScrollView);
scrollView1 = (ScrollView) findViewById(R.id.scrollView1);
horizontalScrollView1 = (HorizontalScrollView) findViewById(R.id.horizontalScrollView1);
horizontalScrollView2 = (HorizontalScrollView) findViewById(R.id.horizontalScrollView2);
IVimage1 = (ImageView) findViewById(R.id.IVimage1);
IVimage2 = (ImageView) findViewById(R.id.IVimage2);
IVimage3 = (ImageView) findViewById(R.id.IVimage3);
IVimage4 = (ImageView) findViewById(R.id.IVimage4);
IVimage5 = (ImageView) findViewById(R.id.IVimage5);
IVimage6 = (ImageView) findViewById(R.id.IVimage6);
scrollView1.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event)
{
// Disallow the touch request for parent scroll on touch of child view
parentScrollView.requestDisallowInterceptTouchEvent(true);
return false;
}
});
horizontalScrollView1.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event)
{
// Disallow the touch request for parent scroll on touch of child view
parentScrollView.requestDisallowInterceptTouchEvent(true);
return false;
}
});
listView1.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event)
{
// Disallow the touch request for parent scroll on touch of child view
parentScrollView.requestDisallowInterceptTouchEvent(true);
return false;
}
});
listView1.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(getApplicationContext(), "Clicked "+parent.getItemAtPosition(position).toString(), Toast.LENGTH_SHORT).show();
}
});
listView2.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(getApplicationContext(), "Clicked "+parent.getItemAtPosition(position).toString(), Toast.LENGTH_SHORT).show();
}
});
listView2.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event)
{
// Disallow the touch request for parent scroll on touch of child view
parentScrollView.requestDisallowInterceptTouchEvent(true);
return false;
}
});
horizontalScrollView2.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event)
{
// Disallow the touch request for parent scroll on touch of child view
parentScrollView.requestDisallowInterceptTouchEvent(true);
return false;
}
});
/*imageView1.setOnClickListener(this);
imageView2.setOnClickListener(this);
imageView3.setOnClickListener(this);*/
IVimage1.setOnClickListener(this);
IVimage2.setOnClickListener(this);
IVimage3.setOnClickListener(this);
IVimage4.setOnClickListener(this);
IVimage5.setOnClickListener(this);
IVimage6.setOnClickListener(this);
imageView1.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event)
{
// Disallow the touch request for parent scroll on touch of child view
parentScrollView.requestDisallowInterceptTouchEvent(true);
Toast.makeText(getApplicationContext(), "Clicked "+v.getTag(), Toast.LENGTH_SHORT).show();
return false;
}
});
imageView2.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event)
{
// Disallow the touch request for parent scroll on touch of child view
parentScrollView.requestDisallowInterceptTouchEvent(true);
Toast.makeText(getApplicationContext(), "Clicked "+v.getTag(), Toast.LENGTH_SHORT).show();
return false;
}
});
imageView3.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event)
{
// Disallow the touch request for parent scroll on touch of child view
parentScrollView.requestDisallowInterceptTouchEvent(true);
Toast.makeText(getApplicationContext(), "Clicked "+v.getTag(), Toast.LENGTH_SHORT).show();
return false;
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.imageView1:
Toast.makeText(getApplicationContext(), "Clicked "+v.getTag(), Toast.LENGTH_SHORT).show();
break;
case R.id.imageView2:
Toast.makeText(getApplicationContext(), "Clicked "+v.getTag(), Toast.LENGTH_SHORT).show();
break;
case R.id.imageView3:
Toast.makeText(getApplicationContext(), "Clicked "+v.getTag(), Toast.LENGTH_SHORT).show();
break;
case R.id.IVimage1:
Toast.makeText(getApplicationContext(), "Clicked "+v.getTag(), Toast.LENGTH_SHORT).show();
break;
case R.id.IVimage2:
Toast.makeText(getApplicationContext(), "Clicked "+v.getTag(), Toast.LENGTH_SHORT).show();
break;
case R.id.IVimage3:
Toast.makeText(getApplicationContext(), "Clicked "+v.getTag(), Toast.LENGTH_SHORT).show();
break;
case R.id.IVimage4:
Toast.makeText(getApplicationContext(), "Clicked "+v.getTag(), Toast.LENGTH_SHORT).show();
break;
case R.id.IVimage5:
Toast.makeText(getApplicationContext(), "Clicked "+v.getTag(), Toast.LENGTH_SHORT).show();
break;
case R.id.IVimage6:
Toast.makeText(getApplicationContext(), "Clicked "+v.getTag(), Toast.LENGTH_SHORT).show();
break;
}
// TODO Auto-generated method stub
}
}
activity_main.xml-
<ScrollView xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:tools="http://schemas.Android.com/tools"
Android:id="@+id/parentScrollView"
Android:layout_width="fill_parent"
Android:layout_height="fill_parent"
Android:layout_marginBottom="5dp"
Android:layout_marginLeft="5dp"
Android:layout_marginRight="5dp"
Android:layout_marginTop="5dp"
Android:background="@drawable/login_bg" >
<RelativeLayout
Android:id="@+id/relativeLayout1"
Android:layout_width="match_parent"
Android:layout_height="wrap_content" >
<ScrollView
Android:id="@+id/scrollView1"
Android:layout_width="fill_parent"
Android:layout_height="300dp" >
<HorizontalScrollView
Android:id="@+id/horizontalScrollView1"
Android:layout_width="match_parent"
Android:layout_height="300dp"
Android:fillViewport="false" >
<RelativeLayout
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_marginLeft="5dp"
Android:background="@drawable/bg" >
<ImageView
Android:id="@+id/imageView1"
Android:layout_width="300dp"
Android:layout_height="400dp"
Android:tag="imageView1" />
<ImageView
Android:id="@+id/imageView2"
Android:layout_width="300dp"
Android:layout_height="400dp"
Android:layout_toRightOf="@+id/imageView1"
Android:tag="imageView2" />
<ImageView
Android:id="@+id/imageView3"
Android:layout_width="300dp"
Android:layout_height="400dp"
Android:layout_toRightOf="@+id/imageView2"
Android:tag="imageView3" />
</RelativeLayout>
</HorizontalScrollView>
</ScrollView>
<ListView
Android:id="@+id/listView1"
Android:layout_width="500dp"
Android:layout_height="400dp"
Android:layout_below="@+id/scrollView1"
Android:layout_centerHorizontal="true"
Android:layout_marginTop="5dp"
Android:background="@drawable/ic_launcherwrweq" >
</ListView>
<HorizontalScrollView
Android:id="@+id/horizontalScrollView2"
Android:layout_width="300dp"
Android:layout_height="wrap_content"
Android:layout_below="@+id/listView1"
Android:layout_centerHorizontal="true"
Android:layout_gravity="center"
Android:layout_marginTop="5dp"
Android:background="@drawable/claim_detail_header_bg"
Android:fillViewport="true" >
<LinearLayout
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:orientation="horizontal" >
<ImageView
Android:id="@+id/IVimage1"
Android:layout_width="125dp"
Android:layout_height="125dp"
Android:padding="15dp"
Android:src="@drawable/a"
Android:tag="a" >
</ImageView>
<ImageView
Android:id="@+id/IVimage2"
Android:layout_width="125dp"
Android:layout_height="125dp"
Android:padding="15dp"
Android:src="@drawable/b"
Android:tag="b" >
</ImageView>
<ImageView
Android:id="@+id/IVimage3"
Android:layout_width="125dp"
Android:layout_height="125dp"
Android:padding="15dp"
Android:src="@drawable/c"
Android:tag="c" >
</ImageView>
<ImageView
Android:id="@+id/IVimage4"
Android:layout_width="125dp"
Android:layout_height="125dp"
Android:padding="15dp"
Android:src="@drawable/g"
Android:tag="g" >
</ImageView>
<ImageView
Android:id="@+id/IVimage5"
Android:layout_width="125dp"
Android:layout_height="125dp"
Android:padding="15dp"
Android:src="@drawable/e"
Android:tag="e" >
</ImageView>
<ImageView
Android:id="@+id/IVimage6"
Android:layout_width="125dp"
Android:layout_height="125dp"
Android:padding="15dp"
Android:src="@drawable/f"
Android:tag="f" >
</ImageView>
</LinearLayout>
</HorizontalScrollView>
<ListView
Android:id="@+id/listView2"
Android:layout_width="500dp"
Android:layout_height="400dp"
Android:layout_below="@+id/horizontalScrollView2"
Android:layout_centerHorizontal="true"
Android:layout_marginTop="5dp"
Android:background="@drawable/ic_launcherwrweq" >
</ListView>
</RelativeLayout>
</ScrollView>
list_item.xml(ListViewの場合)-
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:id="@Android:id/text1"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:gravity="center_vertical"
Android:textSize="25sp"
Android:maxLines="1"
Android:singleLine="true"
/>
ScrollViewを別のScrollView内に配置できます。子ScrollViewを拡張して、onTouchEventメソッドをオーバーライドします。そのようです
import Android.content.Context;
import Android.util.AttributeSet;
import Android.view.MotionEvent;
public class ChildScrollView extends Android.widget.ScrollView {
private int parent_id;
public ChildScrollView(Context context) {
super(context);
}
public ChildScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ChildScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onTouchEvent(MotionEvent event){
requestDisallowInterceptTouchEvent(true);
return super.onTouchEvent(event);
}
}