Pranoy C Pranoy C - 6 months ago 92
Android Question

android listview inside cardview onTouch listener conflict / sensitivity

Here's a test project which shows the issue dropbox link:
https://www.dropbox.com/sh/8s3v9ydcj6jvpl8/AACZ2VRP2N9R1ec7pxrsAn0ga?dl=0

This is a continuation of the question I had here which was answered but now I am asking about the sensitivity/conflict of onTouch:
Android CardView with ListView inside - onTouchListener on CardView not working

I have a cardview with a listview inside. I will need the scroll and click item in list view to work too and I want to be able to move the entire cardview using the ontouchlistener too.
I have set a onTouchListener on the cardview but it doesn't work properly as the listview scroll is conflicting with the cardview movement.

I have been able to get similar thing to work on iOS perfectly so should be doable on android too.

Code:

Put this in build.gradle:

compile 'com.android.support:cardview-v7:22.0+'


MainActivity:

import android.animation.Animator;
import android.graphics.PointF;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.CardView;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

private ListView mylistview;
private CustomCardView mycardview;
PointF lastLocation;
static final int REFRESH_RATE = 10; //or 20, or 5, or 30, whatever works best
int counter = 0;
PointF viewCenter;
PointF cardOriginalLocation;
boolean checkIfPanning;
RelativeLayout layout;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

layout = (RelativeLayout)findViewById(R.id.layout);
mycardview = (CustomCardView)findViewById(R.id.mycardview);
mylistview = (ListView) findViewById(R.id.myListView);

List<String> your_array_list = new ArrayList<String>();
your_array_list.add("foo");
your_array_list.add("bar");
your_array_list.add("foo");
your_array_list.add("bar");
your_array_list.add("foo");
your_array_list.add("bar");
your_array_list.add("foo");
your_array_list.add("bar");
your_array_list.add("foo");
your_array_list.add("bar");
your_array_list.add("foo");
your_array_list.add("bar");
your_array_list.add("foo");
your_array_list.add("bar");


ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,your_array_list );

mylistview.setAdapter(arrayAdapter);
mycardview.setCardElevation(20);



mycardview.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mycardview.getViewTreeObserver().removeOnGlobalLayoutListener(this);
cardOriginalLocation = new PointF(mycardview.getX(), mycardview.getY());
viewCenter = new PointF(layout.getWidth() / 2, layout.getHeight() / 2);
}
});


View.OnTouchListener onTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {


if(mylistview!=null){
//Route all touch event to listview without logic
System.out.println("AAAAAA Touched list");
mylistview.onTouchEvent(event);

}

if (event.getAction()==MotionEvent.ACTION_DOWN){
System.out.println("ACTION_DOWN");

checkIfPanning=true;

lastLocation = new PointF(event.getRawX(),event.getRawY());
return true;
} else if (checkIfPanning && event.getAction()==MotionEvent.ACTION_MOVE/* && (getFrontCard().getX() - (lastLocation.x - event.getRawX()))<=10*/){
System.out.println("ACTION_MOVE");

counter += 1;

if((REFRESH_RATE % counter) == 0) {


float newx = mycardview.getX() - (lastLocation.x - event.getRawX());



float newy = mycardview.getY() - (lastLocation.y - event.getRawY());

mycardview.setX(newx);
mycardview.setY(newy);

lastLocation.set(event.getRawX(), event.getRawY());


float completedPercent = Math.min(((mycardview.getX() + mycardview.getWidth() / 2) - viewCenter.x) / viewCenter.x, 1);
float angle = (completedPercent*15);
mycardview.setRotation(angle);
}
counter=0;
return true;
} else if (event.getAction()==MotionEvent.ACTION_UP){


System.out.println("ACTION_UP");

checkIfPanning=false;

float displaceentFromCenterX = ((mycardview.getX()+mycardview.getWidth()/2) - viewCenter.x);

if (Math.abs(displaceentFromCenterX)>225){
float toMove;
if (displaceentFromCenterX>0){
toMove = layout.getWidth()+mycardview.getHeight();
} else {
toMove = -mycardview.getWidth()-mycardview.getHeight();
}

mycardview.animate().rotationBy(30).translationX(toMove).setDuration(100).setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
System.out.println("onAnimationStart");
}

@Override
public void onAnimationEnd(Animator animation) {
animation.removeListener(this);
System.out.println("DONNNNNE");
mycardview.setX(cardOriginalLocation.x);
mycardview.setY(cardOriginalLocation.y);
mycardview.setRotation(0);

}

@Override
public void onAnimationCancel(Animator animation) {

}

@Override
public void onAnimationRepeat(Animator animation) {

}
});
} else {


mycardview.animate().rotation(0).translationX(0).translationY(0).setDuration(100).setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {

}

@Override
public void onAnimationEnd(Animator animation) {

}

@Override
public void onAnimationCancel(Animator animation) {

}

@Override
public void onAnimationRepeat(Animator animation) {

}
});
}


return true;
} else {
checkIfPanning=false;
if(mylistview!=null){
//Route all touch event to listview without logic
System.out.println("BBBBB Touched list");
mylistview.onTouchEvent(event);
return true;
}
}
return true;
}


};

mycardview.setOnTouchListener(onTouchListener);

}
}


Both the cardview onTouch listener and the listview do detect the touch now. However, when I try to move the cardview, the listview keeps trying to scroll around. And when I try to scroll on the list, the card moves intead.

I understand why this is happening. Basically the onTouch listener seems to be conflicting the cardview movement with listview scroll but I am not sure how to figure out in code on whether I am trying to scroll or move the cardview around.

XML for MainActivity:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.pranapps.testontouch.MainActivity"
android:id="@+id/layout"
>



<com.pranapps.testontouch.CustomCardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="200dp"
android:id="@+id/mycardview"
card_view:cardUseCompatPadding="true">

<ListView
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:id="@+id/myListView"
android:dividerHeight="0.2dp"
android:overScrollMode="always"
android:smoothScrollbar="true"
android:groupIndicator="@null"
></ListView>



</com.pranapps.testontouch.CustomCardView>

</RelativeLayout>


CustomCardView code:

package com.pranapps.testontouch;

import android.content.Context;
import android.support.v7.widget.CardView;
import android.util.AttributeSet;
import android.view.MotionEvent;

/**
* Created by pranoychowdhury on 5/9/16.
*/
public class CustomCardView extends CardView {
public CustomCardView(Context context) {
super(context);
}

public CustomCardView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public CustomCardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
/*
* This method determines whether we want to intercept the motion.
* If we return true, onTouchEvent will be called.
*/
System.out.println("Touched custom from card");
return true;
}
}


Please help with suggestions to try!
Thanks!

Edit: here's a video of how it works on iOS. I can scroll on the listview. Panning left or right makes the card move.
https://www.dropbox.com/s/5dm52vtjb1xgcl5/iOS.mov?dl=0

The cardview won't be scrolling, only the content inside the cardview (which is the listview) will scroll. The cardview can only move around by tracking the fingers. Imagine tinder where cards can be swipped and within the card, you can scroll.

Answer

I was able to solve this myself after 2 weeks of experimenting.

I removed the onInterceptTouchEvent and ontouch from the cardview. Instead, create a custom OnTouchListener and attach it to the listview itself. Then in the custom OnTouchListener, try to figure out the angle at which the touch is going. if angle within a specific range, then up or down scroll. other wise left/right swipe. The angle I am still trying to calibrate to figure out which one is the best for usability wise but this seems to work decently fine.

I got a bit of help from this link for calculating angles: How to detect swipe direction between left/right and up/down

Solution:

MainActivity:

package com.pranapps.testontouch;

import android.animation.Animator;
import android.graphics.PointF;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.CardView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private ListView mylistview;
    private CustomCardView mycardview;
    PointF lastLocation;
    static final int REFRESH_RATE = 10; //or 20, or 5, or 30, whatever works best
    int counter = 0;
    PointF viewCenter;
    PointF cardOriginalLocation;
    boolean checkIfPanning;
    RelativeLayout layout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        layout = (RelativeLayout)findViewById(R.id.layout);
        mycardview = (CustomCardView)findViewById(R.id.mycardview);
        mylistview = (ListView) findViewById(R.id.myListView);

        List<String> your_array_list = new ArrayList<String>();
        your_array_list.add("foo");
        your_array_list.add("bar");
        your_array_list.add("foo");
        your_array_list.add("bar");
        your_array_list.add("foo");
        your_array_list.add("bar");
        your_array_list.add("foo");
        your_array_list.add("bar");
        your_array_list.add("foo");
        your_array_list.add("bar");
        your_array_list.add("foo");
        your_array_list.add("bar");
        your_array_list.add("foo");
        your_array_list.add("bar");


        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,your_array_list );

        mylistview.setAdapter(arrayAdapter);
        mycardview.setCardElevation(20);



        mycardview.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                mycardview.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                cardOriginalLocation = new PointF(mycardview.getX(), mycardview.getY());
                viewCenter = new PointF(layout.getWidth() / 2, layout.getHeight() / 2);
            }
        });


        mylistview.setOnTouchListener(new OnSwipeTouchListener(){

            @Override
            public void onSwipeStart(MotionEvent motionEvent) {
                super.onSwipeStart(motionEvent);
                System.out.println("Swipe Start");
                checkIfPanning=true;
                //if (lastLocation==null) {
                    lastLocation = new PointF(motionEvent.getRawX(),motionEvent.getRawY());
                //}
            }

            @Override
            public void onSwipeEnd(MotionEvent motionEvent) {
                super.onSwipeEnd(motionEvent);

                System.out.println("Swipe End");

                checkIfPanning=false;

                float displaceentFromCenterX = ((mycardview.getX()+mycardview.getWidth()/2) - viewCenter.x);

                if (Math.abs(displaceentFromCenterX)>225){
                    float toMove;
                    if (displaceentFromCenterX>0){
                        toMove = layout.getWidth()+mycardview.getHeight();
                    } else {
                        toMove = -mycardview.getWidth()-mycardview.getHeight();
                    }

                    mycardview.animate().rotationBy(30).translationX(toMove).setDuration(100).setListener(new Animator.AnimatorListener() {
                        @Override
                        public void onAnimationStart(Animator animation) {
                            System.out.println("onAnimationStart");
                        }

                        @Override
                        public void onAnimationEnd(Animator animation) {
                            animation.removeListener(this);
                            System.out.println("DONNNNNE");
                            mycardview.setX(cardOriginalLocation.x);
                            mycardview.setY(cardOriginalLocation.y);
                            mycardview.setRotation(0);

                        }

                        @Override
                        public void onAnimationCancel(Animator animation) {

                        }

                        @Override
                        public void onAnimationRepeat(Animator animation) {

                        }
                    });
                } else {


                    mycardview.animate().rotation(0).translationX(0).translationY(0).setDuration(100).setListener(new Animator.AnimatorListener() {
                        @Override
                        public void onAnimationStart(Animator animation) {

                        }

                        @Override
                        public void onAnimationEnd(Animator animation) {

                        }

                        @Override
                        public void onAnimationCancel(Animator animation) {

                        }

                        @Override
                        public void onAnimationRepeat(Animator animation) {

                        }
                    });
                }
                lastLocation = null;
            }

            @Override
            public void onSwipe(MotionEvent motionEvent) {
                super.onSwipe(motionEvent);
                System.out.println("Swiping");
                counter += 1;

                if((REFRESH_RATE % counter) == 0) {


                    float newx = mycardview.getX() - (lastLocation.x - motionEvent.getRawX());



                    float newy = mycardview.getY() - (lastLocation.y - motionEvent.getRawY());

                    mycardview.setX(newx);
                    mycardview.setY(newy);

                    lastLocation.set(motionEvent.getRawX(), motionEvent.getRawY());


                    float completedPercent = Math.min(((mycardview.getX() + mycardview.getWidth() / 2) - viewCenter.x) / viewCenter.x, 1);
                    float angle =  (completedPercent*15);
                    mycardview.setRotation(angle);
                }
                counter=0;
            }

            @Override
            public void onScroll(MotionEvent motionEvent) {
                super.onSwipe(motionEvent);
                mylistview.onTouchEvent(motionEvent);
            }
        });

        View.OnTouchListener onTouchListener = new View.OnTouchListener() {



            @Override
            public boolean onTouch(View v, MotionEvent event) {

                System.out.println("ONTOUCHHHH");

                if (event.getAction()==MotionEvent.ACTION_DOWN){
                    System.out.println("ACTION_DOWN");

                    checkIfPanning=true;

                    lastLocation = new PointF(event.getRawX(),event.getRawY());
                    return true;
                } else if (checkIfPanning && event.getAction()==MotionEvent.ACTION_MOVE){
                    System.out.println("ACTION_MOVE");

                    counter += 1;

                    if((REFRESH_RATE % counter) == 0) {


                        float newx = mycardview.getX() - (lastLocation.x - event.getRawX());



                        float newy = mycardview.getY() - (lastLocation.y - event.getRawY());

                        mycardview.setX(newx);
                        mycardview.setY(newy);

                        lastLocation.set(event.getRawX(), event.getRawY());


                        float completedPercent = Math.min(((mycardview.getX() + mycardview.getWidth() / 2) - viewCenter.x) / viewCenter.x, 1);
                        float angle =  (completedPercent*15);
                        mycardview.setRotation(angle);
                    }
                    counter=0;
                    return true;
                } else if (event.getAction()==MotionEvent.ACTION_UP){


                    System.out.println("ACTION_UP");

                    checkIfPanning=false;

                    float displaceentFromCenterX = ((mycardview.getX()+mycardview.getWidth()/2) - viewCenter.x);

                    if (Math.abs(displaceentFromCenterX)>225){
                        float toMove;
                        if (displaceentFromCenterX>0){
                            toMove = layout.getWidth()+mycardview.getHeight();
                        } else {
                            toMove = -mycardview.getWidth()-mycardview.getHeight();
                        }

                        mycardview.animate().rotationBy(30).translationX(toMove).setDuration(100).setListener(new Animator.AnimatorListener() {
                            @Override
                            public void onAnimationStart(Animator animation) {
                                System.out.println("onAnimationStart");
                            }

                            @Override
                            public void onAnimationEnd(Animator animation) {
                                animation.removeListener(this);
                                System.out.println("DONNNNNE");
                                mycardview.setX(cardOriginalLocation.x);
                                mycardview.setY(cardOriginalLocation.y);
                                mycardview.setRotation(0);

                            }

                            @Override
                            public void onAnimationCancel(Animator animation) {

                            }

                            @Override
                            public void onAnimationRepeat(Animator animation) {

                            }
                        });
                    } else {


                        mycardview.animate().rotation(0).translationX(0).translationY(0).setDuration(100).setListener(new Animator.AnimatorListener() {
                            @Override
                            public void onAnimationStart(Animator animation) {

                            }

                            @Override
                            public void onAnimationEnd(Animator animation) {

                            }

                            @Override
                            public void onAnimationCancel(Animator animation) {

                            }

                            @Override
                            public void onAnimationRepeat(Animator animation) {

                            }
                        });
                    }


                    return true;
                }
                return true;
            }


        };

        mycardview.setOnTouchListener(onTouchListener);



    }
}

OnSwipeTouchListener:

package com.pranapps.testontouch;

import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.View.OnTouchListener;


public class OnSwipeTouchListener implements OnTouchListener{


    boolean checkIfStartedTouch;
    boolean checkIfSwiping;
    boolean checkIfScrolling;
    private final GestureDetector gestureDetector = new GestureDetector(new GestureListener());
    private MotionEvent currentEvent;

    @Override
    public boolean onTouch(View v, MotionEvent event) {

        currentEvent = event;
        if (event.getAction()==MotionEvent.ACTION_DOWN){
            checkIfStartedTouch = true;
        } else if (event.getAction()==MotionEvent.ACTION_UP){
            if (checkIfSwiping){
                onSwipeEnd(event);
            }
            checkIfStartedTouch = false;
            checkIfScrolling = false;
            checkIfSwiping = false;
        } else {
            if (checkIfSwiping){
                //System.out.println("swipe2");
                onSwipe(event);
                return false;
            } if (checkIfScrolling){
                //System.out.println("scroll2");
                onScroll(event);
                return false;
            }
        }

        return gestureDetector.onTouchEvent(event);
    }

    public void onSwipe(MotionEvent motionEvent) {}
    public void onScroll(MotionEvent motionEvent) {}
    public void onSwipeEnd(MotionEvent motionEvent) {}
    public void onSwipeStart(MotionEvent motionEvent) {}

    public enum Direction {
        up,
        down,
        left,
        right;

        /**
         * Returns a direction given an angle.
         * Directions are defined as follows:
         * <p/>
         * 0 is on right side middle (east direction)
         * angle is anticlockwise, 90 is at north pole
         *
         * @param angle an angle from 0 to 360 - e
         * @return the direction of an angle
         */
        public static Direction get(double angle) {
            System.out.println("Angle: "+angle);
            if (inRange(angle, 50, 130)) {
                System.out.println("UPPPPPPPPPP");
                return Direction.up;
            } else if (inRange(angle, 0, 50) || inRange(angle, 310, 360)) {
                System.out.println("RIGHT");
                return Direction.right;
            } else if (inRange(angle, 240, 310)) {
                System.out.println("DOWN");
                return Direction.down;
            } else {
                System.out.println("LEFTT");
                return Direction.left;
            }

        }//i think I have finally figured out an android coding UI problem after 3 weeks. xml files might work for web development but designing decent UIs on a dedicated device using xml files is a nightmare. The fragmentation of device different resolutions makes things even harder.

        /**
         * @param angle an angle
         * @param init  the initial bound
         * @param end   the final bound
         * @return returns true if the given angle is in the interval [init, end).
         */
        private static boolean inRange(double angle, float init, float end) {
            return (angle >= init) && (angle < end);
        }
    }

    private final class GestureListener extends SimpleOnGestureListener {

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

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



            System.out.println("flingggg");
            float x1 = e1.getX();
            float y1 = e1.getY();

            float x2 = e2.getX();
            float y2 = e2.getY();

            Direction direction = getDirection(x1, y1, x2, y2);
            return onSwipe(direction);
        }*/

        @Override
        public boolean onScroll(MotionEvent e1,
                                MotionEvent e2,
                                float distanceX,
                                float distanceY) {

            // Grab two events located on the plane at e1=(x1, y1) and e2=(x2, y2)
            // Let e1 be the initial event
            // e2 can be located at 4 different positions, consider the following diagram
            // (Assume that lines are separated by 90 degrees.)
            //
            //
            //         \ A  /
            //          \  /
            //       D   e1   B
            //          /  \
            //         / C  \
            //
            // So if (x2,y2) falls in region:
            //  A => it's an UP swipe
            //  B => it's a RIGHT swipe
            //  C => it's a DOWN swipe
            //  D => it's a LEFT swipe
            //

            System.out.println("onscrollllll");
            float x1 = e1.getX();
            float y1 = e1.getY();

            float x2 = e2.getX();
            float y2 = e2.getY();

            Direction direction = getDirection(x1, y1, x2, y2);
            return onSwipe(direction);


        }

        public boolean onSwipe(Direction direction) {

            if (direction == Direction.left || direction == Direction.right) {
                System.out.println("swipe");
                if (!checkIfSwiping) {
                    checkIfSwiping = true;
                    onSwipeStart(currentEvent);
                }
                return false;
            }
            else {
                System.out.println("scroll");
                checkIfScrolling=true;
                return true;
            }
        }

        /**
         * Given two points in the plane p1=(x1, x2) and p2=(y1, y1), this method
         * returns the direction that an arrow pointing from p1 to p2 would have.
         *
         * @param x1 the x position of the first point
         * @param y1 the y position of the first point
         * @param x2 the x position of the second point
         * @param y2 the y position of the second point
         * @return the direction
         */
        public Direction getDirection(float x1, float y1, float x2, float y2) {
            double angle = getAngle(x1, y1, x2, y2);
            return Direction.get(angle);
        }

        /**
         * Finds the angle between two points in the plane (x1,y1) and (x2, y2)
         * The angle is measured with 0/360 being the X-axis to the right, angles
         * increase counter clockwise.
         *
         * @param x1 the x position of the first point
         * @param y1 the y position of the first point
         * @param x2 the x position of the second point
         * @param y2 the y position of the second point
         * @return the angle between two points
         */
        public double getAngle(float x1, float y1, float x2, float y2) {

            double rad = Math.atan2(y1 - y2, x2 - x1) + Math.PI;
            return (rad * 180 / Math.PI + 180) % 360;
        }



    }
}

CustomCardView:

package com.pranapps.testontouch;

import android.content.Context;
import android.support.v7.widget.CardView;
import android.util.AttributeSet;
import android.view.MotionEvent;

/**
 * Created by pranoychowdhury on 5/9/16.
 */
public class CustomCardView extends CardView {
    public CustomCardView(Context context) {
        super(context);
    }

    public CustomCardView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomCardView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        //System.out.println("Touched custom from card");
        return false;
    }


}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.pranapps.testontouch.MainActivity"
    android:id="@+id/layout"
    >



<com.pranapps.testontouch.CustomCardView
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_gravity="center"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:id="@+id/mycardview"
    card_view:cardUseCompatPadding="true">

    <ListView
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:id="@+id/myListView"
        android:dividerHeight="0.2dp"
        android:overScrollMode="always"
        android:smoothScrollbar="true"
        android:groupIndicator="@null"
        ></ListView>



</com.pranapps.testontouch.CustomCardView>

</RelativeLayout>