ScrollView里面的ScrollView

我知道Google的人要求我们不要将Scrollable视图放在另一个Scrollable视图中,但是有没有官方声明指示我们不这样做?

这足够接近了吗?

你不应该使用一个ListView的Horizo​​ntalScrollView,因为ListView负责自己的滚动。 最重要的是,这样做会损害ListView中处理大型列表的所有重要优化,因为它有效地强制ListView显示其整个项目列表,以填充由Horizo​​ntalScrollView提供的无限容器。

http://developer.android.com/reference/android/widget/Horizo​​ntalScrollView.html

更新:

既然你可能被迫使用二维滚动视图,你可以考虑使用这个: 互联网归档 blog.gorges.us/2010/06/android-two-dimensional-scrollview/

我没有使用这个,但它可能是一个合理的方法。

试试这个

注意:这里parentScrollView表示外部ScrollView和childScrollView意味着childScrollView 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上面的答案是正确的做法。 但是,如果有人需要将其应用于父控制较less的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(); } } 

该function的作用是在myScroll中添加一个触摸监听器,当触摸开始在孩子中时禁用父母的触摸拦截,然后在触摸实际结束时将其启用。 您不需要对父ScrollView的引用,它不必是直接的父对象……它将显示列表直到find它。

在我看来,两全其美。

 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。

这是一个可能的解决scheme。 当到达孩子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步 – 这里是正确function所需的魔术方法

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

有没有他们的正式声明指示我们不这样做?

我认为虽然我似乎无法在笔记中find它。 我知道我在尝试在列表活动中使用滚动视图时发现了这样的声明。 我认为在Android UI系统处理嵌套的可滚​​动事件的方式中,实际上有一个逻辑焦点“bug”,可能应该更好地检测并传递给开发人员。 但我的build议是…

最后,为了用户的目的,最好考虑单个可滚动的视图。 这就像在HTML页面的滚动条内部有滚动条; 它可能是合法的,但它是一个可怕的用户体验。

其实,有一个关于它的官方声明,在一个叫做“ ListView的世界很老的video 。 他们说不要把任何可滚动的视图放在另一个视图里(当两者都在同一个方向)。

不过,现在我们有了一个新的观点,可以让两个视图同时滚动,可能会显示一个很酷的效果:

https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html

我没有find任何这方面的例子,所以我写的只是猜测它的作用和用途。

我find了一个很好的解决scheme。 请使用此代码。

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

这一定会奏效。 请尝试让我知道如果不工作。

你可以把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); } } 

在这里,我已经创build了一个ScrollView中的ScrollView相关的示例项目。 一个视图是可滚动的两种方式。 一探究竟 :-

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

Android支持v4库有一个名为NestedScrollView的类。

尝试嵌套滚动视图: http : //ivankocijan.xyz/android-nestedscrollview/

如果有人正在寻找答案,我有一个稍微不同的实现。 我扩展了ScrollView类,并在child中实现了onTouchListener,并在构造函数中将其设置为self。

在onTouchcallback中,如果运动事件对象的指针计数值为2,则返回true,否则返回false。 这样,如果两个手指在屏幕上移动,它会认为它是缩放的捏,否则会认为它是正常的滚动。 我没有要求父母触摸禁用等

 @Override public boolean onTouch(View view, MotionEvent motionEvent) { if(motionEvent.getPointerCount() == 2){ mCallbacks.onPinchZoomAction(motionEvent); return true; } return false; } 

它不仅是Google说它的坏习惯,它没有太大的意义。 假设你有两个嵌套在一个里面的垂直滚动视图。 当你移动你的手指在滚动视图上,你要移动哪一个,内部还是外部?

你应该重新思考你的用户界面devise,不需要这些,有很多方法来制作一个好的用户界面,而且仍然保持简单。