web-dev-qa-db-fra.com

Détection de geste et problème ScrollView

J'essaie de créer une mise en page avec un ViewFlipper contenant ScrollViews. L'idée est de détecter les balayages horizontaux pour passer à ScrollView précédent/suivant. De plus, ScrollView contient un autre ViewFlipper contenant ImageView avec un détecteur de balayage vertical pour aller à ImageView précédent/suivant. Lorsque je remplace le ScrollView par un LinearLayout, les deux détecteurs de mouvements fonctionnent correctement, mais avec le ScrollView, aucun ne fonctionne (les écouteurs de mouvements ne sont même pas des déclencheurs). Pourquoi l'utilisation d'un ScrollView désactive-t-elle mes détecteurs de geste? Comment puis-je le faire fonctionner? 

Activité

public class ProduitHome extends Activity{  

    private Resources res;
    float density;

    private int position, parent_id;;
    private int num_products;

    private Produit produit;
    private ImageDownloader mImageLoader;   

    private ViewFlipper product_viewflipper;
    private ScrollView current_product_layout;
    Animation next_product_out, next_product_in, previous_product_in, previous_product_out;

    private GestureDetector galleryGestureDetector;
    private View.OnTouchListener galleryGestureListener;

    private GestureDetector productGestureDetector;
    private View.OnTouchListener productGestureListener;

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.produit_home);

        num_products = GlobalData.map_list_produits.get(parent_id).size();

        product_viewflipper = (ViewFlipper) findViewById(R.id.product_viewflipper);

        LayoutInflater inflater = getLayoutInflater();


        // Add num_products view to the viewflipper

        for(int i=0; i<num_products; i++){
            ScrollView product_detail = (ScrollView) inflater.inflate(R.layout.produit_detail, null);
            product_viewflipper.addView(product_detail);
        }


        // Set data and show current product

        current_product_layout = (ScrollView) product_viewflipper.getChildAt(position);
        product_viewflipper.setDisplayedChild(position);

        setProductData();


        // Set swipe listener to switch product

        productGestureDetector = new GestureDetector(new ProductGestureListener());
        productGestureListener = new View.OnTouchListener() 
        {
            public boolean onTouch(View v, MotionEvent event) 
            {
                if (productGestureDetector.onTouchEvent(event)) 
                {
                    return true;
                }
                else{
                    return false;
                }
            }
        };

        product_viewflipper.setOnTouchListener(productGestureListener);


        // Set switch product animation

        next_product_out = AnimationUtils.loadAnimation(this, R.anim.next_product_out);
        next_product_in = AnimationUtils.loadAnimation(this, R.anim.next_product_in);
        previous_product_in = AnimationUtils.loadAnimation(this, R.anim.previous_product_in);
        previous_product_out = AnimationUtils.loadAnimation(this, R.anim.previous_product_out);

    }


    class VerticalSwipeListener extends SimpleOnGestureListener {

        @Override
        public boolean onDown(MotionEvent e) {
            return true;
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

            final int SWIPE_MIN_DISTANCE = 80;
            final int SWIPE_MAX_OFF_PATH = 250;
            final int SWIPE_THRESHOLD_VELOCITY = 200; 

            try {
                if (Math.abs(e1.getX() - e2.getX()) > SWIPE_MAX_OFF_PATH)
                    return false;                

                ViewFlipper gallery = (ViewFlipper)current_product_layout.findViewById(R.id.product_gallery);

                if(e1.getY() - e2.getY() > SWIPE_MIN_DISTANCE && Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) {
                    gallery.showNext();                    
                }  else if (e2.getY() - e1.getY() > SWIPE_MIN_DISTANCE && Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) {
                    gallery.showPrevious();
                }
                ((RadioGroup)current_product_layout.findViewById(R.id.gallery_nav)).check(gallery.getDisplayedChild());
            } catch (Exception e) {
            }
            return false;
        }
    }


    class ProductGestureListener extends SimpleOnGestureListener {

        @Override
        public boolean onDown(MotionEvent e) {
            return true;
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

            final int SWIPE_MIN_DISTANCE = 120;
            final int SWIPE_MAX_OFF_PATH = 250;
            final int SWIPE_THRESHOLD_VELOCITY = 200; 

            if(!Utils.IsOnline(ProduitHome.this)){
                SRPDialogs.show(ProduitHome.this, SRPDialogs.NOT_CONNECTED);
            }
            else{

                try {
                    if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
                        return false;
                    if(e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {

                        // show next product

                    }  else if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {

                     // show previous product

                    }
                } catch (Exception e) {
                }
            }
            return false;
        }
    }

    public void setProductData(){

        produit = GlobalData.map_produits.get(GlobalData.map_list_produits.get(parent_id).get(position).id); 

        TextView name = (TextView) current_product_layout.findViewById(R.id.name);
        name.setText(produit.libelle);

        // Load gallery

        int nPics = produit.list_url_pic.size();

        if(nPics>0){

            ViewFlipper gallery = (ViewFlipper) current_product_layout.findViewById(R.id.product_gallery);
            gallery.removeAllViews();           

            mImageLoader = new ImageDownloader(res,
                    ((BitmapDrawable)res.getDrawable(R.drawable.default_row_pic)).getBitmap(), 1);          

            final ViewFlipper.LayoutParams params_vf = new ViewFlipper.LayoutParams(ViewFlipper.LayoutParams.FILL_PARENT, ViewFlipper.LayoutParams.FILL_PARENT);

            for(String url : produit.list_url_pic){

                // Add images to viewflipper
                ImageView imageView_p = new ImageView(this);
                imageView_p.setLayoutParams(params_vf);
                imageView_p.setScaleType(ImageView.ScaleType.CENTER_CROP);
                imageView_p.setTag(url);
                imageView_p.setImageResource(R.drawable.default_row_pic);
                mImageLoader.download(url, imageView_p);
                gallery.addView(imageView_p);
            } 

            // Swipe detector to switch picture in gallery

            galleryGestureDetector = new GestureDetector(new VerticalSwipeListener());
            galleryGestureListener = new View.OnTouchListener() 
            {
                public boolean onTouch(View v, MotionEvent event) 
                {
                    if (galleryGestureDetector.onTouchEvent(event)) 
                    {
                        return true;
                    }
                    else{
                        return false;
                    }
                }
            };

        }
    }
}

Mise en page parent

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:id="@+id/product_home" Android:layout_width="fill_parent"
    Android:layout_height="fill_parent" Android:orientation="vertical"
    Android:background="@color/grey_bg">

    <!-- more stuff -->

    <ViewFlipper Android:id="@+id/product_viewflipper"
        Android:layout_width="fill_parent" Android:layout_height="fill_parent"
        Android:layout_below="@id/header_logo" />

    <!-- more stuff -->

</RelativeLayout>

Disposition des enfants de ViewFlipper

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="fill_parent" Android:layout_height="fill_parent"
    Android:background="@color/grey_bg">

    <LinearLayout Android:layout_width="fill_parent"
        Android:layout_height="wrap_content" Android:orientation="vertical"
        Android:gravity="center_horizontal">

        <!-- more stuff -->

        <RelativeLayout Android:layout_below="@id/bg_content_top"
            Android:layout_above="@id/bg_content_bottom"
            Android:layout_width="300dp" Android:layout_height="fill_parent"
            Android:background="@drawable/bg_content"
            Android:paddingRight="3dp" Android:paddingLeft="3dp"
            Android:layout_centerHorizontal="true">

           <!-- more stuff -->

            <RelativeLayout Android:id="@+id/content"
                Android:layout_below="@id/title_container"
                Android:layout_above="@id/bg_content_bottom"
                Android:layout_width="fill_parent"
                Android:layout_height="wrap_content"
                Android:paddingLeft="7dp" Android:paddingRight="7dp"
                Android:paddingTop="10dp" Android:paddingBottom="10dp">               

                <ViewFlipper Android:id="@+id/product_gallery"
                    Android:clickable="true" Android:focusable="false"
                    Android:layout_width="100dp" Android:layout_height="150dp"
                    Android:layout_marginRight="10dp"
                    Android:layout_below="@id/title_container"
                    Android:layout_toRightOf="@id/gallery_nav" />

                <!-- more stuff -->

            </RelativeLayout>

        </RelativeLayout>

        <!-- more stuff -->

    </LinearLayout>

</ScrollView>
30
jul

Je devais ajouter

@Override
public boolean dispatchTouchEvent(MotionEvent ev){
    super.dispatchTouchEvent(ev);    
    return productGestureDetector.onTouchEvent(ev); 
}

dans mon activité.

56
jul

Ma réponse est la même que la dernière, sauf que je vais être plus explicite.

Changement

<ScrollView xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="fill_parent" Android:layout_height="fill_parent"
    Android:background="@color/grey_bg">

à 

<your.packagename.CustomScrollView ... etc>

Créer une classe

public class CustomScrollView extends ScrollView {
    private GestureDetector gestureDetector;
    View.OnTouchListener gestureListener;

    public CustomScrollView(Context context, AttributeSet attrs) {
          super(context, attrs);
          gestureDetector = new GestureDetector(new YScrollDetector());
          setFadingEdgeLength(0);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return super.onTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //Call super first because it does some hidden motion event handling
        boolean result = super.onInterceptTouchEvent(ev);
       //Now see if we are scrolling vertically with the custom gesture detector
       if (gestureDetector.onTouchEvent(ev)) {
            return result;
       } 
       //If not scrolling vertically (more y than x), don't Hijack the event.
        else {
            return false;
       }
    }

    // Return false if we're scrolling in the x direction  
    class YScrollDetector extends SimpleOnGestureListener {
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float     distanceY) {
        try {
            if (Math.abs(distanceY) > Math.abs(distanceX)) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            // nothing
        }
        return false;
    }
}

Ce code provient de la première réponse: HorizontalScrollView dans ScrollView Touch Handling (Alors, donnez-lui un vote si la réponse est utile). 

Si vous voulez obtenir la direction perpendiculaire alors changez

if (Math.abs(distanceY) > Math.abs(distanceX)) {

à

if (Math.abs(distanceY) < Math.abs(distanceX)) {

La CustomScrollView n'interceptera les balayages que dans un axe, que ce soit horizontalement ou verticalement, en fonction des 2 lignes de code ci-dessus. Puisqu'il n'intercepte que des balayages dans un axe, le reste des événements sera transmis à ses enfants. Vous pouvez maintenant gérer l'événement avec votre auditeur de mouvements/tactiles dans votre activité.

Vous devrez également remplacer les références/transformations par ScrollView par la nouvelle référence personnalisée (CustomScrollView).

11
triggs
parentScrollView.setOnTouchListener(new View.OnTouchListener() {

                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    return productGestureDetector.onTouchEvent(event);
                }
            });

définissez votre vue de défilement principale sur TouchListener et implémentez ce code pour vous. J'espère être utile pour vous.

0
Krunal Shah