RecyclerView.notfiyDataSetChanged() 在横向布局中不起作用
RecyclerView.notfiyDataSetChanged() not working in landscape layout
我正在关注 Android Developer CodeLab (link)。将设备翻转为横向时,我无法获取 notifyDataSetChanged() 或 notifyItemRangeInserted() 来更新我的适配器。我在我的对象上实现了 Parcelable,它在从 onSaveInstanceState 包中拉出后正确地恢复了我的 ArrayList。但是当我调试这些 RecyclerView 方法时,适配器没有填充 ArrayList。此外,我注意到在横向模式下,将 ArrayList 重置为默认 11 项的 FAB 在设备翻转为横向模式时也不起作用。这让我相信可能出了什么问题 class.
MainActivity
public class MainActivity extends AppCompatActivity {
// Tag for key in instance save bundle
static final String SPORT_ARRAY = "Sport Array";
// Member variables
private RecyclerView mRecyclerView;
private ArrayList<Sport> mSportsData;
private SportsAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize the RecyclerView
mRecyclerView = findViewById(R.id.recyclerView);
// Set the Layout Manager
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
// Initialize the ArrayList that will contain the data.
mSportsData = new ArrayList<>();
// Initialize the adapter and set it to the RecyclerView.
mAdapter = new SportsAdapter(this, mSportsData);
mRecyclerView.setAdapter(mAdapter);
// Get the data or restore it
if (savedInstanceState == null || !savedInstanceState.containsKey(SPORT_ARRAY)) {
initializeData();
} else {
restoreData(savedInstanceState);
}
// Helper class for creating swipe to dismiss and drag and drop functionality.
ItemTouchHelper helper = new ItemTouchHelper(
new ItemTouchHelper.SimpleCallback(ItemTouchHelper.LEFT |
ItemTouchHelper.RIGHT | ItemTouchHelper.DOWN | ItemTouchHelper.UP,
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
/**
* Defines the drag and drop functionality.
*
* @param recyclerView The RecyclerView that contains the list items
* @param viewHolder The SportsViewHolder that is being moved.
* @param target The SportsViewHolder that you are switching the original one
* with.
* @return true if the item was removed, false otherwise.
*/
@Override
public boolean onMove(@NonNull RecyclerView recyclerView,
@NonNull RecyclerView.ViewHolder viewHolder,
@NonNull RecyclerView.ViewHolder target) {
// Get the from and to positions.
int from = viewHolder.getAdapterPosition();
int to = target.getAdapterPosition();
// Swap the items and notify the adapter.
Collections.swap(mSportsData, from, to);
mAdapter.notifyItemMoved(from, to);
return true;
}
/**
* Defines the swipe to dismiss functionality.
* @param viewHolder The ViewHolder being swiped.
* @param direction The direction it is swiped in.
*/
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
mSportsData.remove(viewHolder.getAdapterPosition());
mAdapter.notifyItemRemoved(viewHolder.getAdapterPosition());
}
});
// Attach the helper to the RecyclerView.
helper.attachToRecyclerView(mRecyclerView);
}
/**
* Method for initializing the sports data from resources.
*/
private void initializeData() {
// Get the resources from the XML file
String[] sportsList = getResources().getStringArray(R.array.sports_titles);
String[] sportsInfo = getResources().getStringArray(R.array.sports_info);
String[] sportsDetail = getResources().getStringArray(R.array.sports_detail_text);
TypedArray sportsImageResources = getResources().obtainTypedArray(R.array.sports_images);
// Clear the existing data (to avoid duplication)
mSportsData.clear();
// Create the ArrayList of Sports objects with the titles and information about each sport.
for (int i = 0; i < sportsList.length; i++) {
mSportsData.add(new Sport(sportsList[i], sportsInfo[i],
sportsDetail[i], sportsImageResources.getResourceId(i, 0)));
}
// Recycle the sports image resource array.
sportsImageResources.recycle();
// Notify the adapter of the change.
mAdapter.notifyDataSetChanged();
}
private void restoreData(Bundle savedBundle) {
mSportsData.clear();
mSportsData = savedBundle.getParcelableArrayList(SPORT_ARRAY);
// Notify the adapter of inserted items
mAdapter.notifyItemRangeInserted(0, mSportsData.size());
}
/**
* onClick method for the FAB that resets the data.
* @param view The button view that was clicked.
*/
public void resetSports(View view) {
initializeData();
}
/**
* Save the instance state
* @param outState The current instance state
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putParcelableArrayList(SPORT_ARRAY, mSportsData);
super.onSaveInstanceState(outState);
}
}
运动适配器
public class SportsAdapter extends RecyclerView.Adapter<SportsAdapter.ViewHolder>{
// Member variables
private ArrayList<Sport> mSportsData;
private Context mContext;
/**
* Constructor that passes in the sports data and the context.
* @param context Context of the application.
* @param sportsData ArrayList containing the sports data.
*/
SportsAdapter(Context context, ArrayList<Sport> sportsData) {
this.mSportsData = sportsData;
this.mContext = context;
}
/**
* Required method for creating the ViewHolder objects.
* @param viewGroup The ViewGroup into which the View will be added after it is bound to an
* adapter position.
* @param i The view type of the new View.
* @return The newly created ViewHolder.
*/
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
return new ViewHolder(LayoutInflater.from(mContext).inflate(R.layout.list_item, viewGroup,
false));
}
/**
* Required method that binds the data to the ViewHolder.
* @param viewHolder The ViewHolder into which the data should be put.
* @param i The adapter position.
*/
@Override
public void onBindViewHolder(@NonNull SportsAdapter.ViewHolder viewHolder, int i) {
// Get the current sport.
Sport currentSport = mSportsData.get(i);
// Populate the TextViews with data.
viewHolder.bindTo(currentSport);
}
/**
* Required method for determining the size of the data set.
* @return Size of the data set.
*/
@Override
public int getItemCount() {
return mSportsData.size();
}
/**
* ViewHolder class that represents each row of data in the RecyclerVIew.
*/
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
// Member variables for the TextViews.
private TextView mTitleText;
private TextView mInfoText;
private ImageView mSportsImage;
/**
* Constructor for the ViewHolder, used in onCreateViewHolder().
* @param itemView The rootview of the list_item.xml layout file.
*/
ViewHolder(View itemView) {
super(itemView);
// Initialize the views
mTitleText = itemView.findViewById(R.id.title);
mInfoText = itemView.findViewById(R.id.subTitle);
mSportsImage = itemView.findViewById(R.id.sportsImage);
// Set the OnClickListener to the entire view.
itemView.setOnClickListener(this);
}
void bindTo(Sport currentSport) {
// Populate the TextViews with data.
mTitleText.setText(currentSport.getTitle());
mInfoText.setText(currentSport.getInfo());
Glide.with(mContext).load(currentSport.getImageResource()).into(mSportsImage);
}
/**
* Handle click to show DetailActivity.
*
* @param v The view that was clicked.
*/
@Override
public void onClick(View v) {
Sport currentSport = mSportsData.get(getAdapterPosition());
Intent detailIntent = new Intent(mContext, DetailActivity.class);
detailIntent.putExtra("title", currentSport.getTitle());
detailIntent.putExtra("detail", currentSport.getDetail());
detailIntent.putExtra("image_resource", currentSport.getImageResource());
mContext.startActivity(detailIntent);
}
}
}
运动Class
public class Sport implements Parcelable {
// Member variables representing the title and information about the sport.
private String mTitle;
private String mInfo;
private String mDetail;
private final int mImageResource;
/**
* Constructor for the Sport data model.
* @param title The name of the sport.
* @param info Information about the sport.
* @param detail Details about the sport (from Wikipedia).
* @param imageResource The image resource.
*/
Sport(String title, String info, String detail, int imageResource) {
this.mTitle = title;
this.mInfo = info;
this.mDetail = detail;
this.mImageResource = imageResource;
}
protected Sport(Parcel in) {
mTitle = in.readString();
mInfo = in.readString();
mDetail = in.readString();
mImageResource = in.readInt();
}
public static final Creator<Sport> CREATOR = new Creator<Sport>() {
@Override
public Sport createFromParcel(Parcel in) {
return new Sport(in);
}
@Override
public Sport[] newArray(int size) {
return new Sport[size];
}
};
/**
* Gets the title of the sport.
* @return The title of the sport.
*/
String getTitle() {
return mTitle;
}
/**
* Gets the info about the sport.
* @return The info about the sport.
*/
String getInfo() {
return mInfo;
}
/**
* Gets the details about the sport (from Wikipedia).
* @return The details about the sport.
*/
String getDetail() {
return mDetail;
}
/**
* Gets the image resource.
* @return The image resource.
*/
public int getImageResource() {
return mImageResource;
}
/**
* Describe the kinds of special objects contained in this Parcelable
* instance's marshaled representation. For example, if the object will
* include a file descriptor in the output of {@link #writeToParcel(Parcel, int)},
* the return value of this method must include the
* {@link #CONTENTS_FILE_DESCRIPTOR} bit.
*
* @return a bitmask indicating the set of special object types marshaled
* by this Parcelable object instance.
*/
@Override
public int describeContents() {
return 0;
}
/**
* Flatten this object in to a Parcel.
*
* @param dest The Parcel in which the object should be written.
* @param flags Additional flags about how the object should be written.
* May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mTitle);
dest.writeString(mInfo);
dest.writeString(mDetail);
dest.writeInt(mImageResource);
}
}
您没有正确更新适配器。当需要在 restoreData() 方法中从已保存的 Bundle 中恢复列表时,您可以执行以下操作:
mSportsData = savedBundle.getParcelableArrayList(SPORT_ARRAY);
这只是将 mSportsData 引用更改为指向 Bundle 中的列表。但是,RecyclerView 的适配器将基于您最初在 onCreate() 中创建的空列表:
mSportsData = new ArrayList<>();
这就是您的 FAB 也失败的原因,因为它在适配器未使用的列表上调用了 initializeData()。
更新适配器的正确方法是将列表中的对象放入 mSportsData 的 Bundle 中:
mSportsData.addAll(savedBundle.getParcelableArrayList(SPORT_ARRAY));
我正在关注 Android Developer CodeLab (link)。将设备翻转为横向时,我无法获取 notifyDataSetChanged() 或 notifyItemRangeInserted() 来更新我的适配器。我在我的对象上实现了 Parcelable,它在从 onSaveInstanceState 包中拉出后正确地恢复了我的 ArrayList。但是当我调试这些 RecyclerView 方法时,适配器没有填充 ArrayList。此外,我注意到在横向模式下,将 ArrayList 重置为默认 11 项的 FAB 在设备翻转为横向模式时也不起作用。这让我相信可能出了什么问题 class.
MainActivity
public class MainActivity extends AppCompatActivity {
// Tag for key in instance save bundle
static final String SPORT_ARRAY = "Sport Array";
// Member variables
private RecyclerView mRecyclerView;
private ArrayList<Sport> mSportsData;
private SportsAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize the RecyclerView
mRecyclerView = findViewById(R.id.recyclerView);
// Set the Layout Manager
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
// Initialize the ArrayList that will contain the data.
mSportsData = new ArrayList<>();
// Initialize the adapter and set it to the RecyclerView.
mAdapter = new SportsAdapter(this, mSportsData);
mRecyclerView.setAdapter(mAdapter);
// Get the data or restore it
if (savedInstanceState == null || !savedInstanceState.containsKey(SPORT_ARRAY)) {
initializeData();
} else {
restoreData(savedInstanceState);
}
// Helper class for creating swipe to dismiss and drag and drop functionality.
ItemTouchHelper helper = new ItemTouchHelper(
new ItemTouchHelper.SimpleCallback(ItemTouchHelper.LEFT |
ItemTouchHelper.RIGHT | ItemTouchHelper.DOWN | ItemTouchHelper.UP,
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
/**
* Defines the drag and drop functionality.
*
* @param recyclerView The RecyclerView that contains the list items
* @param viewHolder The SportsViewHolder that is being moved.
* @param target The SportsViewHolder that you are switching the original one
* with.
* @return true if the item was removed, false otherwise.
*/
@Override
public boolean onMove(@NonNull RecyclerView recyclerView,
@NonNull RecyclerView.ViewHolder viewHolder,
@NonNull RecyclerView.ViewHolder target) {
// Get the from and to positions.
int from = viewHolder.getAdapterPosition();
int to = target.getAdapterPosition();
// Swap the items and notify the adapter.
Collections.swap(mSportsData, from, to);
mAdapter.notifyItemMoved(from, to);
return true;
}
/**
* Defines the swipe to dismiss functionality.
* @param viewHolder The ViewHolder being swiped.
* @param direction The direction it is swiped in.
*/
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
mSportsData.remove(viewHolder.getAdapterPosition());
mAdapter.notifyItemRemoved(viewHolder.getAdapterPosition());
}
});
// Attach the helper to the RecyclerView.
helper.attachToRecyclerView(mRecyclerView);
}
/**
* Method for initializing the sports data from resources.
*/
private void initializeData() {
// Get the resources from the XML file
String[] sportsList = getResources().getStringArray(R.array.sports_titles);
String[] sportsInfo = getResources().getStringArray(R.array.sports_info);
String[] sportsDetail = getResources().getStringArray(R.array.sports_detail_text);
TypedArray sportsImageResources = getResources().obtainTypedArray(R.array.sports_images);
// Clear the existing data (to avoid duplication)
mSportsData.clear();
// Create the ArrayList of Sports objects with the titles and information about each sport.
for (int i = 0; i < sportsList.length; i++) {
mSportsData.add(new Sport(sportsList[i], sportsInfo[i],
sportsDetail[i], sportsImageResources.getResourceId(i, 0)));
}
// Recycle the sports image resource array.
sportsImageResources.recycle();
// Notify the adapter of the change.
mAdapter.notifyDataSetChanged();
}
private void restoreData(Bundle savedBundle) {
mSportsData.clear();
mSportsData = savedBundle.getParcelableArrayList(SPORT_ARRAY);
// Notify the adapter of inserted items
mAdapter.notifyItemRangeInserted(0, mSportsData.size());
}
/**
* onClick method for the FAB that resets the data.
* @param view The button view that was clicked.
*/
public void resetSports(View view) {
initializeData();
}
/**
* Save the instance state
* @param outState The current instance state
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putParcelableArrayList(SPORT_ARRAY, mSportsData);
super.onSaveInstanceState(outState);
}
}
运动适配器
public class SportsAdapter extends RecyclerView.Adapter<SportsAdapter.ViewHolder>{
// Member variables
private ArrayList<Sport> mSportsData;
private Context mContext;
/**
* Constructor that passes in the sports data and the context.
* @param context Context of the application.
* @param sportsData ArrayList containing the sports data.
*/
SportsAdapter(Context context, ArrayList<Sport> sportsData) {
this.mSportsData = sportsData;
this.mContext = context;
}
/**
* Required method for creating the ViewHolder objects.
* @param viewGroup The ViewGroup into which the View will be added after it is bound to an
* adapter position.
* @param i The view type of the new View.
* @return The newly created ViewHolder.
*/
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
return new ViewHolder(LayoutInflater.from(mContext).inflate(R.layout.list_item, viewGroup,
false));
}
/**
* Required method that binds the data to the ViewHolder.
* @param viewHolder The ViewHolder into which the data should be put.
* @param i The adapter position.
*/
@Override
public void onBindViewHolder(@NonNull SportsAdapter.ViewHolder viewHolder, int i) {
// Get the current sport.
Sport currentSport = mSportsData.get(i);
// Populate the TextViews with data.
viewHolder.bindTo(currentSport);
}
/**
* Required method for determining the size of the data set.
* @return Size of the data set.
*/
@Override
public int getItemCount() {
return mSportsData.size();
}
/**
* ViewHolder class that represents each row of data in the RecyclerVIew.
*/
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
// Member variables for the TextViews.
private TextView mTitleText;
private TextView mInfoText;
private ImageView mSportsImage;
/**
* Constructor for the ViewHolder, used in onCreateViewHolder().
* @param itemView The rootview of the list_item.xml layout file.
*/
ViewHolder(View itemView) {
super(itemView);
// Initialize the views
mTitleText = itemView.findViewById(R.id.title);
mInfoText = itemView.findViewById(R.id.subTitle);
mSportsImage = itemView.findViewById(R.id.sportsImage);
// Set the OnClickListener to the entire view.
itemView.setOnClickListener(this);
}
void bindTo(Sport currentSport) {
// Populate the TextViews with data.
mTitleText.setText(currentSport.getTitle());
mInfoText.setText(currentSport.getInfo());
Glide.with(mContext).load(currentSport.getImageResource()).into(mSportsImage);
}
/**
* Handle click to show DetailActivity.
*
* @param v The view that was clicked.
*/
@Override
public void onClick(View v) {
Sport currentSport = mSportsData.get(getAdapterPosition());
Intent detailIntent = new Intent(mContext, DetailActivity.class);
detailIntent.putExtra("title", currentSport.getTitle());
detailIntent.putExtra("detail", currentSport.getDetail());
detailIntent.putExtra("image_resource", currentSport.getImageResource());
mContext.startActivity(detailIntent);
}
}
}
运动Class
public class Sport implements Parcelable {
// Member variables representing the title and information about the sport.
private String mTitle;
private String mInfo;
private String mDetail;
private final int mImageResource;
/**
* Constructor for the Sport data model.
* @param title The name of the sport.
* @param info Information about the sport.
* @param detail Details about the sport (from Wikipedia).
* @param imageResource The image resource.
*/
Sport(String title, String info, String detail, int imageResource) {
this.mTitle = title;
this.mInfo = info;
this.mDetail = detail;
this.mImageResource = imageResource;
}
protected Sport(Parcel in) {
mTitle = in.readString();
mInfo = in.readString();
mDetail = in.readString();
mImageResource = in.readInt();
}
public static final Creator<Sport> CREATOR = new Creator<Sport>() {
@Override
public Sport createFromParcel(Parcel in) {
return new Sport(in);
}
@Override
public Sport[] newArray(int size) {
return new Sport[size];
}
};
/**
* Gets the title of the sport.
* @return The title of the sport.
*/
String getTitle() {
return mTitle;
}
/**
* Gets the info about the sport.
* @return The info about the sport.
*/
String getInfo() {
return mInfo;
}
/**
* Gets the details about the sport (from Wikipedia).
* @return The details about the sport.
*/
String getDetail() {
return mDetail;
}
/**
* Gets the image resource.
* @return The image resource.
*/
public int getImageResource() {
return mImageResource;
}
/**
* Describe the kinds of special objects contained in this Parcelable
* instance's marshaled representation. For example, if the object will
* include a file descriptor in the output of {@link #writeToParcel(Parcel, int)},
* the return value of this method must include the
* {@link #CONTENTS_FILE_DESCRIPTOR} bit.
*
* @return a bitmask indicating the set of special object types marshaled
* by this Parcelable object instance.
*/
@Override
public int describeContents() {
return 0;
}
/**
* Flatten this object in to a Parcel.
*
* @param dest The Parcel in which the object should be written.
* @param flags Additional flags about how the object should be written.
* May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mTitle);
dest.writeString(mInfo);
dest.writeString(mDetail);
dest.writeInt(mImageResource);
}
}
您没有正确更新适配器。当需要在 restoreData() 方法中从已保存的 Bundle 中恢复列表时,您可以执行以下操作:
mSportsData = savedBundle.getParcelableArrayList(SPORT_ARRAY);
这只是将 mSportsData 引用更改为指向 Bundle 中的列表。但是,RecyclerView 的适配器将基于您最初在 onCreate() 中创建的空列表:
mSportsData = new ArrayList<>();
这就是您的 FAB 也失败的原因,因为它在适配器未使用的列表上调用了 initializeData()。
更新适配器的正确方法是将列表中的对象放入 mSportsData 的 Bundle 中:
mSportsData.addAll(savedBundle.getParcelableArrayList(SPORT_ARRAY));