RecyclerView.notfiyDataSetChanged () не работает в альбомной разметке - PullRequest
0 голосов
/ 22 февраля 2019

Я следую вместе с CodeLab для разработчиков Android ( ссылка ).При переворачивании устройства в альбомную ориентацию я не могу получить notifyDataSetChanged () или notifyItemRangeInserted () для обновления моего адаптера.Я реализовал Parcelable на своем объекте, и он корректно восстанавливает мой ArrayList после извлечения его из пакета onSaveInstanceState.Но когда я отлаживаю в этих методах RecyclerView, адаптер не заполняется ArrayList.Кроме того, я заметил, что в ландшафтном режиме FAB, который сбрасывает ArrayList на 11 элементов по умолчанию, также не работает, когда устройство переворачивается в альбомную.Это заставляет меня поверить, что что-то может быть не в том классе.

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);
}
}

SportsAdapter

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);
        }
    }
}

Спортивный класс

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);
    }
}

1 Ответ

0 голосов
/ 22 февраля 2019

Вы не обновляете адаптер должным образом.Когда пришло время восстановить список из сохраненного Пакета в методе restoreData (), вы делаете:

mSportsData = savedBundle.getParcelableArrayList(SPORT_ARRAY);

Это просто изменяет ссылку mSportsData, чтобы указывать на список из Пакета.Тем не менее, адаптер RecyclerView будет основан на пустом списке, который вы изначально создали в onCreate () с помощью:

mSportsData = new ArrayList<>();

Вот почему ваш FAB также не работает, потому что он вызывает initializeData () для списка, который не являетсяиспользуется адаптером.

Правильный способ обновить адаптер - поместить объекты из списка в Bundle в mSportsData:

mSportsData.addAll(savedBundle.getParcelableArrayList(SPORT_ARRAY));
...