0

I have a recyclerview displaying a simple list of items. On long pressing a particular item, a piece of audio associated with this item (stored in the assets folder) is played. On releasing, the audio is paused, and can be returned to at the paused point by long pressing. To that end, I implemented an onTouchListener in the ViewHolder of the RecyclerViews Adapter and created an interface that I call in onBindViewHolder. It works but when I scroll through the items the audio plays automatically and loops endlessly. I can still long press an item and hear its audio playing on top of the automatically looping audio.

I'm not sure where exactly the problem is. I'm not sure if it relates to the way items are recycled in RecyclerView, given it's only something thats an issue when I scroll? I tried to add an onItemTouchListener in the the corresponding fragment and do the work over there but had the same issue.

Or maybe the problem is in how I've set up my onTouchListener and interface? I'm not sure I fully understand what should be happening in onInterceptTouchEvent.

Or does the issue maybe lie in how I've set up my mediaplayer in the onTouchEvent?

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

List<Clap> mItems;
private Context context;
ItemTouchListener itlistener;
GestureDetector mGestureDetector;


public interface ItemTouchListener {
    boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e, List<Clap> list);
    void onTouchEvent(List<Clap> list, View view, int position, MotionEvent me);
    void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
}

@Override
public long getItemId(int position) {
    return position;
}

public ClapsAdapter(Context mContext, List<Clap> myClap) { 
    super();
    this.mItems = myClap;
    this.context = mContext;
    setHasStableIds(true);


    mItems = new ArrayList<Clap>();
    Clap mClaps = new Clap();
    mClaps.setCName("Hearty Clap");
    mClaps.setImageId(R.drawable.clapping1);
    mClaps.setAudio("applause-01.mp3");
    mItems.add(mClaps);

    mClaps = new Clap();
    mClaps.setCName("Business Clap");
    mClaps.setImageId(R.drawable.clapping2);
    mClaps.setAudio("fake_applause.mp3");
    mItems.add(mClaps);

    mClaps = new Clap();
    mClaps.setCName("Green Clap");
    mClaps.setImageId(R.drawable.clap3);
    mClaps.setAudio("laugh_and_applause.mp3");
    mItems.add(mClaps);

    mClaps = new Clap();
    mClaps.setCName("Slow Clap");
    mClaps.setImageId(R.mipmap.slow_clap);
    mClaps.setAudio("fake_applause.mp3");
    mItems.add(mClaps);

    mClaps = new Clap();
    mClaps.setCName("Emoji Clap");
    mClaps.setImageId(R.mipmap.clap_emoji);
    mClaps.setAudio("light_applause.mp3");
    mItems.add(mClaps);

    mClaps = new Clap();
    mClaps.setCName("Slow Clap");
    mClaps.setImageId(R.mipmap.slow_clap);
    mClaps.setAudio("laughter-1.wav");
    mItems.add(mClaps);

    mClaps = new Clap();
    mClaps.setCName("Emoji Clap");
    mClaps.setImageId(R.mipmap.clap_emoji);
    mClaps.setAudio("laughter-2.mp3");
    mItems.add(mClaps);
   // this.itlistener = itl;
}

public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnTouchListener{


    public  TextView title;
    public ImageView imageView;
    private ItemTouchListener touchListener;

    final MediaPlayer mp = new MediaPlayer();
    private Context mContext;
    List<Clap> mItems;

   public ViewHolder(View itemView) {
        super(itemView);

       imageView = (ImageView) itemView.findViewById(R.id.icon);
       title = (TextView) itemView.findViewById(R.id.description);
       itemView.setTag(itemView);
       itemView.setClickable(true);
       itemView.setOnTouchListener(this);


   }

    public void setTouchListener (ItemTouchListener itemTouchListener){
        this.touchListener = itemTouchListener;
    }


    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (touchListener != null) {
            touchListener.onTouchEvent(mItems, v, getAdapterPosition(), event);
        } return true;
    }




}


@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
    super.onAttachedToRecyclerView(recyclerView);
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int i) {
    //Inflate the layout, initialize the View Holder
    View v = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.item_clap, parent, false);
    return new ViewHolder(v);

}


@Override
public void onBindViewHolder(ViewHolder holder, int position) {

    Clap clap = mItems.get(position);
    holder.title.setText(mItems.get(position).getCName());
    holder.imageView.setImageResource(mItems.get(position).getImageId());


    final MediaPlayer mp = new MediaPlayer();

    holder.setTouchListener(new ItemTouchListener() {
        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e, List<Clap> list) {


            View childView = rv.findChildViewUnder(e.getX(), e.getY());
            if (childView != null && this != null && mGestureDetector.onTouchEvent(e)) {
                this.onTouchEvent(list, rv,rv.getChildAdapterPosition(childView), e);
            }

            return true;
        }

        @Override
        public void onTouchEvent(List<Clap> list, View view, int position, MotionEvent me) {
          // Toast.makeText(context, "TOUCH ME!!!",
             //       Toast.LENGTH_SHORT).show();

            switch (me.getAction()) {

                case MotionEvent.ACTION_DOWN: {

                    if (mp.isPlaying()) {
                        mp.stop();
                        mp.release();
                    }

                    try {
                        mp.reset();
                        AssetFileDescriptor afd;
                        afd = view.getContext().getAssets().openFd(mItems.get(position).getAudio());
                        mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
                        mp.prepare();

                    } catch (IllegalStateException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    mp.setLooping(true);

                    mp.start();
                }

                break;
                case MotionEvent.ACTION_UP: {
                    mp.pause();
                    Toast.makeText(context, mItems.get(position).getCName(),
                            Toast.LENGTH_SHORT).show();

                }
                break;

            }

        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

        }
    });


}

@Override
public int getItemCount() {
    //returns the number of elements the RecyclerView will display
    //return items.size();
    return (null != mItems ? mItems.size() : 0);
}

public void insert(int position, Clap clap) {
    mItems.add(position, clap);
    notifyItemInserted(position);
}


public void remove(Clap clap) {
    int position = mItems.indexOf(clap);
    mItems.remove(position);
    notifyItemRemoved(position);
}

Any help on the matter would be greatly appreciated. I feel like I've spent more time stuck on this than I should!

Andrei
  • 42,814
  • 35
  • 154
  • 218

2 Answers2

0

you should read about managing touch events in view group http://developer.android.com/training/gestures/viewgroup.html

wojciech_maciejewski
  • 1,277
  • 1
  • 12
  • 28
  • Thanks for replying and sorry I have not responded sooner. I've been trying to get my head around how touch events are propagated - it seems complex. I don't really understand what logic I should be writing in onInterceptTouchEvent and what should be in onTouchEvent. I've tried a few combinations of things that I've found online but the behaviour is still the same. Works as expected (audio plays during ACTION_DOWN and pauses on ACTION_UP) but when I scroll the associated audio of the last touched item plays automatically and on a loop. Any other advice would be appreciated. – Tadhg Ó Cuirrín Apr 22 '16 at 13:47
0

You can attach GestureDetector to your onTouchEvent() and then write your logic in onLongPress().

GestureDetector gestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
        public void onLongPress(MotionEvent e) {
            //do something here
        }
    });

    public boolean onTouchEvent(MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
    };
Amit Tiwari
  • 3,684
  • 6
  • 33
  • 75
  • Thanks for replying and sorry I have not responded sooner. Tried your approach, but wasn't sure how to reference the position and contents of the respective items? – Tadhg Ó Cuirrín Apr 22 '16 at 13:52