检测到Recyclerview不一致。视图持有者适配器positionViewHolder

时间:2017-10-18 19:55:50

标签: android android-recyclerview recycler-adapter

我的应用程序现在正在使用两个列表。一个是对象列表,另一个是同一对象列表。我的Activity中有一个方法,它将Object列表转换为对象列表。我的适配器通过这个调用传递列表列表:

mAdapter.swap(transformList(pipe.mList));

方法swap负责将转换后的列表传递给我的适配器:

public void swap(List<List<Feed>> feedList) {

    if (finallist == null) {
      finallist = new ArrayList<>();
    }
    finallist.clear();
    finallist.addAll(feedList);
  }

转换会检查一行中是否有图像并将所有图像放在一个列表中(我正在尝试实现类似于WhatsApp图像分组的内容)。所以我有一堆消息,它们可以是短信,​​文件,图像等。如果是最后一封,我将它们分组到一个列表中。

让我以一个场景为例:

我原始数组中有四张图片和一条短信息。转换将所有四个图像放入一个对象列表中,并将另一个对象列表中的文本消息放入其中(两者都插入到我的列表列表中)。

我想到了两种处理这种转换的方法:1 - 在Adapter内部进行,2 - 在我的Activity中执行并将修改后的列表传递给Adapter。这些解决方案中的每一个都产生了不同的问题。

按照1中的步骤,我能够以我想要的方式显示所有内容。分组工作得很好!问题是如果原始数组的长度等于30,并且转换后的数组的长度减少到12. RecyclerView会将所有剩余的18个项目显示为空状态(因此在转换后它不是&# 39;正确处理删除物品。)

按照2中的步骤,我无法显示所有内容。只是我的数组的第一个元素。我会收到IndexOutOfBoundsException in RecyclerView happens java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder错误消息。但我无法确定问题所在。我在这里查了很多问题,但没有一个能帮助我。

这是我的Adapter类:

public class FeedAdapter extends BaseSkeletonAdapter<Feed> implements FeedHolder.FeedHolderListener{
  private static final int HOLDER_COMMENT = 1;
  private static final int HOLDER_IMAGE = 2;
  private static final int HOLDER_FILE = 3;
  private static final int HOLDER_AUDIO = 4;
  private static final int HOLDER_MARKER = 5;
  private static final int HOLDER_EMPTY = 6;
  private static final int HOLDER_GROUP = 7;

  private final FeedItemListener mListener;
  private final int mAvatarSize;
  private final String mUserId;
  private final int mPictureSize;
  private final int mSkeletonColor;
  public static List<List<Feed>> finallist;

  public FeedAdapter(FeedItemListener listener, String userId, int avatarSize, int pictureSize, int skeletonColor) {
    super(2);
    mListener = listener;
    mUserId = userId;
    mAvatarSize = avatarSize;
    mPictureSize = pictureSize;
    mSkeletonColor = skeletonColor;
  }

  @Override
  public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    switch (viewType){
      case HOLDER_COMMENT:
      case HOLDER_IMAGE:
      case HOLDER_FILE:
      case HOLDER_MARKER:
      case HOLDER_AUDIO:
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_feed, parent, false);
        return new FeedHolder(view, this, mPictureSize);
      case HOLDER_GROUP:
        System.out.println("É um grupo!!!");
        View viewGroup = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_feed_group,parent,false);
        return new FeedHolder(viewGroup, this, mPictureSize);
      case HOLDER_EMPTY:
      default:
        View empty = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_empty, parent, false);
        return new EmptyPlaceholderViewHolder(empty, R.string.placeholder_feed_empty_title, R.string.placeholder_feed_empty_description, R.drawable.ic_feed_placeholder);
    }
  }

  @Override
  protected void onBind(RecyclerView.ViewHolder holder, int position) {

    if(!(holder instanceof EmptyPlaceholderViewHolder)){
      //Feed feed = finallist.get(position).get(0);

      if (holder instanceof FeedHolder) {
        if (position < finallist.size()) {
          if (mUserId.equals(finallist.get(position).get(0).getCreatedById())) {
            ((FeedHolder) holder).onBind(finallist.get(position), mUserId, mAvatarSize);
          } else {
            ((FeedHolder) holder).onBind(finallist.get(position), mUserId, mAvatarSize);
          }
        }
      }
    }
  }

  @Override
  protected void onBind(RecyclerView.ViewHolder holder, int position, List<Object> payloads) {

    if (payloads.isEmpty()) {
      onBindViewHolder(holder, position);
    }else {

      if (holder instanceof FeedHolder) {
        ((FeedHolder) holder).onBind(finallist.get(position), payloads, mUserId, mAvatarSize);
      }
    }
  }

  @Override
  protected void setHolderSkeleton(RecyclerView.ViewHolder holder) {
    if (holder instanceof FeedHolder) {
      ((FeedHolder) holder).setHolderSkeleton(R.drawable.rounded_skeleton, mSkeletonColor);
    }
  }

  @Override
  protected void clearHolderSkeleton(RecyclerView.ViewHolder holder) {
    if (holder instanceof FeedHolder) {
      ((FeedHolder) holder).clearHolderSkeleton();
    }
  }

  @Override
  public int getItemViewType(int position) {
    if(mSkeletonMode){
      return HOLDER_COMMENT;
    } if (finallist != null && finallist.size() > 0 && position >= 0 && position < finallist.size()) {
      System.out.println("Tamanho total: " + finallist.size());
      if (finallist.get(position).size() > 1) {
        System.out.println("Tamanho do grupo: " + finallist.get(position).size());
        return HOLDER_GROUP;
      } else {
      Feed feed = finallist.get(position).get(0);
      if (feed != null) {
          String type = feed.getFeedType();
          if (type != null) {
            switch (type) {
              case FEED_IMAGE:
                return HOLDER_IMAGE;
              case FEED_AUDIO:
                return HOLDER_AUDIO;
              case FEED_FILE:
                return HOLDER_FILE;
              case FEED_MARKER:
                return HOLDER_MARKER;
              case FEED_COMMENT:
              default:
                return HOLDER_COMMENT;
            }
          }
        }
      }
      return HOLDER_COMMENT;
    }else {
      System.out.println("Tá vazia!");
      return HOLDER_EMPTY;
    }
  }

  public List<Feed> getItems() {
    return returnList(finallist);
  }

  public List<List<Feed>> getListItems() {
    return finallist;
  }

  public void swap(List<List<Feed>> feedList) {

    if (finallist == null) {
      finallist = new ArrayList<>();
    }
    finallist.clear();
    finallist.addAll(feedList);
  }

  @Override
  public void toggleLike(final int pos){
    if(mListener != null && pos >= 0 && pos < finallist.size()){
      mListener.toggleLike(finallist.get(pos).get(0));
    }
  }

  @Override
  public void onLongClick(final int pos, final View v) {
    if (mListener != null && pos >= 0 && pos < finallist.size()) {
      mListener.onLongClick(finallist.get(pos).get(0), v);
    }
  }

  @Override
  public int onAudioActionClicked(final int pos, final int progress) {
    if (mListener != null) {
      return mListener.onAudioActionClicked(pos, finallist.get(pos).get(0), progress);
    }else {
      return 0;
    }
  }

  @Override
  public void onClick(int pos) {
    if (finallist!=null && pos >= 0 && pos<finallist.size()) {
      Feed feed = finallist.get(pos).get(0);
      if (feed != null && mListener != null) {
        mListener.onClick(feed);
      }
    }
  }

  public interface FeedItemListener {
    void toggleLike(@NonNull Feed feed);
    void onLongClick(@NonNull Feed feed, @NonNull View v);
    void onClick(@NonNull Feed feed);
    int onAudioActionClicked(int pos, @NonNull Feed feed, final int progress);
  }

  private void transformList(List<Feed> mItems) {
    finallist = new ArrayList<>();
    for (int i = 0; i< mItems.size();i++) {
      List<Feed> feed = new ArrayList<>();
      feed.add(mItems.get(i));
      finallist.add(feed);
    }

    int j = 0;
    List<String> list = new ArrayList<>();
    List<Integer> indexList = new ArrayList<>();
    //System.out.println("Tamanho: " + mItems.size());
    for (int i = 0; i < mItems.size(); i++) {

      if (!mItems.get(i).getFeedType().equals("filePicture")) {
        if (j >= 4) {
          String temp = "";
          for (int k = 0; k < j; k++) {
            temp = temp +  "->" + Integer.toString(i - (k+1));
            if (k != 0) {
              finallist.get(i - 1).add(finallist.get(i - (k + 1)).get(0));
              indexList.add(i - (k+1));
            }

          }
          list.add(temp);
        }
        j = 0;
      } else {
        j = j + 1;
      }
      if (i == mItems.size()-1) {
        //System.out.println("Imagem por ultimo!");
        if (j >= 4) {
          //System.out.println("Grupo vem por ultimo!");
          String temp = "";
          for (int k = 0; k < j; k++) {
            temp = temp +  "->" + Integer.toString(i - (k));
            if (k != 0) {
              finallist.get(i).add(finallist.get(i - (k)).get(0));
              indexList.add(i - (k));
            }
          }
          list.add(temp);
        }
      }
    }
    Collections.sort(indexList);
    int aux = 0;
    for (int i = 0; i < indexList.size();i++) {
      //System.out.println("Valor da posição:  " + indexList.get(i)+ "\nTipo: "+ finallist.get((indexList.get(i).intValue())+aux).get(0).getFeedType()
      //        +"\nValor da posição + i: " + (indexList.get(i)+aux) + "\nAux: " + aux);
      finallist.remove((indexList.get(i).intValue())+aux);
      //notifyItemRangeRemoved(0, finallist.size());
      //notifyDataSetChanged();
      aux = aux - 1;
    }

    /*for (int i = 0; i< finallist.size(); i++){
      if (finallist.get(i).size() > 1) {
        System.out.println("groupImage: " + finallist.get(i).size());
      } else {
        System.out.println(finallist.get(i).get(0).getFeedType());
      }
    }*/
    //returnList(finallist);
    notifyItemRangeRemoved(0, returnList(finallist).size() - finallist.size() - 1);
    //notifyItemRangeInserted(0, finallist.size());
  }

  public List<Feed> returnList(List<List<Feed>> lists) {
    List<Feed> list = new ArrayList<>();
    if (lists != null) {
      for (int i = 0; i < lists.size(); i++) {
        if (lists.get(i).size() > 1) {
          for (int j = 0; j < lists.get(i).size(); j++) {
            list.add(lists.get(i).get(j));
          }
        } else {
          list.add(lists.get(i).get(0));
        }
      }
      System.out.println("Tamanho de list: " + list.size());
    }

    return list;
  }

}

这是我的活动:

public abstract class FeedActivity extends UltraBaseActivity implements FeedAdapter.FeedItemListener, AudioManager.OnAudioFocusChangeListener, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {
  private static final String EXTRA_PROJECT_ID = "extra_project_id";
  private static final String EXTRA_PROJECT_NAME = "extra_project_name";
  private static final String EXTRA_RESOURCE_ID = "extra_resource_id";
  private static final String EXTRA_RESOURCE_NAME = "extra_resource_name";
  private static final String EXTRA_RESOURCE_KIND = "extra_resource_kind";

  @BindView(R.id.swipeLayout) SwipeRefreshLayout mRefreshLayout;
  @BindView(R.id.recyclerView) RecyclerView mRecyclerView;
  @BindView(R.id.feedCreateFragment) View mFeedCreateLayout;
  @BindView(R.id.mic) ImageView mMicView;

  private WeakReference<FeedCreateFragment> mFeedCreateFragment;
  protected FeedViewModel mViewModel;
  protected FeedAdapter mAdapter;
  private Feed mLongClick;
  private Toolbar mToolbar;
  protected int mScrollTo = -1;
  protected WrapContentLinearLayoutManager mLayoutManager;

  private String mPlayingFeedId;
  private int mPlayingPos;
  private int mActionResourcePause = R.drawable.ic_pause_black_24dp;
  private int mActionResourcePlay = R.drawable.ic_play_black_24dp;
  private MediaPlayer mPlayer;
  private AudioManager mAudioManager;
  protected Handler mAudioHandler;
  protected Runnable mUpdateAudioHolderRunnable = new Runnable() {
    @Override
    public void run() {
      try {
        if (mPlayer != null && mPlayer.isPlaying()) {
          notifyAdapterAudioUpdate(mPlayer.getCurrentPosition(), mActionResourcePause);
          mAudioHandler.postDelayed(this, 100);
        } else {
          mAudioHandler.removeCallbacks(mUpdateAudioHolderRunnable);
        }
      } catch (IllegalStateException e){
        MyLog.e(TAG, "Error while updating seed bar", e);
        mAudioHandler.removeCallbacks(mUpdateAudioHolderRunnable);
      }
    }
  };

  public static void start(@NonNull Context context, @NonNull String projectId, @NonNull  String projectName, @NonNull String resourceId, @NonNull String resourceName, @NonNull String resourceKind){
    Intent intent = setIntent(context, projectId, projectName, resourceId, resourceName, resourceKind);
    if (intent == null) return;
    context.startActivity(intent);
  }

  public static void startForResult(@NonNull Fragment fragment, @NonNull String projectId, @NonNull  String projectName, @NonNull String resourceId, @NonNull String resourceName, @NonNull String resourceKind){
    Intent intent = setIntent(fragment.getContext(), projectId, projectName, resourceId, resourceName, resourceKind);
    if (intent == null) return;
    fragment.startActivityForResult(intent, Constants.Intents.INTENT_REQUEST_VIEW_FEED);
  }

  @Nullable
  protected static Intent setIntent(@NonNull Context context, @NonNull String projectId, @NonNull String projectName, @NonNull String resourceId, @NonNull String resourceName, @NonNull String resourceKind) {
    Intent intent;
    if (resourceKind.equals(Task.ROUTE)) {
      intent = new Intent(context, FeedTaskActivity.class);
    }else if(resourceKind.equals(Chat.ROUTE)){
      intent = new Intent(context, FeedChatActivity.class);
    } else {
      MyLog.e(TAG, "Error invalid resource Kind - " + resourceKind);
      return null;
    }
    intent.putExtra(EXTRA_PROJECT_ID, projectId);
    intent.putExtra(EXTRA_PROJECT_NAME, projectName);
    intent.putExtra(EXTRA_RESOURCE_ID, resourceId);
    intent.putExtra(EXTRA_RESOURCE_NAME, resourceName);
    intent.putExtra(EXTRA_RESOURCE_KIND, resourceKind);
    return intent;
  }

  public FeedActivity() {
    super(R.layout.activity_feed);
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    isLogged();
    init(getIntent(), savedInstanceState);
    super.onCreate(savedInstanceState);
    initAdapter();

    mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
    mAudioHandler = new Handler();

    mViewModel.subscribe()
        .compose(this.<Resource<List<Feed>>>bindUntilEvent(ActivityEvent.DESTROY))
        .flatMap(flatMapDiffUtils())
        .subscribeOn(Schedulers.newThread())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(onNext(), onError(), onCompleted());
  }

  @NonNull
  private Func1<Resource<List<Feed>>, Observable<FeedViewModel.Pipe>> flatMapDiffUtils() {
    return new Func1<Resource<List<Feed>>, Observable<FeedViewModel.Pipe>>() {
      @Override
      public Observable<FeedViewModel.Pipe> call(Resource<List<Feed>> r) {
        if (mViewModel.hasResource()) {
          List<Feed> old = mAdapter.getItems();
          MyLog.i(TAG, "New length: " + (r.data != null ? r.data.size() : 0) + " - Old: " + old.size());
          final FeedDiffCallback cb = new FeedDiffCallback(old, r.data, mUser.getId());
          final DiffUtil.DiffResult result = DiffUtil.calculateDiff(cb);
          return Observable.just(new FeedViewModel.Pipe(r.data, result, r.status == Status.LOADING && (r.data ==null || r.data.size() == 0)));
        } else {
          MyLog.i(TAG, "Loading resource from server");
          return Observable.empty();
        }
      }
    };
  }

  private Action1<? super FeedViewModel.Pipe> onNext() {
    return new Action1<FeedViewModel.Pipe>() {
      @Override
      public void call(FeedViewModel.Pipe pipe) {
        if (pipe != null) {
          initFeedFragment();

          mRefreshLayout.setRefreshing(false);
          pipe.mDiffResult.dispatchUpdatesTo(mAdapter);
          mAdapter.setSkeletonMode(pipe.mSkeletonMode);
          //List<List<Feed>> list = new ArrayList<>();
          //list = tranformList(pipe.mList);
          mAdapter.swap(transformList(pipe.mList));
          System.out.println("Tamanho desse troço: " + transformList(pipe.mList).size());
          //mAdapter.notifyDataSetChanged();
          //mAdapter.notifyItemRangeRemoved(0, pipe.mList.size());

          if (mScrollTo == -1) {
            mRecyclerView.scrollToPosition(mAdapter.getItemCount()-1);
          }else {
            mRecyclerView.scrollToPosition(mScrollTo);
          }

          updateMenuOptions();
          showLoading(false);
        }
      }
    };
  }

  protected Action0 onCompleted() {
    return new Action0() {
      @Override
      public void call() {
        MyLog.i(TAG, "Finishing feed activity");
        finish();
      }
    };
  }

  protected Action1<Throwable> onError() {
    return new Action1<Throwable>() {
      @Override
      public void call(Throwable throwable) {
        MyLog.e(TAG, "Error", throwable);
      }
    };
  }

  @Override
  protected void initToolbar() {
    mToolbar = (Toolbar) findViewById(R.id.toolbar);
    if(mToolbar !=null) {
      if (mViewModel != null) {
        mToolbar.setTitle(mViewModel.getProjectName());
        mToolbar.setSubtitle(mViewModel.getResourceName());
      }
      mToolbar.setSubtitleTextColor(ContextCompat.getColor(this, R.color.palette_black));
      mToolbar.setNavigationIcon(R.drawable.ic_action_back_black);
    }
    setSupportActionBar(mToolbar);

    if (mToolbar != null) {
      mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          onBackPressed();
        }
      });
    }
  }

  protected void updateTitle(){
    if(mToolbar !=null && mViewModel != null) {
      mToolbar.setTitle(mViewModel.getProjectName());
      mToolbar.setSubtitle(mViewModel.getResourceName());
    }
  }

  @Override
  protected void userUpdated(User user) { }

  private void init(Intent i, Bundle b){
    if (i != null) {
      initViewModel(
          i.getStringExtra(EXTRA_PROJECT_ID),
          i.getStringExtra(EXTRA_PROJECT_NAME),
          i.getStringExtra(EXTRA_RESOURCE_ID),
          i.getStringExtra(EXTRA_RESOURCE_NAME),
          i.getStringExtra(EXTRA_RESOURCE_KIND));
    }else if(b != null){
      initViewModel(
          b.getString(EXTRA_PROJECT_ID),
          b.getString(EXTRA_PROJECT_NAME),
          b.getString(EXTRA_RESOURCE_ID),
          b.getString(EXTRA_RESOURCE_NAME),
          b.getString(EXTRA_RESOURCE_KIND));
    }else {
      MyLog.i(TAG, "Error while initializing view model");
      finish();
    }
  }

  @Override
  protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putString(EXTRA_PROJECT_ID, mViewModel.getProjectId());
    outState.putString(EXTRA_PROJECT_NAME, mViewModel.getProjectName());
    outState.putString(EXTRA_RESOURCE_ID, mViewModel.getResourceId());
    outState.putString(EXTRA_RESOURCE_NAME, mViewModel.getResourceName());
    outState.putString(EXTRA_RESOURCE_KIND, mViewModel.getResourceKind());
  }

  private void initAdapter(){
    mAdapter = new FeedAdapter(
        this,
        mUser.getId(),
        getResources().getDimensionPixelSize(R.dimen.task_avatar_size),
        (int) (AndroidUtils.getScreenWidth(this) * 0.6),
        ContextCompat.getColor(this, R.color.bg_skeleton)
    );
    mRefreshLayout.setColorSchemeResources(R.color.yellow, android.R.color.darker_gray, R.color.yellow_edit_note, android.R.color.background_dark);
    mRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
      @Override
      public void onRefresh() {
        mScrollTo = -1;
        mViewModel.reload();
      }
    });
    mLayoutManager = new WrapContentLinearLayoutManager(this);
    mRecyclerView.setLayoutManager(mLayoutManager);
    mRecyclerView.setAdapter(mAdapter);
  }

  private void initFeedFragment(){
    if(!(mFeedCreateFragment != null && mFeedCreateFragment.get() != null && getSupportFragmentManager().findFragmentById(R.id.feedCreateFragment) instanceof FeedCreateFragment)){
      mFeedCreateFragment = new WeakReference<>(FeedCreateFragment.newInstance(mViewModel.getProjectId(), mViewModel.getResourceId(), mViewModel.getResourceKind(), mViewModel.getResourceUsers()));
      getSupportFragmentManager()
          .beginTransaction()
          .add(R.id.feedCreateFragment, mFeedCreateFragment.get())
          .commitAllowingStateLoss();

      if (mViewModel.canCreateFeed()) {
        mFeedCreateLayout.setVisibility(View.VISIBLE);
      }else {
        mFeedCreateLayout.setVisibility(View.GONE);
      }
    }
  }

  protected abstract void initViewModel(String projectId, String projectName, String resourceId, String resourceName, String resourceKind);
  protected abstract void updateMenuOptions();

}

这是错误的一部分(文本达到最大大小):

10-18 17:29:14.702 23722-23722/com.construct.test E/WrapContentLinearLayoutManager: IndexOutOfBoundsException in RecyclerView happens
                                                                                    java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{615b449 position=3 id=-1, oldPos=0, pLpos:0 scrap [attachedScrap] tmpDetached no parent}
                                                                                        at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:5297)
                                                                                        at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5479)
                                                                                        at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5440)
                                                                                        at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5436)
                                                                                        at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2224)
                                                                                        at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1551)
                                                                                        at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1511)
                                                                                        at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:595)
                                                                                        at com.construct.v2.adapters.WrapContentLinearLayoutManager.onLayoutChildren(WrapContentLinearLayoutManager.java:20)

1 个答案:

答案 0 :(得分:0)

我的问题是由我BaseSkeletonAdapter<Feed>扩展的FeedAdapter引起的。 BaseSkeletonAdapter使用的是原始列表,而不是我必须使用的列表列表。所以我创建了一个新的类SkeletonAdapterFeed,它基本上等于前一个,但是新的类正在接收列表列表而不仅仅是列表。 我知道这听起来有点令人困惑。我对我现在正在工作的项目没有充分的理解,所以这就是为什么我不了解课程的所有内容等。