我在TabLayout中使用带有CardView的RecyclerView。如果我在滑动到另一个选项卡并返回第一个选项卡后向上和向下滚动,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.
}
}
}
}
}
答案 0 :(得分:0)
如果我说得对,那就试着把它放进populateData()
if(data!=null) data.clear();