繁体   English   中英

Android:使用RecyclerView和CardView进行商品复制-如何停止复制?

[英]Android: Item Duplication with RecyclerView & CardView - How do I stop the duplication?

我在TabLayout中将RecyclerView与CardView一起使用。 如果我在滑动到其他标签并返回到第一个标签后上下滚动,则RecyclerView将在第一个标签上复制卡片。 如何防止重复? 我将在帖子的注释中发布pastebin链接到其余代码。

MyRecyclerViewAdapater.java:

package com.benrcarvergmail.cvhsmobileapplication;

import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

public class MyRecyclerViewAdapater extends RecyclerView.Adapter<MyRecyclerViewAdapater.MyViewHolder> {
    private List<AnnouncementsFragment.Announcement> mDataset; // ArrayList implementation to hold data

    private static final String TAG = "MyRecyclerViewAdapter";

    // Provide a reference to the views for each data item
    // Complex data items may need more than one view per item, and
    // you provide access to all the views for a data item in a view holder
    public static class MyViewHolder extends RecyclerView.ViewHolder {
        public CardView mCardView;          // The CardView itself
        public TextView mInfoTextView;      // The announcement's text
        public TextView mIntroTextView;     // The announcement's intro text
        public TextView mDateTextView;      // The announcement's date
        public TextView mTitleTextView;     // The announcement's title
        public ImageView mCardViewIcon;     // The announcement's icon

        private boolean isExpanded = false;

        // References to all of the elements of the CardView
        public MyViewHolder(View v) {
            super(v); // Call the super() constructor
            mCardView = (CardView) v.findViewById(R.id.card_view);                  // The CardView
            mIntroTextView = (TextView) v.findViewById(R.id.intro_text_view);       // The intro text
            mInfoTextView = (TextView) v.findViewById(R.id.info_text_view);         // The text
            mTitleTextView = (TextView) v.findViewById(R.id.title_text_view);       // The title
            mDateTextView = (TextView) v.findViewById(R.id.date_text_view);         // The date
            mCardViewIcon = (ImageView) v.findViewById(R.id.card_view_icon);        // The icon

            v.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.i(TAG, "onClick() called!");

                    // If it is already expanded, collapse it. Otherwise, expand it.
                    if (isExpanded) {
                        // Collapses the CardView and sets isExpanded to false
                        Log.i(TAG, "isExpanded: " + isExpanded);
                        isExpanded = collapse(v); // returns false
                    } else {
                        // Expands the CardView and sets isExpanded to true
                        Log.i(TAG, "isExpanded: " + isExpanded);
                        isExpanded = expand(v); // returns True
                    }
                }
            });
        }

        // Expand the CardView
        private boolean expand(View cardView) {
            // Make the intro text invisible and make the full text visible
            mIntroTextView.setVisibility(View.GONE);
            mInfoTextView.setVisibility(View.VISIBLE);
            return true;
        }

        // Collapse the CardView
        private boolean collapse(View cardView) {
            // Make the intro text visible and make the full text invisible
            mInfoTextView.setVisibility(View.GONE);
            mIntroTextView.setVisibility(View.VISIBLE);
            return false;
        }
    }

    // Provide a suitable constructor (depends on the kind of dataset)
    public MyRecyclerViewAdapater(List<AnnouncementsFragment.Announcement> myDataset) {
        mDataset = myDataset;
        setHasStableIds(true);
    }

    // Sets a new Dataset to be our Dataset of Announcements
    public void updateList(List<AnnouncementsFragment.Announcement> newData) {
        mDataset = newData;
        notifyDataSetChanged();
    }

    // Add an Announcement to our Dataset
    public void addItem(int position, AnnouncementsFragment.Announcement newAnnouncement) {
        mDataset.add(position, newAnnouncement);
        notifyItemInserted(position);
    }

    // Remove an Announcement from our Dataset
    public void removeItem(int position) {
        mDataset.remove(position);
        notifyItemRemoved(position);
    }

    // Create new views (invoked by the layout manager)
    @Override
    public MyRecyclerViewAdapater.MyViewHolder onCreateViewHolder(ViewGroup parent,
                                                     int viewType) {
        // create a new view
        View v = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.card_item, parent, false);
        // set the view's size, margins, paddings and layout parameters
        MyViewHolder vh = new MyViewHolder(v);

        return vh;
    }

    @Override
    /*
    This method internally calls onBindViewHolder(ViewHolder, int) to update
    the RecyclerView.ViewHolder contents with the item at the given position
    and also sets up some private fields to be used by RecyclerView.
     */
    public void onBindViewHolder(MyViewHolder holder, int position) {

        /* Get the proper Announcement's intro text (generate it, it isn't already generated
        upon or during instantiation) and assign it to the appropriate TextView. */
        holder.mIntroTextView.setText(mDataset.get(position).generateIntro());

        /* Get the proper Announcement's text and assign the
        informational TextView's text to a shortened version of the
        text. The full text will be displayed upon expansion of the CardView. */
        holder.mInfoTextView.setText(mDataset.get(position).getText());

        /* Get the proper Announcement's date and assign the
        date TextView's text to the aforementioned date.toString() */
        holder.mDateTextView.setText(mDataset.get(position).getAnnouncementDate().toString());

        /* Get the proper Announcement's title and assign the
        title TextView's text to the aforementioned title. */
        holder.mTitleTextView.setText(mDataset.get(position).getTitle());

        // Ensure that only the intro text is visible at first
        holder.mInfoTextView.setVisibility(View.GONE);
        holder.mIntroTextView.setVisibility(View.VISIBLE);

        int imagePath = mDataset.get(position).getImageSource();
        if(!(imagePath == Integer.MIN_VALUE)) {
            /* When Image support is fully implemented, we'd assign the Image a source.
            For now, however, nothing happens except we print that an Image was specified. */
            Log.i(TAG, "An image ID was specified for the Announcement "
                    + mDataset.get(position).getTitle()); // We could use .toString() instead of .getTitle()
        }

        Log.i(TAG, "onBindViewHolder() called");
}
    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    // Returns number of elements in the mDataset List
    public int getItemCount() {
        if (mDataset == null) {
            return -1;
        } else {
            return mDataset.size();
        }
    }
}

AnnouncementsFragment.java:

package com.benrcarvergmail.cvhsmobileapplication;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;;

import java.util.ArrayList;
import java.util.Date;

/**
 * The type Announcements fragment.
 */
public class AnnouncementsFragment extends Fragment {

    private ArrayList<Announcement> data;

    private static final String TAG = "AnnouncementsFragment";

    /**
     * Instantiates a new Announcements fragment.
     */
    public AnnouncementsFragment() {
        // Instantiate the data ArrayList so we may populate it during onCreateView()
        data = new ArrayList<Announcement>();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View rootView = inflater.inflate(R.layout.fragment_announcements, container, false);
        // Create object reference to the RecyclerView created in fragment_announcements.xml
        RecyclerView rv = (RecyclerView) rootView.findViewById(R.id.rv_recycler_view);
        // Ensure that its size is fixed (unchanging)
        rv.setHasFixedSize(true);
        // Populate the data ArrayList. We currently do not utilize the boolean return type
        populateData();
        // Create an adapter for the RecyclerView, passing the ArrayList of text we want displayed
        MyRecyclerViewAdapater adapter = new MyRecyclerViewAdapater(data);
        // Set the RecyclerView's adapater to the one we just created
        rv.setAdapter(adapter);

        // A LinearLayoutManager is a A RecyclerView.LayoutManager
        // implementation which provides similar functionality to ListView.
        LinearLayoutManager llm = new LinearLayoutManager(getActivity());
        rv.setLayoutManager(llm);

        return rootView;
    }

    // This will populate the data ArrayList with the data we want to display. This may
    // eventually get more complicated (if we require lots of different data other than
    // text to be shown. Additionally, this will eventually grab the information from a server.
    private boolean populateData() {
        // This text was generated with an Android Studio plugin known as Insert Dummy Text. That
        // fact is completely useless but nevertheless, it's a good plugin and I recommend it. I
        // add a new line ( + "\n" to each String to ensure it doesn't get cut off. This may mess
        // things up of the String is only one line though, so we'll see what happens.

        // Add each new announcement to the ArrayList. We are creating the Announcements when we pass them.
        data.add(new Announcement("Test Announcement #1",
                "Chilled celery can be made melted by seasoning with white wine. " +
                    "Turkey mousse has to have a delicious, sour pickles component." + "\n",
                        Integer.MIN_VALUE,
                            new Date()));
        data.add(new Announcement("Test Announcement #2",
                "Cook iced lettuces in a bottle with soy sauce for about an hour to increase their viscosity." +
                    "Remember: scraped melon tastes best when peeled in a frying pan varnished with dill." + "\n",
                        Integer.MIN_VALUE,
                            new Date()));
        data.add(new Announcement("Test Announcement #3",
                "After warming the chickpeas, enamel avocado, rhubarb and maple syrup " +
                "with it in a plastic bag. Toast two chocolates, rice, and marmalade in a large " +
                "frying pan over medium heat, cook for a dozen minutes and soak with some " +
                "zucchini."+ "\n",
                    Integer.MIN_VALUE,
                        new Date()));
        data.add(new Announcement("Test Announcement #4",
               "All children like pressed raspberries in peanut sauce and woodruff." +
                    "Try draining paste rinseed with gold tequila, enameled with corn syrup."+ "\n",
                        Integer.MIN_VALUE,
                            new Date()));
        data.add(new Announcement("Test Announcement #5",
               "Mash peanut butter quickly, then mix with whiskey and serve thoroughly in pan." +
                    "Mash margarine smoothly, then mix with kefir and serve fairly in bottle." + "\n",
                        Integer.MIN_VALUE,
                            new Date()));

        return true; // May eventually return false if unable to pull data from server
    }


    /**
     * Announcement class to store all data pertaining to what might be
     * displayed or associated with any given announcement. This implementation
     * is subject to change at any point, as a better methodology may be discovered.
     *
     * There are a lot of possible future features to announcements. The possibilities
     * include: customisable icon, Announcement type (club, sports, general, weather, etc.),
     * Announcement caster (dedicated field pertaining to whom the announcement is from), etc.
     */
    class Announcement {

        private String title;           // The announcement's title
        private String text;            // The announcement's textual information
        private int imageSource;        // In the format R.id.XYZ
        private Date announcementDate;  // The date the announcement was posted.

        /**
         * Instantiates a new Announcement with a title, text, an image, and a date.
         *
         * @param title  the announcement's title
         * @param text   the text-based information for the Announcement
         * @param source the source for the (optional) image in the format R.id.XYZ
         * @param date   the date of the announcement
         */

        public Announcement(String title, String text, int source, Date date) {
            this.title = title;
            this.text = text;
            imageSource = source;
            announcementDate = date;
        }

        /**
         * Instantiates a new Announcement with a title, text, and a date.
         * This also will assign imageSource to Integer.MIN_VALUE so it will
         * be something that we can check for and that won't be used automatically by accident.
         *
         * @param title  the announcement's title
         * @param text   the text-based information for the Announcement
         * @param date   the date of the announcement
         */

        public Announcement(String title, String text, Date date) {
            this.title = title;
            this.text = text;
            imageSource = Integer.MIN_VALUE;
            announcementDate = date;
        }

        /**
         * Instantiates a new Announcement with text, an image, and a date.
         *
         * @param text   the text-based information for the Announcement
         * @param source the source for the (optional) image in the format R.id.XYZ
         * @param date   the date of the announcement
         */

        public Announcement(String text, int source, Date date) {
            this.text = text;
            imageSource = source;
            announcementDate = date;
        }

        /**
         * Instantiates a new Announcement with just the text and a date.
         * This also will assign imageSource to Integer.MIN_VALUE so it will
         * be something that we can check for and that won't be used automatically by accident.
         * @param text the text-based information for the Announcement
         * @param date the date of the announcement
         */
        public Announcement(String text, Date date) {
            this.text = text;
            announcementDate = date;
            imageSource = Integer.MIN_VALUE;
        }

        /**
         * Instantiates a new Announcement with just an image and a date.
         *
         * @param source the source for the (optional) image in the format R.id.XYZ
         * @param date   the date of the announcement
         */
        public Announcement(int source, Date date) {
            imageSource = source;
            announcementDate = date;
        }

        /**
         * Instantiates a new Announcement with just an image. The date is
         * automatically assigned to the date and time of the method call.
         *
         * @param source the source for the (optional) image in the format R.id.XYZ
         */
        public Announcement(int source) {
            imageSource = source;
            announcementDate = new Date();
        }

        /**
         * Instantiates a new Announcement with just text. The date is
         * automatically assigned to the date and time of the method call.
         * This also will assign imageSource to Integer.MIN_VALUE so it will
         * be something that we can check for and that won't be used automatically by accident.
         * @param text the text for the Announcement.
         */
        public Announcement(String text) {
            this.text = text;
            imageSource = Integer.MIN_VALUE;
            announcementDate = new Date();
        }

        /**
         * Gets announcement date.
         *
         * @return the announcement date
         */
        public Date getAnnouncementDate() {
            return announcementDate;
        }

        /**
         * Sets announcement date.
         *
         * @param announcementDate the new announcement date
         */
        public void setAnnouncementDate(Date announcementDate) {
            this.announcementDate = announcementDate;
        }

        /**
         * Gets image source.
         *
         * @return the Announcement's image source
         */
        public int getImageSource() {
            return imageSource;
        }

        /**
         * Sets image source.
         *
         * @param imageSource the new image source in the form R.id.XYZ
         */
        public void setImageSource(int imageSource) {
            this.imageSource = imageSource;
        }

        /**
         * Gets text.
         *
         * @return the Announcement's text
         */
        public String getText() {
            return text;
        }

        /**
         * Sets text.
         *
         * @param text the new text value
         */
        public void setText(String text) {
            this.text = text;
        }

        /**
         * Gets title.
         *
         * @return the Announcement's title
         */
        public String getTitle() {
            return title;
        }

        /**
         * Sets title.
         *
         * @param title the new Announcement title
         */
        public void setTitle(String title) {
            this.title = title;
        }

        @Override
        public boolean equals(Object obj) {
            // If obj is null, return false
            if (obj == null) {
                return false;
            }

            // clazz.isAssignableFrom(Foo.class) returns true if the
            // clazz object is a superclass or superinterface of Foo
            if (!Announcement.class.isAssignableFrom(obj.getClass())) {
                return false;
            }

            // Check to see if all necessary variables are equal or not
            final Announcement objPerson = (Announcement) obj;
            if ((this.announcementDate == null) ? (objPerson.announcementDate != null) : !this.announcementDate.equals(objPerson.announcementDate)) {
                return false;
            }
            if (this.imageSource != objPerson.imageSource) {
                return false;
            }
            if ((this.text == null) ? (objPerson.text != null) : !this.text.equals(objPerson.text)) {
                return false;
            }
            if ((this.title == null) ? (objPerson.title != null) : !this.title.equals(objPerson.title)) {
                return false;
            }
            return true;
        }

        /**
         * Converts Announcement to String form in the format
         * Announcement: ANNOUNCEMENT_TITLE, ANNOUNCEMENT_TEXT, ANNOUNCEMENT_DATE, ANNOUNCEMENT_IMAGE_SOURCE
         */
        @Override
        public String toString() {
            return "Announcement: " + title + ", " + text + ", " + announcementDate.toString() + ", " + imageSource;
        }

        /**
         * This method creates a substring from the announcement's text to be used as a intro of
         * sorts. Basically, this generated String can be used to display on each CardView when
         * the CardView isn't expanded. Upon expansion, the CardView will display the full text
         * of the announcement.
         *
         * @return a substring of the announcement
         */
        public String generateIntro() {
            Log.i(TAG, "generateIntro() called!");
            if (text.length() == 0) {
                return "...";
            } else {
                // Ensure that the text is long to generate an 80-character substring
                if (text.length() >= 80) {
                    String toReturn = text.substring(0, 80) + "...";
                    // Log.i(TAG, "Returning: " + toReturn + "\n for String: " + text);
                    return toReturn;
                } else {
                    // Log.i(TAG, "Returning full String for String: \n" + text);
                    return text; // The text is already short enough.
                }
            }
        }
    }
}

如果我把它正确比尝试将其放入populateData()

if(data!=null) data.clear();

暂无
暂无

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

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