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.