kcire_eae
kcire_eae

Reputation: 349

How to get specific view clicked from RecyclerView with onInterceptTouchEvent?

I have been looking for, but still I have no found anything. I tried

View child = mirecycler.findChildViewUnder(e.getX(), and.getY());  

and it worked only to get the parent in my case the CardView container "row" , I need get specific TextView or ImageView.

I want to set onClick to ImageView without affect onClick of CardView.

public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
    View child = rv.findChildViewUnder(e.getX(), e.getY());
    if (child != null && mGestureDetector.onTouchEvent(e)) {
        Toast.makeText(MainActivity.this, "tocaste " + child, Toast.LENGTH_SHORT).show();
        return true;
    }
    return false;
}

@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}

@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}

Upvotes: 3

Views: 3281

Answers (4)

Nikola Despotoski
Nikola Despotoski

Reputation: 50538

public class RecyclerViewItemClickListener implements RecyclerView.OnItemTouchListener {

    private static final String TAG = RecyclerItemClickListener.class.getSimpleName();

    public void setListener(OnItemClickListener mListener) {
        this.mListener = mListener;
    }

    private OnItemClickListener mListener;

    public RecyclerViewItemClickListener(Context context) {
        this(context, null);
    }


    GestureDetector mGestureDetector;

    public RecyclerViewItemClickListener(Context context, OnItemClickListener listener) {
        mListener = listener;
        mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override public boolean onSingleTapUp(MotionEvent e) {
                return true;
            }
        });
    }

    @Override public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
        View childView = view.findChildViewUnder(e.getX(), e.getY());
        View exactView = findExactChild(childView, e.getX(), e.getY())
        if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
            mListener.onItemClick(view, exactView, view. view.getChildPosition(childView));
        }
        return false;
    }

    @Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    }
}

public View findExactChild(View childView, int x, int y){
            if(!(childView instanceof ViewGroup)) return child;
        ViewGroup group = (ViewGroup) childView;
            final int count = group.getChildCount();
            for (int i = count - 1; i >= 0; i--) {
                final View child = group.getChildAt(i);
                final float translationX = child.getTranslationX();
                final float translationY = child.getTranslationY();
                if (x >= child.getLeft() + translationX &&
                        x <= child.getRight() + translationX &&
                        y >= child.getTop() + translationY &&
                        y <= child.getBottom() + translationY) {
                    return child;
                }
            }
            return null;
        }
    }

As you said findViewUnder returns only the container. You need another pass to run to find out if x and y of the touch event are on any child of the container.

View exactView = findExactChild(childView, e.getX(), e.getY())

This will run another pass to find exact child.

Upvotes: 1

kcire_eae
kcire_eae

Reputation: 349

NBajanca's answer works well but within adapter is not possible access to UI elements of mainactivity, so a solution would be the use de an callback.

our code in adapter, then, would look like this.

public class NegociosAdapter extends RecyclerView.Adapter<NegociosAdapter.ViewHolder>  {

   interface MyCallback{
       void Presionofoto(int position);
   }
    private MyCallback callback;

 public NegociosAdapter(ArrayList<Negocios> negociosArrayList,MyCallback callback) {
        this.negociosArrayList = negociosArrayList;
        this.callback=callback;
    }
public static class ViewHolder extends RecyclerView.ViewHolder  {

        public ImageView foto;
        public TextView nombre;

        public ViewHolder(View itemView) {

            super(itemView);
            foto = (ImageView) itemView.findViewById(R.id.foto);
            nombre = (TextView) itemView.findViewById(R.id.txtnombre);

        }
    }
 public void onBindViewHolder(ViewHolder viewHolder, final int position) {

        Negocios rowNegocio = negociosArrayList.get(position);
        viewHolder.nombre.setText(rowNegocio.getNombre());
         viewHolder.foto.setImageBitmap(rowNegocio.getImagen());
         viewHolder.foto.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                callback.Presionofoto(position);
               //When click here method "Presionofoto" be running on mainactivity

            }
        });
}
 @Override
    public int getItemCount() {
        return negociosArrayList.size();
    }
}

in mainactivity this:

public class MainActivity extends AppCompatActivity implements NegociosAdapter.MyCallback{

 NegociosAdapter adapter = new NegociosAdapter(datosResult,this);
            recycler.setAdapter(adapter);

//we have that implement method abstract "Presionofoto"

@Override
public void Presionofoto(final int position) {
//here we can access to UI, this 
 Toast.makeText(getBaseContext(), "Hello From MainActivity", Toast.LENGTH_SHORT).show();
}
}

Upvotes: 0

NBajanca
NBajanca

Reputation: 3730

If you have to use onInterceptTouchEvent I'm not sure I can help you. I only use it when I don't have to manage specific clicks, but maybe my solution can work together with it (Although I didn't tried it)

I will provide a solution for you to detect the point where the click was made, using a customer adapter:

public class SomeRecyclerAdapter extends RecyclerView.Adapter<SomeRecyclerAdapter.SomeListViewHolder> {

    ....

    @Override
public void onBindViewHolder(final SomeViewHolder holder, final int position) {
    SomeItem item = someList.get(position);

    holder.itemView.setTag(item);
    holder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            SomeListItem item = (SomeListItem) holder.itemView.getTag();
            Toast.makeText(context, "Recycle Click " + item.getName(), Toast.LENGTH_SHORT).show();
        }
    });

    holder.vImage.setTag(item);
    holder.vImage
            .setOnClickListener(new CompoundButton.OnClickListener(){

                @Override
                public void onClick(View v) {
                    SomeListItem item = (SomeItem) holder.vImage.getTag();
                    Toast.makeText(context, item.getName() + " Image Clicked", Toast.LENGTH_SHORT).show();
                }
            });
    ....

    public static class SomeListViewHolder extends RecyclerView.ViewHolder{
        protected ImageView vImage;

        public SomeListViewHolder(View itemView) {
            super(itemView);
            vImage = (ImageView) itemView.findViewById(R.id.tem_some_list);
        }

    }

}

My example is how to handle a click in a Recycler Item Image. I've tried this solution and it works, this meaning that if you click in the item (except the Image) it will print "Recycle Click ...", and if you click on the image it will print " .... Image Clicked".

Maybe (not tested, but you can do it and let us know) you can use the onInterceptTouchEvent for the CardView, and the onClickListener for the image.

I hope this is what you were looking for and if you find a better solution let me know.

Upvotes: 1

eclipse1203
eclipse1203

Reputation: 635

So one solution would be to use an onTouchListener. What you can do is set an onTouchListener on you child ImageView and return true so that the touch doesn't get passed up to the view parent.

Upvotes: 0

Related Questions