-1

I am calling notifyItemChanged(position) from a for loop to set ImageViewVisibility = inVisible, but All the image views are shown at once after for loop is ended. Whereas I want to show/hide imageView one at a time. What do I need to do to fix this?

Present output:

All image views are visible all together at once when for loops ends. (I mean OnBindviewHolder method is getting called only after for loop is ended`

Expected output:

As for loops executed for each index, I want to show/hide imageView for each row one by one (I mean OnBindviewHolder method should call as each for loop is called)

What I have tried:

I have tried notifyItemChanged(pos);,notifyDataSetChanged();, notifyItemInserted(pos); but none of them helped me to get expected output. I also tried https://stackoverflow.com/a/35392153/1684778 but still the same ouput.

activity

private List<Multiples> items = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_display_result); 
    recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view); 

    // use a linear layout manager
    layoutManager = new LinearLayoutManager(this);
    recyclerView.setLayoutManager(layoutManager);

    // specify an adapter (see also next example)
    items.addAll(DataGenerator.getPeopleData(this, of, value));
    mAdapter = new MyAdapter(items);
    recyclerView.setAdapter(mAdapter);


    //----------now give few seconds untill all default data is loaded in the recyclerView----------------
    final Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            //--------------Now call for loop on every 2 seconds so that we can hide or show ImageViews in each 2 seconds 
            MyAdapter m= (MyAdapter) mAdapter;
            for (Multiples item : items) {
                Multiples multiples1=items.get(items.indexOf(item));
                multiples1.setImageShow(true);  // set true to hide loop 

                Log.i("ms","----------in activity--------"+items.indexOf(item));

                m.updateItem(item,items.indexOf(item));  // forward each row to adapter to take effect

                try {   // sleep for 2 seconds so that we can see the effect of above code(hide/show imageView for this row index  items.indexOf(item)
                    Log.i("s","#####################  going to sleep #######################"+items.indexOf(item) );
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }, 2500);

}

adapter

public void updateItem(final Multiples newItem, final int pos) {
    newItem.setImageShow(true);
    items.set(pos, newItem); //update passed value in your adapter's data structure
    notifyItemChanged(pos);
}

// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
    // - get an element from your dataset at this position
    // - replace the contents of the view with that element

    if (holder instanceof MyAdapter.MyViewHolder) {
        final MyAdapter.MyViewHolder view = (MyAdapter.MyViewHolder) holder;
        final Multiples p = items.get(position);
        view.name.setText(p.first + " X " + p.getSecond() + "= " + p.getResult());

        // if(position>0) {
        if (p.imageShow) {
            view.image1.setVisibility(View.VISIBLE);
            view.image.setVisibility(View.INVISIBLE);
        } else {
            view.image1.setVisibility(View.INVISIBLE);
            view.image.setVisibility(View.VISIBLE);
        }
    }
    // }

}
Dan
  • 2,086
  • 11
  • 71
  • 137

1 Answers1

1

Your issue is that you're blocking the main thread while you try to change all of the visibilities. So although you are trying to sleep to put a delay between the operations, the previous ones can't execute because the thread is blocked. In other words, you are queuing up all of the actions but they can't take effect until you release the main thread and the system can process them, which then happens all at the same time.

public void run() {
    // THIS IS HAPPENING ON THE MAIN THREAD - ANDROID CAN'T CONTINUE DRAWING
    // UNTIL THIS METHOD FINISHES
        //--------------Now call for loop on each 2 seconds so that we can hide or show ImageViews in each 2 seconds 
        MyAdapter m= (MyAdapter) mAdapter;
        for (Multiples item : items) {
            Multiples multiples1=items.get(items.indexOf(item));
            multiples1.setImageShow(true);  // set true to hide loop 

            Log.i("ms","----------in activity--------"+items.indexOf(item));

            m.updateItem(item,items.indexOf(item));  // forward each row to adapter to take effect

            try {   // sleep for 2 seconds so that we can see the effect of above code(hide/show imageView for this row index  items.indexOf(item)
                Log.i("s","#####################  going to sleep #######################"+items.indexOf(item) );

                // THIS SLEEP IS BLOCKING THE MAIN THREAD - ANDROID CAN'T DRAW
                // UNTIL THE OUTER FUNCTION IS DONE
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    } // THE END OF THE RUN BLOCK - NOW ANDROID CAN RUN THE INSTRUCTIONS THAT
      // HAVE BEEN QUEUED UP, WHICH WILL APPEAR INSTANT

One thing you could do is fire off runnables in the for loop, like you're already doing with the main function.

public void run() {
        //--------------Now call for loop on each 2 seconds so that we can hide or show ImageViews in each 2 seconds 
        MyAdapter m= (MyAdapter) mAdapter;

        int index = 0;
        for (Multiples item : items) {
            // DO NOT SLEEP TO NOT BLOCK THE MAIN THREAD - SCHEDULE WORK FOR LATER INSTEAD
            handler.postDelayed(new Runnabled() {
                Multiples multiples1=items.get(items.indexOf(item));
                multiples1.setImageShow(true);  // set true to hide loop 

                // OTHER LOGIC FOR THIS ITEM HERE

            }, 2000 * index++); // SCHEDULE EACH ITEM TO BE 2 SECONDS LATER THAN THE PREVIOUS
        }
    } // THE LOOP FINISHES INSTANTLY AND DOES NOT BLOCK THE MAIN THREAD

This way you schedule all of the updates to happen at the time you want and immediately free the main thread. Later, at the scheduled times, the main thread is free to process the instructions and do the animation as desired.

Hope that helps!

dominicoder
  • 9,338
  • 1
  • 26
  • 32
  • Thanks, It works as per the question. Now, What i have to write, if i want to put TTS to read each row item title , So instead of time delay , now i want to play each text based on Speaking(Text-to speech) completed, & once TTS is played a row then i want to show/hide imageView . So once TTS moved to next row, then i want to show/hide image View of next row etc... Example: please check this screenshot: https://i.stack.imgur.com/ddfNz.jpg – Dan Apr 08 '19 at 06:24
  • I'd suggest creating a brand new question (you can link to this one as a reference) than trying to continue solving different issues in these comments. – dominicoder Apr 08 '19 at 19:13
  • Thanks! Please check this link may you can help me on this brand new question: https://stackoverflow.com/questions/55582922 – Dan Apr 08 '19 at 23:11