0

My app recyclerview having audio, imageview, textview as items. When scrolling the recyclerview, it is scrolled properly without any issue if item is not updated. But scrolling the recyclerview is not smooth while playing a audio item of recyclerview, it behaves as below attached gif image. While playing an audio, i am updating audio current playing time in textview. I am not sure what is issue is here. Kindly help me. Thanks.

Here is my code.

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

ArrayList<SessionHistoryMesssage> messageArrayList;

MediaPlayer mPlayer = null;

LocalService mService;
boolean mBound = false;

private RelativeLayout.LayoutParams params;
int currentlyPlayingPosition = -1;
public String mBasePath = "", imageDirectory = "", mSessionId = "", audioDirectory = "";
private ProgressDialog progressDialog;

public RetrofitRestAdapter retrofitRestAdapter;
private MessageAdapter messageListAdapter;
private SessionHistoryResponse sessionHistoryResponse;
public Context mContext;
private int mSelectionPosition = -1;
private int num = 0;
public boolean isSessionStarted=false;

public static Handler mHandler;
public int playposition = -1, sessionstartPosition = -1,     alreadystartPosition = -1, sessionendPosition = -1;

public MessageAdapter(Context mContext
        , ArrayList<SessionHistoryMesssage> messageArrayList, String mBasePath) {

    this.mContext = mContext;
    this.messageArrayList = messageArrayList;
    this.mBasePath = mBasePath;

    mHandler = new Handler();

    Intent intent = new Intent(mContext, LocalService.class);
    this.mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

}

// 1. Type      a) SEND  b) RECEIVE
// 2. Message   a) AUDIO b) IMAGE

@Override
public int getItemViewType(int position) {
    // Just as an example, return 0 or 2 depending on position
    // Note that unlike in ListView adapters, types don't have to be contiguous

    Log.i("ViewType", messageArrayList.get(position).getmMediaType() + "");
    Log.i("sent by", messageArrayList.get(position).getmSentBy() + "");

    return Integer.parseInt(messageArrayList.get(position).getmSentBy());
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    VHItemAudio dataObjectHolder = null;

    if (Integer.parseInt(Utility.readPatientId(mContext)) == viewType) {

        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.layout_a_conversation_item, parent, false);
        dataObjectHolder = new VHItemAudio(view);

        return dataObjectHolder;

    } else {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.layout_b_conversation_item, parent, false);

        dataObjectHolder = new VHItemAudio(view);

        return dataObjectHolder;

    }
}

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

    final VHItemAudio vhItemHolder = (VHItemAudio) holder;

    vhItemHolder.bind(messageArrayList.get(position), position);
   }

public class VHItemAudio extends RecyclerView.ViewHolder {

    ImageView playPauseImageView, imgDownloadAudio, imgDownloadImage;
    TextView txtImageSendTime, runningTimerTextView, txtAudioSendTime, txtSessionStart;
    SeekBar seekBar;
    ImageView imageView;
    RelativeLayout layoutAudio, layoutImage, layoutdownloadImage, layoutSessionStart;
    ProgressBar progressImageDownload, progressAudioDownload;


    private ImageView imageShareImageView,recordAudioImageView,imgEndSession;

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

        playPauseImageView = (ImageView) itemView.findViewById(R.id.playPauseImageView);
        txtImageSendTime = (TextView) itemView.findViewById(R.id.txtImageSendTime);
        runningTimerTextView = (TextView) itemView.findViewById(R.id.runningTimerTextView);
        seekBar = (SeekBar) itemView.findViewById(R.id.seekBar);
        imgDownloadAudio = (ImageView) itemView.findViewById(R.id.imgDownloadAudio);
        imgDownloadImage = (ImageView) itemView.findViewById(R.id.imgDownloadImage);
        imageView = (ImageView) itemView.findViewById(R.id.imageView);
        txtAudioSendTime = (TextView) itemView.findViewById(R.id.txtAudioSendTime);
        layoutAudio = (RelativeLayout) itemView.findViewById(R.id.layoutAudio);
        layoutImage = (RelativeLayout) itemView.findViewById(R.id.layoutImage);
        layoutdownloadImage = (RelativeLayout) itemView.findViewById(R.id.layoutdownloadImage);
        progressImageDownload = (ProgressBar) itemView.findViewById(R.id.progressImageDownload);
        progressAudioDownload = (ProgressBar) itemView.findViewById(R.id.progressAudioDownload);
        layoutSessionStart = (RelativeLayout) itemView.findViewById(R.id.layoutSessionStart);
        txtSessionStart = (TextView) itemView.findViewById(R.id.txtSessionStart);

        Log.i("", "Adding Listener");


        imageShareImageView= (ImageView) ((PatientActivity)PatientActivity.patientContext).findViewById(R.id.imageShareImageView);
        recordAudioImageView= (ImageView) ((PatientActivity) PatientActivity.patientContext).findViewById(R.id.recordAudioImageView);
        imgEndSession= (ImageView) ((PatientActivity) PatientActivity.patientContext).findViewById(R.id.imgEndSession);

    }


    public void bind(final SessionHistoryMesssage sessionHistoryMesssage, final int position) {

        if (sessionHistoryMesssage.getmSessionStart().equalsIgnoreCase("yes")
                && Integer.parseInt(sessionHistoryMesssage.getmTransToId())
                == Integer.parseInt(Utility.readPatientId(mContext))) {

            sessionstartPosition = position;
            isSessionStarted=true;

        } else if (sessionHistoryMesssage.getmSessionStart().equalsIgnoreCase("already started")) {

            alreadystartPosition = position;

        } else if (sessionHistoryMesssage.getmSessionStart().equalsIgnoreCase("end")) {

            sessionendPosition = position;

        } else {

        }

        if (sessionHistoryMesssage.getmMediaType().equalsIgnoreCase("audio")) {



            layoutAudio.setVisibility(View.VISIBLE);
            layoutImage.setVisibility(View.GONE);

            seekBar.setFocusable(false);

            txtAudioSendTime.setText(Utility.convertDateTime(sessionHistoryMesssage.getmSentTime()));

            audioDirectory = Environment.getExternalStorageDirectory().getAbsolutePath() + "/A/Audio/";

            File file = new File(Environment.getExternalStorageDirectory()
                    + "/A/Audio/" + messageArrayList.get(position).getmMediaValue());

            if (file.exists()) {

                imgDownloadAudio.setVisibility(View.GONE);
                playPauseImageView.setVisibility(View.VISIBLE);
                playPauseImageView.setImageResource(R.drawable.play_icon);

            } else {

                imgDownloadAudio.setVisibility(View.VISIBLE);
                playPauseImageView.setVisibility(View.GONE);

                imgDownloadAudio.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {

                        imgDownloadAudio.setVisibility(View.GONE);

                        Log.v("basepath", mBasePath);

                        new DownloadFile(mBasePath + messageArrayList.get(position).getmMediaPath() +
                                messageArrayList.get(position).getmMediaValue(),
                                audioDirectory + messageArrayList.get(position).getmMediaValue()
                                , imgDownloadAudio, progressAudioDownload).execute();
                    }
                });

            }

            seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                @Override
                public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

                }

                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {

                }

                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {

                    if (playposition == position) {
                        int seekBarPosition = seekBar.getProgress();
                        mPlayer.seekTo(seekBarPosition);
                    }
                }
            });

            playPauseImageView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {

                    if (!mService.isPlaying()) {

                        playposition = position;
                        mService.playAudio(audioDirectory + messageArrayList.get(position).getmMediaValue());
                        notifyDataSetChanged();

                    } else {

                        if (playposition == position) {
                            playposition = -1;
                            mHandler.removeCallbacks(null);
                            mService.stopAudio();
                            notifyDataSetChanged();
                        } else {
                            playposition = position;
                            mService.playAudio(audioDirectory + messageArrayList.get(position).getmMediaValue());
                            notifyDataSetChanged();
                        }
                    }

                }
            });

        } else if (messageArrayList.get(position).getmMediaType().equalsIgnoreCase("photo")) {

            layoutImage.setVisibility(View.VISIBLE);
            layoutAudio.setVisibility(View.GONE);

            txtImageSendTime.setText(Utility.convertDateTime(messageArrayList.get(position).getmSentTime()));

            imageDirectory = Environment.getExternalStorageDirectory().getAbsolutePath() + "/A/Image/";

            Log.v("image path123", mBasePath + messageArrayList.get(position).getmMediaPath()
                    + messageArrayList.get(position).getmMediaValue());

            try {

                File file = new File(Environment.getExternalStorageDirectory()
                        + "/A/Image/" + messageArrayList.get(position).getmMediaValue());



                if (!file.exists()) {
                    layoutdownloadImage.setVisibility(View.VISIBLE);
                    imageView.setVisibility(View.GONE);
                } else {
                    layoutdownloadImage.setVisibility(View.GONE);
                    imageView.setVisibility(View.VISIBLE);
                    imageView.setImageBitmap(BitmapFactory.decodeFile(file.getAbsolutePath()));
                }

                layoutdownloadImage.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {

                        mSessionId = messageArrayList.get(position).getmSessionId();
                        imgDownloadImage.setVisibility(View.GONE);

                        new DownloadImage(mBasePath + messageArrayList.get(position).getmMediaPath()
                                + sessionHistoryMesssage.getmMediaValue(),
                                sessionHistoryMesssage.getmMediaValue(),
                                imgDownloadImage, progressImageDownload
                        ).execute();
                    }
                });

                imageView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        showImageDialog(position);
                    }
                });


            } catch (Exception e) {
                e.printStackTrace();
            }

        }

        if (sessionstartPosition == position) {

                layoutSessionStart.setVisibility(View.VISIBLE);
                layoutAudio.setVisibility(View.GONE);
                layoutImage.setVisibility(View.GONE);

            txtSessionStart.setText("Start session");

            if (sessionHistoryMesssage.getmMediaType().equalsIgnoreCase("audio")) {
                params = (RelativeLayout.LayoutParams) layoutSessionStart.getLayoutParams();
                params.addRule(RelativeLayout.BELOW, R.id.layoutAudio);
            } else if (sessionHistoryMesssage.getmMediaType().equalsIgnoreCase("photo")) {
                params = (RelativeLayout.LayoutParams) layoutSessionStart.getLayoutParams();
                params.addRule(RelativeLayout.BELOW, R.id.layoutImage);
            } else {

            }

            if(!isSessionStarted) {
                imageShareImageView.setVisibility(View.GONE);
                recordAudioImageView.setVisibility(View.GONE);
                imgEndSession.setVisibility(View.GONE);
                isSessionStarted=true;
            }


            txtSessionStart.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    retrofitRestAdapter = new RetrofitRestAdapter();

                    Map<String, String> sessionstartParams = new HashMap<String, String>();
                    sessionstartParams.put("session_id", sessionHistoryMesssage.getmSessionId());
                    final ProgressDialog progressDialog = new ProgressDialog(mContext);
                    progressDialog.setTitle("Please wait...");
                    progressDialog.show();

                    retrofitRestAdapter.sessionStart(sessionstartParams, new Callback<SessionHistoryResponse>() {
                        @Override
                        public void onResponse(Call<SessionHistoryResponse> call, Response<SessionHistoryResponse> response) {

                            Log.v("session start response", new Gson().toJson(response.body()));
                            progressDialog.dismiss();
                            txtSessionStart.setEnabled(false);
                            txtSessionStart.setClickable(false);

                            imageShareImageView.setVisibility(View.VISIBLE);
                            recordAudioImageView.setVisibility(View.VISIBLE);
                            imgEndSession.setVisibility(View.VISIBLE);
                        }

                        @Override
                        public void onFailure(Call<SessionHistoryResponse> call, Throwable t) {

                            t.printStackTrace();
                            progressDialog.dismiss();
                            imageShareImageView.setVisibility(View.GONE);
                            recordAudioImageView.setVisibility(View.GONE);
                            imgEndSession.setVisibility(View.GONE);
                        }
                    });

                }
            });
        } else {

        }

        if (position == alreadystartPosition) {
            layoutSessionStart.setClickable(false);
            txtSessionStart.setClickable(false);
            imageShareImageView.setVisibility(View.VISIBLE);
            recordAudioImageView.setVisibility(View.VISIBLE);
            imgEndSession.setVisibility(View.VISIBLE);
        } else {

        }

        if (position == sessionendPosition) {

            layoutSessionStart.setClickable(false);
            txtSessionStart.setClickable(false);
           // layoutSessionStart.setVisibility(View.VISIBLE);
            txtSessionStart.setText("End session");

            if (sessionHistoryMesssage.getmMediaType().equalsIgnoreCase("audio")) {
                params = (RelativeLayout.LayoutParams) layoutSessionStart.getLayoutParams();
                params.addRule(RelativeLayout.BELOW, R.id.layoutAudio);
            } else if (sessionHistoryMesssage.getmMediaType().equalsIgnoreCase("photo")) {
                params = (RelativeLayout.LayoutParams) layoutSessionStart.getLayoutParams();
                params.addRule(RelativeLayout.BELOW, R.id.layoutImage);
            } else {

            }

            imageShareImageView.setVisibility(View.GONE);
            recordAudioImageView.setVisibility(View.GONE);
            imgEndSession.setVisibility(View.GONE);
        } else {

        }


        if (position == playposition) {
            if (mBound) {

                Log.v("service", "bound");

                mHandler.removeCallbacks(null);

                seekBar.setMax(mService.getrunningTime() / 1000);

                Log.v("duration", mService.getrunningTime() + "");
                playPauseImageView.setImageResource(R.drawable.stop_icon);

                ((PatientActivity) mContext).runOnUiThread(new Runnable() {

                    @Override
                    public void run() {

                        if (mService.isPlaying()) {

                            num = mService.getDuration();
                            seekBar.setProgress(num / 1000);
                            runningTimerTextView.setText(convertMilliToMinutes(num));
                            //                            vhItemHolder.seekBar.setFocusable(false);
                            mHandler.postDelayed(this, 50);

                        } else {
                            playposition = -1;
                            mHandler.removeCallbacks(null);
                            seekBar.setProgress(0);
                            seekBar.setFocusable(false);
                            runningTimerTextView.setText("00:00");
                            playPauseImageView.setImageResource(R.drawable.play_icon);

                        }


                    }
                });
            } else {
                Log.v("service", "not bound");
            }

        } else {

            mHandler.removeCallbacks(null);
            playPauseImageView.setImageResource(R.drawable.play_icon);
            runningTimerTextView.setText("00:00");

            seekBar.setMax(0);
        }


    }
}

Service class

public class LocalService extends Service {

boolean mBound = false;

private final IBinder mBinder = new LocalBinder();
private int timer;

private MediaPlayer mediaPlayer = new MediaPlayer();
private boolean isStopped = true;
// Random number generator

/**
 * Class used for the client Binder.  Because we know this service always
 * runs in the same process as its clients, we don't need to deal with IPC.
 */
public class LocalBinder extends Binder {

    public LocalService getService() {
        // Return this instance of LocalService so clients can call public methods

        return LocalService.this;
    }
}

@Override
public IBinder onBind(Intent intent) {

    return mBinder;
}

public int getrunningTime() {

    return mediaPlayer.getDuration();
}

public int getDuration() {
    // Log.v("player duration",timer+"");
    if (mediaPlayer != null) {
        if (!isStopped) {
            Log.v("playing", (mediaPlayer.getCurrentPosition()) + "");
            return mediaPlayer.getCurrentPosition();
        } else {
             //      mediaPlayer.reset();
            mHandler.removeCallbacks(null);
            return 0;

        }
    } else {
        Log.v("not playing", (mediaPlayer.getCurrentPosition()) + "");
        mHandler.removeCallbacks(null);
        return 0;
    }
}

@Override
public void onCreate() {
    super.onCreate();
    mediaPlayer = new MediaPlayer();

    try {

        mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {

                isStopped = false;
                //playAudio();

                Log.v("max duration", timer + "");

            }
        });

        mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {

                //mediaPlayer.stop();
                isStopped = true;
                mediaPlayer.reset();
                //mediaPlayer.release();
            }
        });


    } catch (Exception e) {
        e.printStackTrace();
    }

}

public boolean isPlaying() {
    if (mediaPlayer != null) {
        try {
            if (mediaPlayer.isPlaying()) {
                return true;
            } else {
                return false;
            }


        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    } else {
        return false;
    }
}

public void playAudio(String path) {

    Log.v("audio path",path);
    if (mediaPlayer.isPlaying()) {
        mediaPlayer.stop();
    }

    mediaPlayer.reset();

    try {
        mediaPlayer.setDataSource(path);
        mediaPlayer.prepare();
        mediaPlayer.start();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public void stopAudio() {

    if (mediaPlayer != null) {
        mediaPlayer.stop();
        //  mediaPlayer.reset();
        isStopped = true;
    }

}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    return START_STICKY;
}

@Override
public void unbindService(ServiceConnection conn) {
    super.unbindService(conn);

    mediaPlayer.reset();
    mediaPlayer.release();
}


@Override
public void onDestroy() {
    super.onDestroy();
    mediaPlayer.reset();
    mediaPlayer.release();

  }
}

enter image description here

Janaki
  • 145
  • 2
  • 16
  • I got the solution from this link. It may help for others. https://stackoverflow.com/questions/28305745/textview-scrolling-is-resetting-when-i-update-a-different-textview-from-the-same – Janaki Aug 08 '17 at 07:12
  • Hi, Did you get the solution of your problem? @Janaki – ranasaha Aug 01 '19 at 08:13

2 Answers2

0

Because you are calling notifyDataSetChanged() and it's updating the whole recyclerview whenever you do that. You better call notifyItemChanged(position) to update the certain item that you are supposed to update.

((PatientActivity) mContext).runOnUiThread(new Runnable() {

                    @Override
                    public void run() {

                        if (mService.isPlaying()) {

                            num = mService.getDuration();
                            seekBar.setProgress(num / 1000);
                            runningTimerTextView.setText(convertMilliToMinutes(num));
                            //                            vhItemHolder.seekBar.setFocusable(false);
                            mHandler.postDelayed(this, 50);

                        } else {
                            playposition = -1;
                            mHandler.removeCallbacks(null);
                            seekBar.setProgress(0);
                            seekBar.setFocusable(false);
                            runningTimerTextView.setText("00:00");
                            playPauseImageView.setImageResource(R.drawable.play_icon);

                        }


                    }
                });

can you convert that into something like.

if (position == playposition) {
            if (mBound) {

                Log.v("service", "bound");

                mHandler.removeCallbacks(null);

                seekBar.setMax(mService.getrunningTime() / 1000);

                Log.v("duration", mService.getrunningTime() + "");
                playPauseImageView.setImageResource(R.drawable.stop_icon);


                        if (mService.isPlaying()) {

                            num = mService.getDuration();
                            seekBar.setProgress(num / 1000);
                            runningTimerTextView.setText(convertMilliToMinutes(num));
                            //                            vhItemHolder.seekBar.setFocusable(false);
                           notifyItemChanged(position);

                        } else {
                            playposition = -1;
                            mHandler.removeCallbacks(null);
                            seekBar.setProgress(0);
                            seekBar.setFocusable(false);
                            runningTimerTextView.setText("00:00");
                            playPauseImageView.setImageResource(R.drawable.play_icon);
notifyItemChanged(position);
                        }




            }
}

Try to remove the whole usage of the runnable.

Bryan
  • 337
  • 1
  • 12
  • i already tried notifyItemChanged(position). But it is not helpful. – Janaki Aug 04 '17 at 08:47
  • can you post the local service class? – Bryan Aug 04 '17 at 09:31
  • You have 2 different layout with the same viewholder. This should have been handled in 2 different viewholders, so you can make something like ViewHolder instanceof PlayerViewHolder, things like these will make it easy to track. But all in all, I think the problem lies with your runnable, since it updates the view and it runs through the UI thread. – Bryan Aug 04 '17 at 09:53
  • How can i update textview without using UI thread. Help me to solve this issue. – Janaki Aug 04 '17 at 10:41
  • IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling. exception is thrown if I put your code. – Janaki Aug 04 '17 at 12:13
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/151083/discussion-between-bryan-and-janaki). – Bryan Aug 04 '17 at 16:01
0

setHasStableIds(true); Add this in your adapter constructor and Override these two methodes in adapter.

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

@Override
public int getItemViewType(int pos) {
       //return pos;
return Integer.parseInt(messageArrayList.get(position).getmSentBy());
}

It might help you...

Mohd Saquib
  • 580
  • 4
  • 14