简体   繁体   English

显示后如何检测每个 RecyclerView 项目

[英]How to detect each RecyclerView item after it is displayed

I want to detect each item in my RecylerView after it is displayed to the user.我想在RecylerView显示给用户后检测每个项目。

Basically, I am trying to play a sound on each item after it is loaded on the screen.基本上,我试图在每个项目加载到屏幕后播放声音。

But I am not able to detect whenever each item is loaded on the screen!但是我无法检测到每个项目何时加载到屏幕上! Is there any method I have to call to detect each item rendered有什么方法我必须调用来检测呈现的每个项目

E.g 1st RecyclerView item displayed -> play sound
    2st RecyclerView item displayed -> play sound...

My Adapter class looks like this -我的适配器类看起来像这样 -

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

    private List<Multiples> items = new ArrayList<>();

    private Context ctx;
    private OnItemClickListener mOnItemClickListener;
    private int animation_type = 0;
    .........
    .........

I am calling this initComponent() method from onCreated() method.我从onCreated()方法调用这个initComponent() ) 方法。 Can you give advice on what should I do to achieve my goal as described above?您能否就我应该怎么做才能实现上述目标提供建议?

private void initComponent() {
    recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setHasFixedSize(true);
    items = DataGenerator.getPeopleData(this,of,value);
    setAdapter();

    /* MediaPlayer mp=MediaPlayer.create(this, R.raw.sword);
    if (mp.isPlaying()) {
        mp.stop();
        mp.release();
        mp = MediaPlayer.create(this, R.raw.sword);
    } mp.start();*/
}

private void setAdapter() {
    // Set data and list adapter
    mAdapter = new AdapterListAnimation(this, items, animation_type);
    recyclerView.setAdapter(mAdapter);

    // on item list clicked
    mAdapter.setOnItemClickListener(new AdapterListAnimation.OnItemClickListener() {
        @Override
        public void onItemClick(View view, com.math.multiplication.model.Multiples obj, int position) {
            Snackbar.make(parent_view, "Item " + obj.first + " clicked", Snackbar.LENGTH_SHORT).show();
        }
    });
}

you need to override onViewAttachedToWindow and onViewDetachedFromWindow .您需要覆盖onViewAttachedToWindowonViewDetachedFromWindow but for detecting holer type you need getItemViewType() just like that:但是为了检测空洞类型,您需要getItemViewType()就像这样:

public class PostAdapter extends RecyclerView.Adapter {

@Override
public int getItemViewType(int position) {
    switch (types.get(position)){
        case 1:
            return 1;
        case 2:
            return 2;
        default:
            return position;


    }
}
@Override
public void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder) {
    super.onViewAttachedToWindow(holder);
    if (holder.getItemViewType() == 1){
        //play song

    }

}

@Override
public void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) {
    super.onViewDetachedFromWindow(holder);
    if (holder.getItemViewType() == 1){
        //pause song

    }
}

You can simply use onViewAttachedToWindow(VH) in your adapter.您可以在适配器中简单地使用 onViewAttachedToWindow(VH) 。 See https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#onViewAttachedToWindow(VH)请参阅https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#onViewAttachedToWindow(VH)

Update:更新:

As you know RecyclerView will be call OnBindViewHolder only once for each item.如您所知,对于每个项目, RecyclerView只会调用一次OnBindViewHolder

RecyclerView will not call this method again if the position of the item changes in the data set unless the item itself is invalidated or the new position cannot be determined.如果item在数据集中的位置发生变化,RecyclerView不会再次调用该方法,除非item本身失效或者无法确定新的位置。

So you can use onViewAttachedToWindow所以你可以使用onViewAttachedToWindow

(onViewAttachedToWindow) Called when a view created by this adapter has been attached to a window. (onViewAttachedToWindow) 当此适配器创建的视图已附加到窗口时调用。

This can be used as a reasonable signal that the view is about to be seen by the user.这可以用作用户即将看到视图的合理信号。 If the adapter previously freed any resources in onViewDetachedFromWindow those resources should be restored here.如果适配器之前释放了 onViewDetachedFromWindow 中的任何资源,则应在此处恢复这些资源。

You can use it like this:你可以像这样使用它:

public class Adapter extends RecyclerView.Adapter<MyViewHolder> {
   
    // rest of your code

   
    @Override
    public void onViewAttachedToWindow(MyViewHolder holder) {
         super.onViewAttachedToWindow(holder);
        // play your sound
    }
}

Hope it helps!希望能帮助到你!

You need to add listener for TTS.您需要为 TTS 添加监听器。 Then update your RecyclerView to show right image when speech starts and ends.然后更新您的RecyclerView以在语音开始和结束时显示正确的图像。

I've created a test project to show how it can be implemented.我创建了一个测试项目来展示它是如何实现的。 Here you can see how it works.在这里你可以看到它是如何工作的。 Here is my github repository.是我的 github 存储库。

Here is main part of my MainActivity class.这是我的MainActivity类的主要部分。

private void initTts() {
    tts = new TextToSpeech(this, this);
    tts.setLanguage(Locale.US);
    tts.setOnUtteranceProgressListener(new MyListener());
}

@Override
public void onInit(int status) {
    playSound(0);
}

private void playSound(int index) {
    HashMap<String, String> hashMap = new HashMap<>();
    hashMap.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, String.valueOf(index));
    tts.speak(data.get(index), TextToSpeech.QUEUE_ADD, hashMap);
}

class MyListener extends UtteranceProgressListener {
    @Override
    public void onStart(String utteranceId) {
        int currentIndex = Integer.parseInt(utteranceId);
        mMainAdapter.setCurrentPosition(currentIndex);
        handler.post(new Runnable() {
            @Override
            public void run() {
                mMainAdapter.notifyDataSetChanged();
            }
        });
    }

    @Override
    public void onDone(String utteranceId) {
        int currentIndex = Integer.parseInt(utteranceId);
        mMainAdapter.setCurrentPosition(-1);
        handler.post(new Runnable() {
            @Override
            public void run() {
                mMainAdapter.notifyDataSetChanged();
            }
        });
        if (currentIndex < data.size() - 1) {
            playSound(currentIndex + 1);
        }
    }

    @Override
    public void onError(String utteranceId) {
    }
}

If I understood your problem correctly, you want to play a sound when an item in the RecyclerView is loaded.如果我正确理解了您的问题,您希望在加载RecyclerView中的项目时播放声音。

Hence I would like to think of a workaround here.因此,我想在这里想一个解决方法。 You might consider having an item added after the sound for the previous item has been played.您可以考虑在播放前一个项目的声音后添加一个项目。

I would like to provide a pseudo code for what I am trying to explain.我想为我试图解释的内容提供一个伪代码。 You might consider using the MediaPlayer.OnCompletionListener for listening if your sound has stopped playing and then add the next item to the RecyclerView and then call notifyDataSetChanged on your adapter to load the next item in your RecyclerView .如果您的声音停止播放,您可以考虑使用MediaPlayer.OnCompletionListener来监听,然后将下一个项目添加到RecyclerView ,然后在适配器上调用notifyDataSetChanged以加载RecyclerView中的下一个项目。

Hence you might want the following modification in your adapter first.因此,您可能希望首先在适配器中进行以下修改。

// Declare a function in your adapter which adds new item in the RecyclerView
public void addItem(Multiples item) {
    items.add(item);
    notifyItemInserted(items.size());
}

Now in your Activity where you set up the RecyclerView , you need to have the following arrays.现在在您设置RecyclerViewActivity中,您需要具有以下数组。

ArrayList<Multiples> allItems = new ArrayList<Multiples>(); // This list will contain all the data
int soundToBePlayed = -1; // Initialize the value with -1
ArrayList<Integer> soundList = getAllSoundsInAList(); 

Modify your functions as follows.如下修改您的函数。

private void initComponent() {
    recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setHasFixedSize(true);
    allItems = DataGenerator.getPeopleData(this,of,value);
    setAdapter();
}

private void setAdapter() {
    mAdapter = new AdapterListAnimation(this, animation_type); // Do not pass the items list
    recyclerView.setAdapter(mAdapter);

    // .. Your item click listener goes here
}

As the items was removed from the constructor, you need to modify the constructor of your adapter as well.由于items已从构造函数中删除,因此您还需要修改适配器的构造函数。 Just remove the variable which takes the items .只需删除接受items的变量。 Because we will be adding elements to the items later.因为我们稍后将向items添加元素。

Then you need to write another function which needs to be called in your onCreate function of your activity after your call your initComponent function.然后,您需要在调用initComponent函数后编写另一个需要在活动的onCreate函数中调用的函数。 The function should look like the following.该函数应如下所示。

private void addItemInRecyclerViewAndPlaySound() {

    soundToBePlayed++;

    adapter.addItem(allItems.get(soundToBePlayed));
    recyclerView.scrollToPosition(adapter.getItemCount() - 1);

    Integer soundId = soundList.get(soundToBePlayed);

    MediaPlayer mp = MediaPlayer.create(this, soundId);

    if (mp.isPlaying()) {
        mp.stop();
        mp.release();
        mp = MediaPlayer.create(this, soundId);
        mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            public void onCompletion(MediaPlayer mp) {
                // Call this function again to add next item
                // and play the next sound at the same time
                if(soundToBePlayed < allItems.size() - 1)
                    addItemInRecyclerViewAndPlaySound();
            }
        });
    }

    mp.start();
}

I have not tested the code, however, I think you get the idea already.我没有测试过代码,但是,我想你已经明白了。 This can be implemented gracefully using a interface inside the adapter as well.这也可以使用适配器内部的interface优雅地实现。 The imeplemntation technique is your choice.实施技术是您的选择。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM