2016-05-24 4 views
0

Я начал использовать Realm для одного из моих проектов некоторое время назад и при попытке обновить область до версии 0.89.0 или выше, мое поведение RecyclerView полностью разваливается. Я пытался изолировать проблему и сводилось к этому образцу: https://bitbucket.org/pr-shadoko/realmrecyclerviewtest Вот основные классы: Активность:Обновление Realm до 0.89.0 breaks RecyclerView

public class MainActivity extends AppCompatActivity { 
    ItemTouchHelper itemTouchHelper; 
    RecyclerView recyclerView; 
    TagsAdapter adapter; 
    Realm realm; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     RealmConfiguration config = new RealmConfiguration.Builder(this).build(); 
     Realm.setDefaultConfiguration(config); 
     realm = Realm.getDefaultInstance(); 

     adapter = new TagsAdapter(realm, new OnDragStartListener() { 
      @Override 
      public void onDragStart(RecyclerView.ViewHolder viewHolder) { 
       itemTouchHelper.startDrag(viewHolder); 
      } 
     }); 
     itemTouchHelper = new ItemTouchHelper(new TagTouchHelperCallback(adapter)); 
     recyclerView = (RecyclerView) findViewById(R.id.rv); 
     recyclerView.setAdapter(adapter); 
     itemTouchHelper.attachToRecyclerView(recyclerView); 
    } 

    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
     super.onCreateOptionsMenu(menu); 
     getMenuInflater().inflate(R.menu.main, menu); 
     return true; 
    } 

    @Override 
    public boolean onOptionsItemSelected(MenuItem item) { 
     switch(item.getItemId()) { 
      case R.id.action_add: 
       Tag tag = new Tag(); 
       tag.setTag("#tag" + tag.getTagId()); 
       realm.beginTransaction(); 
       realm.copyToRealmOrUpdate(tag); 
       realm.commitTransaction(); 
       adapter.notifyItemInserted(1); 
      default: 
       return super.onOptionsItemSelected(item); 
     } 
    } 
} 

RecyclerView адаптер:

public class TagsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements ItemTouchHelperAdapter { 
    private static final int TYPE_TAG = 0; 
    private static final int TYPE_HEADER = 1; 

    private Realm realm; 
    private RealmResults<Tag> tags; 
    private OnDragStartListener dragStartListener; 

    public TagsAdapter(Realm realm, OnDragStartListener dragStartListener) { 
     this.realm = realm; 
     this.tags = realm.where(Tag.class).findAllSorted("order", Sort.DESCENDING); 
     this.dragStartListener = dragStartListener; 

     setHasStableIds(true); 
    } 

    @Override 
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
     switch(viewType) { 
      case TYPE_TAG: 
       View taskView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false); 
       return new TagViewHolder(taskView); 
      case TYPE_HEADER: 
       View headerView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false); 
       return new HeaderViewHolder(headerView); 
      default: 
       return null; 
     } 
    } 

    @Override 
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, int adapterPosition) { 
     switch(holder.getItemViewType()) { 
      case TYPE_TAG: 
       onBindViewHolderTag((TagViewHolder) holder, adapterPosition); 
       break; 
      case TYPE_HEADER: 
       onBindViewHolderHeader((HeaderViewHolder) holder, adapterPosition); 
       break; 
      default: 
     } 
    } 

    private void onBindViewHolderTag(final TagViewHolder holder, final int adapterPosition) { 
     final Tag tag = tags.get(getDataSetPosition(adapterPosition)); 
     holder.title.setText(tag.getTag()); 
     holder.itemView.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       Log.d("TAGS", "item clicked: id=" + tag.getTagId() + " ; adapterPosition=" + adapterPosition + " ; datasetPosition=" + getDataSetPosition(adapterPosition)); 
      } 
     }); 
     holder.itemView.setOnTouchListener(new View.OnTouchListener() { 
      @Override 
      public boolean onTouch(View v, MotionEvent event) { 
       if(MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) { 
        dragStartListener.onDragStart(holder); 
       } 
       return false; 
      } 
     }); 
    } 

    private void onBindViewHolderHeader(HeaderViewHolder holder, int adapterPosition) { 
     holder.header.setText("TITLE"); 
    } 

    private int getDataSetPosition(int adapterPosition) { 
     switch(getItemViewType(adapterPosition)) { 
      case TYPE_TAG: 
       return adapterPosition - 1; 
      case TYPE_HEADER: 
      default: 
       return RecyclerView.NO_POSITION; 
     } 
    } 

    @Override 
    public int getItemCount() { 
     return tags.size() + 1; 
    } 

    @Override 
    public long getItemId(int adapterPosition) { 
     switch(getItemViewType(adapterPosition)) { 
      case TYPE_TAG: 
       return tags.get(getDataSetPosition(adapterPosition)).getTagId(); 
      case TYPE_HEADER: 
      default: 
       return RecyclerView.NO_ID; 
     } 
    } 

    @Override 
    public int getItemViewType(int adapterPosition) { 
     if(adapterPosition == 0) { 
      return TYPE_HEADER; 
     } 
     return TYPE_TAG; 
    } 

    @Override 
    public boolean onItemMoved(final int fromAdapterPosition, final int toAdapterPosition) { 
     Log.i("TAGS", "move from " + fromAdapterPosition + " to " + toAdapterPosition); 
     if(getItemViewType(toAdapterPosition) != TYPE_TAG) { 
      return false; 
     } 

     Tag task1 = tags.get(getDataSetPosition(fromAdapterPosition)); 
     Tag task2 = tags.get(getDataSetPosition(toAdapterPosition)); 
     Log.i("TAGS", "realm pos from=" + getDataSetPosition(fromAdapterPosition) + " ; to=" + getDataSetPosition(toAdapterPosition)); 
     int order1 = task1.getOrder(); 
     realm.beginTransaction(); 
     task1.setOrder(task2.getOrder()); 
     task2.setOrder(order1); 
     realm.copyToRealmOrUpdate(task1); 
     realm.copyToRealmOrUpdate(task2); 
     realm.commitTransaction(); 
     notifyItemMoved(fromAdapterPosition, toAdapterPosition); 
     return true; 
    } 

    @Override 
    public void onItemSwiped(RecyclerView.ViewHolder holder, int direction) {} 

    public class TagViewHolder extends RecyclerView.ViewHolder { 
     public final TextView title; 

     public TagViewHolder(View itemView) { 
      super(itemView); 
      this.title = (TextView) itemView.findViewById(R.id.label); 
     } 
    } 

    public class HeaderViewHolder extends RecyclerView.ViewHolder { 
     public final TextView header; 

     public HeaderViewHolder(View itemView) { 
      super(itemView); 
      this.header = (TextView) itemView.findViewById(R.id.label); 
     } 
    } 
} 

Определение таблицы тегов:

public class Tag extends RealmObject { 
    @Ignore 
    private final static Object nextIdLock = new Object(); 
    @Ignore 
    private static Integer nextId; 

    @PrimaryKey 
    @Required 
    private Integer tagId; 
    @Required 
    private Integer order; 
    @Required 
    private String tag; 

    public Tag() { 
     synchronized(nextIdLock) { 
      if(nextId == null) { 
       Realm realm = Realm.getDefaultInstance(); 
       RealmResults<Tag> tags = realm.where(Tag.class).findAll(); 
       if(tags.size() != 0) { 
        nextId = tags.max("tagId").intValue() + 1; 
       } else { 
        nextId = 0; 
       } 
       realm.close(); 
      } 
      order = tagId = nextId++; 
     } 
    } 

    public Integer getTagId() { 
     return tagId; 
    } 

    public Tag setTagId(Integer tagId) { 
     this.tagId = tagId; 
     return this; 
    } 

    public Integer getOrder() { 
     return order; 
    } 

    public Tag setOrder(Integer order) { 
     this.order = order; 
     return this; 
    } 

    public String getTag() { 
     return tag; 
    } 

    public Tag setTag(String tag) { 
     this.tag = tag; 
     return this; 
    } 
} 

Другие файлы (классы, интерфейсы, макеты и т. Д.) Являются довольно сложными, поэтому я не буду вставлять их здесь, чтобы сохранить вопрос доступен, но они доступны в репозитории.

Теперь симптомы:

  • область версия 0.88.2 (филиал мастер):
    • При нажатии на кнопку «добавить пункт» добавляет элемент в верхней части списка с плавной анимацией
    • Прикосновение элемент позволяет перетащить его переупорядочить список
    • так работает, как ожидалось
  • область версия 0.89.0 (филиал царством-0.89.0):
    • При нажатии на кнопку «добавить пункт»:
      • первый щелчок: добавляет элемент в области, но не обновляет вид
      • дальнейшие клики: сбой приложения ("java.lang.IndexOutOfBoundsException: обнаружена несогласованность. Недопустимый вид держателя адаптер positionViewHolder {349439e7 позиции = 2 ID = 0, OldPos = 1, pLpos: 1 лом [attachedScrap] не tmpDetached нет родителя} ")
    • Касание позиции:
      • Остановки перетаскивания на первый своп
      • переставляет элементы в области (можно увидеть, заставляя деятельность, чтобы заново создать, таким образом, строительство RecyclerView из свежих данных)
      • возвращается просмотр в исходном состоянии или свопов других элементы ??

Единственное, что изменилось между двумя ветвями - это версия Царства. Я огляделся по поводу подобной проблемы, не повезло. На данный момент я не уверен, что это 0.89.0+, который нарушает поведение RecyclerView или если это 0.88.2, который не должен был работать таким образом.

Любая помощь была бы принята с благодарностью.

ответ

1

Это потому, что в 0.89.0, чтобы создать правильное итерационное поведение на RealmResults, локальные коммиты обновляют RealmResults только на следующем событии цикла, а не сразу.

Так что этот код здесь

realm.beginTransaction(); 
    task1.setOrder(task2.getOrder()); 
    task2.setOrder(order1); 
    realm.copyToRealmOrUpdate(task1); 
    realm.copyToRealmOrUpdate(task2); 
    realm.commitTransaction(); 
    notifyItemMoved(fromAdapterPosition, toAdapterPosition); 

потерпит неудачу, потому что список еще не обновлен после commitTransaction().

Я имел в виду выяснить умный способ решить эту проблему, но безрезультатно, хотя форсирование an immediate refresh via the HandlerControllerможет достичь тех же результатов.

Смежные вопросы