2017-02-08 5 views
0

Я сделал приложение чата, где использовал RecyclerView. Сообщения могут быть текстовыми или аудиосообщениями. Все работает нормально, за исключением случаев, когда я изменяю текст таймера на TextView (для компоновки аудиоплеера, который я сделал), от того, как долго воспроизводится песня. Я делаю это в Runnable. Но когда я прокручиваю RecyclerView, таймер TextView меняет текст в случайном положении.RecyclerView случайное изменение положения при прокрутке

Вот как я изменяю TextView текст:

public void updateTimer(final int position) { 
    View view = mRecyclerViewChat.getLayoutManager().findViewByPosition(position); 
    timer = (TextView) view.findViewById(R.id.timer); 

    r = new Runnable() { 
     public void run() { 
      int currentDuration; 
      if (player.isPlaying()) { 
       currentDuration = player.getCurrentPosition(); 
       timer.setText("" + milliSecondsToTimer((long) currentDuration)); 
       timer.postDelayed(this, 1000); 
      } else { 
       timer.removeCallbacks(this); 
      } 
     } 
    }; 

    timer.post(r); 
} 

position Здесь есть значение позиции я получаю от onBindViewHolder.

EDIT

Вот onBindViewHolder

@Override 
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 
    if (TextUtils.equals(mChats.get(position).senderUid, 
      FirebaseAuth.getInstance().getCurrentUser().getUid())) { 
     if (mChats.get(position).mediaUrlLocal == null) { 
      configureMyChatViewHolder((MyChatViewHolder) holder, position); 
     } else { 
      configureMyChatMediaViewHolder((MyChatMediaViewHolder) holder, position); 
     } 
    } else { 
     if (mChats.get(position).mediaUrlLocal == null) { 
      configureOtherChatViewHolder((OtherChatViewHolder) holder, position); 
     } else { 
      configureOtherChatMediaViewHolder((OtherChatMediaViewHolder) holder, position); 
     } 
    } 
} 

и вот playMedia метод, который вызывается из configureMyChatMediaViewHolder метода:

private void playMyMedia(final MyChatMediaViewHolder myChatViewHolder, final Chat chat, final int position) { 
    MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever(); 
    metaRetriever.setDataSource(chat.mediaUrlLocal); 

    String duration = 
      metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); 
    long dur = Long.parseLong(duration); 
    String seconds = String.valueOf((dur % 60000)/1000); 

    String minutes = String.valueOf(dur/60000); 
    String out = minutes + ":" + seconds; 

    myChatViewHolder.timer.setText(out); 

    if (chat.isPlay) { 
     myChatViewHolder.play.setVisibility(View.GONE); 
     myChatViewHolder.pause.setVisibility(View.VISIBLE); 
    } else { 
     myChatViewHolder.play.setVisibility(View.VISIBLE); 
     myChatViewHolder.pause.setVisibility(View.GONE); 
    } 

    myChatViewHolder.play.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      chat.isPlay = !chat.isPlay; 
      if (previousChat != position && previousChat != -1) { 
       previousChatObj = mChats.get(previousChat); 
      } 
      previousChat = position; 
      myChatViewHolder.play.setVisibility(View.GONE); 
      myChatViewHolder.pause.setVisibility(View.VISIBLE); 
      callback.onPlayClickListener(chat, previousChatObj, position); 
     } 
    }); 

    myChatViewHolder.pause.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      chat.isPlay = !chat.isPlay; 
      myChatViewHolder.play.setVisibility(View.VISIBLE); 
      myChatViewHolder.pause.setVisibility(View.GONE); 
      callback.onPauseClickListener(chat, position); 
     } 
    }); 
} 

У меня есть только один медиа экземпляр проигрывателя в фрагмент, из которого установлен адаптер для чата.

+0

Просьба представить вам 'ViewHolder' и' onBindViewHolder() 'реализации. Использование 'final int position', как вы делаете, неверно - вы никогда не должны кэшировать позицию RecyclerView. Также неясно, когда каждый раз просматривайте таймер TextView - он должен храниться в ViedHolder и т. Д. –

+0

Я отправил ответ, пожалуйста, дайте мне знать, если это поможет –

+0

Еще одна идея: после утилизации ваш TextView все еще может иметь Бегуны прикреплены к нему. поэтому ** перед ** вы вызываете 'myChatViewHolder.timer.setText (out);' call 'myChatViewHolder.timer.removeCallbacks (myChatViewHolder.timer.getTag());'. В 'updateTimer()' добавить последнюю строку 'timer.setTag (r)' –

ответ

0

Recycler view повторно использует виды, которые в настоящее время не видны. Поэтому, если ваш аудиоплеер начинает обновлять один текстовый вид, когда вы прокручиваете это текстовое изображение, то с той же ссылкой, но с другим текстом (песня), но ваш исполняемый экземпляр все еще активен и выполняет свою работу, чтобы обновить текстовый вид с той же ссылкой.

  1. Вы должны создать пользовательский класс, который Runnable будет содержать значение позиции и будет нести ответственность только за TextView следует обновить.

  2. Другой подход (менее эффективный) заключается в использовании ListView, а не для повторного использования ячеек для создания нового для каждого элемента. Это приведет к производительности вопросы

Если вы можете отправить код я хотел бы, чтобы помочь вам.

*** EDITED ****

Вот некоторая часть коды, которая может сделать вещи более ясно

@Override 
    public void onBindViewHolder(ViewHolder holder, int position) { 
     holder.songDurationView.setTag(position); 
     //TODO: implement some more logic here and start the MusicSongRunnable 
    } 



class MusicSongRunnable implements Runnable { 

    int positionOfSong; 
    TextView textView; 

    public MusicSongRunnable(int positionOfSong, TextView textView) { 
     this.positionOfSong = positionOfSong; 
     this.textView = textView; 
    } 

    @Override 
    public void run() { 
     if (player.isPlaying() && positionOfSong == textView.getTag()) { 
     //TODO: update the song; 
    } 
} 
+0

так в чем разница между тем, что вы сказали, и тем, что я сделал? Я также сохраняю значение позиции в методе, которое должно отвечать только за обновление соответствующего TextView –

+0

Не в комету, а в другом классе, который реализует Runnable и теперь именно то, что TextView в какой позиции обновить. Я отредактировал свой ответ, надеюсь, что он помогает –

+0

, см. Редактирование, которое я сделал –

0

Не ясно, где вы звоните updateTimer, но я предполагаю, что вы называете его от callback.onPlayClickListener(..) и callback.onPauseClickListener(..).

Первый, не передаёте position вы получили в качестве параметра клик слушателей, но, как documentation states вы должны использовать ViewHolder.getAdapterPosition() метод. Обратите внимание, что когда вы используете значение позиции, вы также должны проверить, что она отличается от RecuclerView.NO_POSITION каждый раз и только затем ее использовать. Сделайте свой параметр позиции не окончательным во всех методах. Это предотвратит вас от ошибок.Каждый раз, когда вы хотите использовать позицию щелчка слушателей использовать myChatViewHolder.getAdapterPosition()

Второй, так как вы кэшировать position значение в Runnable в любом случае, это, вероятно, не достаточно. Поэтому вы должны передать myChatViewHolder.timer в качестве параметра updateTimer и избавиться от первых двух строк. В конце концов вы называете updateTimer как это:

updateTimer(myChatViewHolder.timer); 

и ваш 'updateTimer' сейчас:

public void updateTimer(final TextView timer) { 

    r = new Runnable() { 
     public void run() { 
      int currentDuration; 
      if (player.isPlaying()) { 
       currentDuration = player.getCurrentPosition(); 
       timer.setText("" + milliSecondsToTimer((long) currentDuration)); 
       timer.postDelayed(this, 1000); 
      } else { 
       timer.removeCallbacks(this); 
      } 
     } 
    }; 

    timer.post(r); 
} 

Больше из документации по этому вопросу:

RecyclerView не будет вызывать onBindViewHolder(), если позиция элемента изменяется в наборе данных, если сам элемент не признан недействительным или новая позиция не может быть определена. По этой причине вы должны использовать только параметр позиции при получении связанного элемента данных внутри этого метода и не должны копировать его.

+0

ok, я сделал, как вы сказали, и передал TextView в качестве аргумента метода updateTimer(), но у меня такая же проблема, случайный текстовый текст в случайных позициях также обновлен –

+0

Возможно, Вам нужно будет увидеть весь ваш код. Это может быть не только проблема с RecyclerView –

Смежные вопросы