2016-02-27 102 views
1

我在TabLayout中使用帶有CardView的RecyclerView。如果我在滑動到另一個選項卡並返回第一個選項卡後上下滾動,RecyclerView將複製第一個選項卡上的卡片。我如何去防止重複?我將在帖子的評論中發佈pastebin鏈接到代碼的其餘部分。Android:使用RecyclerView和CardView重複項目 - 如何停止重複?

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. 
       } 
      } 
     } 
    } 
} 
+0

MainActi vity.java http://pastebin.com/YsiL7yfn card_item.xml http://pastebin.com/wxFni7vq – Scusemua

回答

0

,如果我得到了它正確不是試圖把它變成populateData()

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

修復它 - 非常感謝你! :-)我不確定populateData()是否可能是罪魁禍首,但是在實現if(data!= null)data.clear();我應該抓住這個,這是有道理的。謝謝你,小夥伴。 – Scusemua

+0

快樂......結束......接受它作爲答案,如果它有幫助。 :) –

+0

我接受它,只要五分鐘的時間,我不能接受它作爲答案,就結束了。再次感謝你! :) – Scusemua