简体   繁体   中英

RecyclerView and AdapterView, fails to show items

I was doing some simple examples, to have them as guides, etc. I have a RecyclerView with its own adapter. The items are data models, with a text and an image. The ViewHolder, besides containing the corresponding views, I added a Boolean to control if the image is visible or not.

When I click, for example in the first item, the image disappears (or appears if it is clicked again).

The problem is that if you click (for example the first element) the image disappears as expected, but when scrolling and the recycler loads the new elements not visible, an element suddenly appears with the hidden image.

Debugging a little I see that when loading the element, depends appears with the boolean to true, when in theory should be false.

I can not understand what is happening, since the elements in the list are different.

PS: As I said, the code is really easy, so don't expect great things.

MainActivity:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = (RecyclerView) findViewById(R.id.recycler);

        List<Person> personList = new ArrayList<>();
        personList.add(new Person ("User1"));
        personList.add(new Person ("User2"));
        personList.add(new Person ("User3"));
        personList.add(new Person ("User4"));
        personList.add(new Person ("User5"));
        personList.add(new Person ("User6"));
        personList.add(new Person ("User7"));
        personList.add(new Person ("User8"));
        personList.add(new Person ("User9"));
        personList.add(new Person ("User10"));
        personList.add(new Person ("User11"));
        personList.add(new Person ("User12"));
        personList.add(new Person ("User13"));
        personList.add(new Person ("User14"));
        personList.add(new Person ("User15"));
        personList.add(new Person ("User16"));
        personList.add(new Person ("User17"));
        personList.add(new Person ("User18"));
        personList.add(new Person ("User19"));
        personList.add(new Person ("User20"));
        personList.add(new Person ("User22"));
        personList.add(new Person ("User23"));


        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        CustomImageAdapter adapter = new CustomImageAdapter(personList);
        recyclerView.setAdapter(adapter);

Adapter:

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

    private List<Person> personList;

    public CustomImageAdapter(List<Person> personList) {
        this.personList = personList;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.image_person, parent, false);

        return new ViewHolder(itemView);
    }

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

        Person person = personList.get(position);
        holder.name.setText(person.getName());
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!holder.imageHide) {
                    holder.image.setVisibility(View.INVISIBLE);

                } else {
                    holder.image.setVisibility(View.VISIBLE);
                }
                holder.imageHide = !holder.imageHide;
            }
        });
    }

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

    public static class ViewHolder extends RecyclerView.ViewHolder {
        TextView name;
        ImageView image;
        boolean imageHide = false;


        public ViewHolder(View itemView) {
            super(itemView);
            name = (TextView) itemView.findViewById(R.id.name);
            image = (ImageView) itemView.findViewById(R.id.image);
            imageHide = false;
        }
    }
}

Data Model: (Just a sneak peak)

public class Person {
    private String name;
    private String image;


    public Person(String name) {
        this.name = name;
    }
    ....
}

Layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="10dp">

    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView" />

    <ImageView
        android:id="@+id/image"
        android:layout_width="30dp"
        android:layout_height="30dp"
        app:srcCompat="@mipmap/ic_launcher"/>
</LinearLayout>

Couple of images as example:

在此处输入图片说明

在此处输入图片说明

The ViewHolder Class is intended to be used for quick access to "View" Related items such as textviews or imageViews things of that nature.

When the content needs to be dynamic based on an object model, then the object model needs to drive the visibility pattern. Not a boolean nested inside the viewholder class.

Think of it this way. The recycler view recycles (n) number of views.

--View 1 (shows person 1) boolean visible in viewholder 1

--View 2 (shows person 2) boolean visible in viewholder 1

--View 3 (shows person 3) boolean visible in viewholder 1

click on view 1

--View 1 -- save boolean false "image not visible"

Current View after Click

--View 1 (shows person 1) boolean invisible in viewholder 1

--View 2 (shows person 2) boolean visible in viewholder 1

--View 3 (shows person 3) boolean visible in viewholder 1

Now you scroll recycler

view 1 is off screen and recycled for next onscreen as Person 4 now retaining boolean value in viewholder pattern

--View 2 (shows person 2) boolean visible in viewholder 2

--View 3 (shows person 3) boolean visible in viewholder 3

--View 1 (shows person 4) boolean invisible in viewholder 1

So to correct this: Simply modify your code like this:

public class Person {
private String name;
private String image;
private boolean isVisible;

public boolean getIsVisible(){
     return isVisible;
}
public void setIsVisible(boolean value){
    isVisible = value;
}


public Person(String name) {
    this.name = name;
}
....

}

Then modify your adapter like this:

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

    final Person person = personList.get(position);
    holder.name.setText(person.getName());
    holder.image.setVisibility(person.getIsVisibility() ? VISIBLE : INVISIBLE);
    onItemClick(holder.root, person, position);
}

   /*///////////////////////////////////////////////////////////////
// CLICK LISTENERS
*////////////////////////////////////////////////////////////////
private void onItemClick(final LinearLayout root, final Person model, final int position){
    root.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            person.setIsVisible(!person.getIsVisible());
            root.findViewById(YOUR_IMAGE_ID).setVisibility(person.getIsVisible() ? VISIBLE : INVISIBLE);
             //or if you prefer to not findViewById, you can just update person boolean and call
            //notifyDataSetChanged();

        }

    });

}

Then add the root id to your XML file and viewholder class if you choose to do the findViewById route (recommended as more efficient then notify)

Another thing you may consider for your guide is to explain how to handle the click outside of the adapter. I favor an interface usually if I'm not doing databinding. Add this to bottom of your adapter.

 public interface ItemSelectedListener {
    void personList_onItemClick(View view, int position, final Person person);
    void personList_onItemLongClick(View view, int position, final Person person);

}

Then in constructor require the ItemSelectedListener and store in the adapter class. Then you can modify your onClick handler to do

  private void onItemClick(final LinearLayout root, final Person person, final int position){
    root.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(mItemSelectedListener != null){
                mItemSelectedListener.personList_onClick(v, position, person);

            }

        }

    });

}

and of course repeat the above for long click handling.

   private void onItemLongClick(final LinearLayout root, final Person person, final int position){
    root.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            if(mItemSelectedListener != null){
                mItemSelectedListener.personList_onItemLongClick(v, position, model);

            }

            return false;

        }

    });

}

and of course add the listener for long click into your bindview as well

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

        final Person person = personList.get(position);
        holder.name.setText(person.getName());
        holder.image.setVisibility(person.getIsVisibility() ? VISIBLE : INVISIBLE);
        onItemClick(holder.root, person, position);
        onItemLongClick(holder.root, person, position);
    }

Your code looks ok, but it may be an issue with how RecyclerViews work. I think this is happening to you because you're only changing the value for the holder, and the holders are being recycled to hold different data items.

Can you try attaching a boolean imageHide to your Person class, then in the onClick change that boolean in the person class and set the visibility for the image. Then in onBind have some logic to check if the person should have the image hidden or visible.

you need to add on the onBindViewHolder a validation, something like this:

holder.image.setVisibility(holder.imageHide ?View.VISIBLE:View.INVISIBLE);

regards.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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