Je sais que des gens de Google nous ont demandé de ne pas mettre la vue Scrollable dans une autre vue Scrollable, mais y a-t-il une déclaration officielle de leur part nous ordonnant de ne pas le faire?
Est-ce assez proche?
Vous ne devez jamais utiliser un HorizontalScrollView avec un ListView, car ListView s'occupe de son propre défilement. Plus important encore, cela annule toutes les optimisations importantes de ListView pour traiter les grandes listes, car elle oblige effectivement ListView à afficher l'intégralité de sa liste d'éléments pour remplir le conteneur infini fourni par HorizontalScrollView.
http://developer.Android.com/reference/Android/widget/HorizontalScrollView.html
MISE À JOUR:
Étant donné que vous pouvez être obligé d'utiliser une vue de défilement bidimensionnelle, vous pouvez envisager d'utiliser ceci: Archive Internet de blog.gorges.us/2010/06/Android-bidimensionnel-scrollview /
Je ne l'ai pas utilisé, mais cela peut être une approche raisonnable.
Essayez celui-ci
Remarque: Ici parentScrollView
signifie ScrollView externe et childScrollView
signifie ScrollView interne
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;
}
});
réponse d'Atul Bhardwaj ci-dessus est la bonne façon de le faire. Mais au cas où quelqu'un aurait besoin de l'appliquer à un ScrollView où vous avez moins de contrôle sur le parent, je pense que c'est assez flexible et juste la façon dont il est censé fonctionner:
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();
}
}
La fonction ajoute un écouteur tactile à myScroll
qui désactive l'interception tactile du parent lorsqu'un contact commence chez l'enfant, puis le réactive lorsque le toucher se termine réellement. Vous n'avez pas besoin d'une référence au parent ScrollView
et ce ne doit pas être le parent immédiat ... il parcourra la liste d'affichage jusqu'à ce qu'il le trouve.
Le meilleur des deux mondes, à mon avis.
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 () = parent scrollView.
Voici une solution possible. Une fois atteint la fin d'un ScrollView enfant, il passe le contrôle au ScrollView parent pour le faire défiler. Il fonctionne avec ScrollView et ListView dans un ScrollView.
Étape 1 - définir le parent OnTouchListener
parentScroll.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
v.getParent().requestDisallowInterceptTouchEvent(false);
return false;
}
});
Étape 2 - définir le OnTouchListener pour enfants (ScrollView ou 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;
}
});
Étape 3 - voici les méthodes magiques requises pour la fonctionnalité correcte
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;
}
J'ai trouvé une très bonne solution. Veuillez utiliser ce code.
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;
}
});
Cela fonctionnera sûrement. S'il vous plaît essayez de me le faire savoir si cela ne fonctionne pas.
[...] y a-t-il une déclaration officielle de leur part nous ordonnant de ne pas le faire?
Je pense qu'il y a bien que je n'arrive pas à le trouver dans mes notes. Je sais que j'ai trouvé une telle déclaration en essayant d'avoir une vue de défilement dans une activité de liste. Je pense qu'il y a en fait un "bug" de focalisation logique dans la façon dont le système Android UI traite les scrollables imbriqués qui devraient probablement être mieux détectés et communiqués au développeur. Mais mon conseil est ...
En fin de compte, il est préférable d'envisager une seule vue déroulante pour le bien de l'utilisateur de toute façon. C'est comme avoir des barres de défilement à l'intérieur des barres de défilement sur une page HTML; il peut être légal mais c'est une expérience utilisateur terrible.
La bibliothèque v4 de support Android possède une classe appelée NestedScrollView.
Essayez la vue de défilement imbriquée: http://ivankocijan.xyz/Android-nestedscrollview/
En fait, il y a une déclaration officielle à ce sujet, sur une vidéo assez ancienne appelée " le monde de ListView ". Ils disent de ne pas mettre de vue déroulante dans une autre (quand les deux sont dans la même direction).
Cependant, nous avons maintenant une nouvelle vue qui permet aux deux vues de défiler en même temps, probablement pour montrer un effet cool:
https://developer.Android.com/reference/Android/support/v4/widget/NestedScrollView.html
Je n'ai trouvé aucun exemple pour cela, donc ce que j'ai écrit n'est qu'une estimation de ce qu'il fait et de son utilisation.
Si quelqu'un cherche une réponse à cela, j'ai eu une implémentation légèrement différente. J'ai étendu la classe ScrollView et implémenté onTouchListener dans l'enfant, et je l'ai défini sur self dans le constructeur.
Dans le rappel onTouch, si l'objet d'événement de mouvement est venu avec une valeur pour le nombre de pointeurs comme 2, j'ai renvoyé true, sinon false. De cette façon, si deux doigts se déplaçaient sur l'écran, cela le considérerait comme une pincée pour zoomer, sinon le considérerait comme un défilement normal. Je n'ai pas demandé la désactivation du toucher des parents, etc.
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if(motionEvent.getPointerCount() == 2){
mCallbacks.onPinchZoomAction(motionEvent);
return true;
}
return false;
}
Ici, j'ai créé un exemple de projet lié à ScrollView dans un ScrollView. Une vue est défilable dans les deux sens. Vérifiez-le :-
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 (pour 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"
/>
Vous pouvez placer un ScrollView dans un autre ScrollView. Étendez simplement le ScrollView enfant pour remplacer la méthode onTouchEvent. Ainsi
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);
}
}