0

I am using this tutorial to build an itemTouchListener for my RecyclerView. The recyclerview is filled with more items than the screen fits (more than 10), so recycling gets into action. The itemtouchHelper handles both up-down and left-right movement. After 2 days of struggle (had setStableIds to true which caused flickering of the viewholder views when the were moved up-down), I finally got a better behaviour. My code of the crucial features:

  @Override
    public int getItemCount() {
        return questionlist.size();
    }

    @Override
    public void onViewMoved(int oldPosition, int newPosition) {
        targetqueobj = questionlist.get(oldPosition);

        this.fromPosition = oldPosition;
        this.toPosition = newPosition;
        questionlist.remove(targetqueobj);
        questionlist.add(newPosition, targetqueobj);

        //    targetqueobj.setinturn(toPosition+1);
    }



    @Override
    public void onViewSwiped(final RecyclerView.ViewHolder thisviewholder,final int position, int direction) {
        targetqueobj = questionlist.get(position);
        if (direction == ItemTouchHelper.LEFT){
            // saveqset();
            Intent intent = new Intent(context, QuestionEditActivity.class);
            intent.putExtra("com.logictop.mqapp.QuestionObjParcelable",targetqueobj);
            context.startActivity(intent);
        }
        if (direction == ItemTouchHelper.RIGHT) {
            // DIALOG
            AlertDialog.Builder builder = new AlertDialog.Builder(context);
            builder.setMessage(R.string.remove_question);
            builder.setNegativeButton(R.string.No, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface arg0, int arg1) {
             //       thisviewholder.itemView.setAlpha(1);
                    notifyDataSetChanged();
                }
            });

            builder.setPositiveButton(R.string.Yes, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface arg0, int arg1) {
                    questionlist.remove(position);
                    notifyItemRemoved(position);
                    notifyItemRangeChanged(position, getItemCount()-position);
                }
            });
            Dialog dialog = builder.create();
            dialog.setCancelable(false);
            dialog.setCanceledOnTouchOutside(false);
            dialog.show();

        }
    }
    if (direction == ItemTouchHelper.RIGHT) {
        // DIALOG
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setMessage(R.string.remove_question);
        builder.setNegativeButton(R.string.No, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface arg0, int arg1) {
                thisviewholder.itemView.setAlpha(1);
                notifyDataSetChanged();
            }
        });

        builder.setPositiveButton(R.string.Yes, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface arg0, int arg1) {
                questionlist.remove(position);
                notifyItemRemoved(position);
                notifyItemRangeChanged(position, getItemCount()-position);
            }
        });
        Dialog dialog = builder.create();
        dialog.setCancelable(false);
        dialog.setCanceledOnTouchOutside(false);
        dialog.show();

    }
}

The problem is this. Though the recyclerview runs smoothly with the items very nicely changing positions, when an item is swiped away, sometimes another item looks also removed when the recyclerview is scrolled down (so it seems that the other item that gets removed fills the same "screen" position in the recyclerview after it is scrolled). It doesn't happen every time and it mostly happens when a view in some specific positions are swiped away. That "gap" can be mended if I move a neighbor view up or down.

sorry, you have to wait for a bit to see the thing happening after I remove a view for the second time and scroll down

I have tried every solution I found in here (notifyDataChanged, notifyItemRangeChanged (with every parameter compination). But nothing could get me a stable behaviour. AFter a lot of stackoverflow searching I decided to follow the advice of holder.setIsRecyclable(false) even though I didn't want to do that (as it eliminates the point of having a recyclerview after all). But in that case, other problems appear. You'll see below that when most of the views are swiped away, they lower ones won't leave the screen, even though they apparently have "left the adapter" (cannot swipe them away). And in the end, the last views stay stuck.

you have to wait a bit here as well, in the end you'll see the stuck views

I have tested both ways on a completely new project with nothing external coming into the way. I have tried putting notifyDataChanged() in an overriden function of clearView. Nothing seems to provide a rock solid stable recyclerview that won't get at some point a gap in itself.

The question now: is there a way to make a recyclerview with working recycling behave like it is supposed to behave or should I accept that situation? Thank you very much for your attention!

UPDATE 1---------------------------- As I was told there could be an issue with this thisviewholder.itemView.setAlpha(1); I commented it out along with the corresponding overriden onChildDraw (the whole of it) that is used to fade the view out when it is swiped out. So now nothing gets invisible. Still the problem persists.

UPDATE 2---------------------------- I have enforced stableIds (had already done that once and it didn't work)

@Override
public long getItemId(int position){
    return questionlist.get(position).getquestionid();
}

and the adapter contructor

public QSetEditAdapter(ArrayList<QuestionObj_prcl> questionlist, Context context) {
        this.context = context;
        this.questionlist = questionlist;
        setHasStableIds(true);
    }

Still the problem persists.

UPDATE 3---------------------------- I ended up using a different tutorial and now everything works as expected. Thanks a lot!

oupoup
  • 75
  • 8
  • Obviously you are not restoring ViewHolder to default state in `Adapter.bindViewHolder` (obviously I'm talking about `thisviewholder.itemView.setAlpha(1);`) ... that's how ends following random tutorials from guy named srijith – Selvin Nov 21 '17 at 21:29
  • @Selvin what do you mean by _restoring ViewHolder to default state_? I guess not the onPause and onResume stuff, right? Because I didn't mention any such issues (haven't tried that as the problems appear on earlier stage). – oupoup Nov 21 '17 at 22:47
  • Isn't it obvious: ViewHolder stores views ... Views have some state like text property ... text color, etc. if you touch it(property) outside the onBindView you need to restore its value to default inside onBindView too ***(obviously I'm talking about thisviewholder.itemView.setAlpha(1);)*** – Selvin Nov 21 '17 at 22:49
  • The piece of code you are referring to (`thisviewholder.itemView.setAlpha(1);`) is for when the user declines removing the item, so the item has to regain it's alpha (if you check the tutorial you'll see that it overrides the `onChildDraw` and gets the alpha to zero on the view swiped away). But I am not referring to what happens when the user selects No (haven't captured that). It's the yes that is problematic (at least, for now). – oupoup Nov 21 '17 at 22:56
  • **it is not regain in case of "yes"** `builder.setPositiveButton(R.string.Yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface arg0, int arg1) { questionlist.remove(position); notifyItemRemoved(position); notifyItemRangeChanged(position, getItemCount()-position); } });` **and "the gap" is invisible item** and as I wrote in **first** comment it would be safetly to do this in bindViewHolder so when Adapter will bind new view it would restore its alpha – Selvin Nov 21 '17 at 22:57
  • my second comment came before I saw yours. Let me check it out... – oupoup Nov 21 '17 at 23:10
  • updated the question. Thanks for your help by the way. – oupoup Nov 21 '17 at 23:27

2 Answers2

2

Change your adapter this way:

Andre Classen
  • 3,868
  • 3
  • 26
  • 36
-1

I don't know why this is happening Exactly in the method below the list, it keeps the value you gave it for the first time and does not update this value !!

public void onViewSwiped (int position)

But what is the solution??? The solution is to use the -> touchHelper.startSwipe(holder) method For example :

holder.itemView.setOnTouchListener (new View.OnTouchListener () { VerOverride public boolean onTouch (View view, MotionEvent event) { if (event.getAction () == MotionEvent.ACTION_DOWN) { Update = false; touchHelper.startSwipe (holder); } return true; } });

The job of this method is to reload the deleted animations and views Good luck