1

我现在面临一些问题,现在我很难解决。所以我指的是社区中的Realm和RecyclerView天才。 我在一个ToDo-List上工作,在2天后将完成的任务设置回ToDo-List。该应用程序使用带有两个选项卡的ViewPager:“TODO”&“完成”。领域和RecyclerView项目排序和自动ViewPager片段通信

1. RecyclerView

1.1。我希望片段1中完成的任务在2天后自动发送回片段0。 问题:如果计数器在0(或以下),则项目被发送到片段0. 如果我在下一行删除项目,则会发生异常错误:"java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling" 因此,我将删除函数放入处理程序中。然后,只有当一个人被送回时,它才能正常工作。如果许多项目被同时发送回应用程序崩溃。当我重新打开应用程序时,一切正常,因为它已成功保存在领域,但一个项目总是保存两次。 问题在哪里(在DoneAdapter.java中)?

2.境界

2.1。当我将物品添加到RecyclerView(并同时添加到Realm)时,该物品将被添加到底部。但我想在位置0添加每个新项目。 (我知道如何使用ArrayList实现此目标,但我希望在重新打开应用程序时存储和显示项目,因此我使用的是Realm DB。) 你有任何建议来实现这一目标吗?

2.2。是否可以稍后在onLongClickListener上实现拖放物品并使用Realm重新排列位置? (我想用这个https://www.youtube.com/watch?v=tNgevYpyA9E

2.3。我想在添加和检查项目时添加一些不错的动画。领域不支持mRecyclerView.setItemAnimator(...);,但我听说可以通过添加mAdapter.setHasStableIds(true);。不幸的是它会抛出一个例外:java.lang.IllegalStateException: Cannot change whether this adapter has stable IDs while the adapter has registered observers.(你可以在我的代码中看到这个) 你有任何解决方案吗?你可以推荐任何在线数据库(例如Firebase),我可以与Realm或更一般地同步:是否可以将在线数据库与Realm同步?你知道任何教程(Udemy,YouTube)吗?用于设置此同步过程?)

最后:我想在每个午夜用后台服务更新数据库,以便已完成部分中的计数器自动更新。有谁知道如何做到这一点?也许与protected void onHandleIntent(Intent intent)? 你是否也知道在调试模式中是否有一个选项来模拟传球时间?

下面是代码:

MainActivity.java

public class MainActivity extends AppCompatActivity implements ToOtherFragmentCommunicator { 


private ViewPagerAdapter mViewPagerAdapter; 
private ViewPager mViewPager; 
private static final int DONE = 1; 
private static final int TODO = 0; 

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

    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 
    setSupportActionBar(toolbar); 


    mViewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager()); 
    mViewPager = (ViewPager) findViewById(R.id.container); 
    mViewPager.setAdapter(mViewPagerAdapter); 

    TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs); 
    tabLayout.setupWithViewPager(mViewPager); 
    tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { 
     @Override 
     public void onTabSelected(TabLayout.Tab tab) { 
      mViewPager.setCurrentItem(tab.getPosition()); 
     } 

     @Override 
     public void onTabUnselected(TabLayout.Tab tab) { 

     } 

     @Override 
     public void onTabReselected(TabLayout.Tab tab) { 

     } 
    }); 

    RealmConfiguration configuration = new RealmConfiguration.Builder(this).build(); 
    Realm.setDefaultConfiguration(configuration); 
} 

@Override 
public void itemToOtherFragment(String data, int fragment) { 
    if (DONE == fragment) { 
     Done done = (Done) mViewPagerAdapter.getItem(fragment); 
     done.createDoneItem(data); 
    } else if (TODO == fragment) { 
     ToDo toDo = (ToDo) mViewPagerAdapter.getItem(fragment); 
     toDo.createToDoItem(data); 
    } 
} 
} 

ToDo.java

public class ToDo extends Fragment { 

private RecyclerView mRecyclerView; 
private ToDoAdapter mAdapter; 
private EditText taskInput; 
private String taskName; 
private Realm mRealm; 
private RealmResults<ListItems> mResults; 


@Nullable 
@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
    View toDoView = inflater.inflate(R.layout.todo_layout, container, false); 

    mRecyclerView = (RecyclerView) toDoView.findViewById(R.id.todo_rv); 


    mRealm = Realm.getDefaultInstance(); 
    mResults = mRealm.where(ListItems.class).equalTo("fragment", 0).findAllAsync(); 

    setRecyclerView(); 

    mRecyclerView.setItemAnimator(null); 

    //TODO add product to shopping list 
    final Handler handler = new Handler(); 
    taskInput = (EditText) toDoView.findViewById(R.id.task_input); 
    taskInput.setOnKeyListener(new View.OnKeyListener() { 
     @Override 
     public boolean onKey(View v, int keyCode, KeyEvent event) { 
      if (taskInput.getText().length() > 0 && (event.getAction() == KeyEvent.ACTION_DOWN) && 
        (keyCode == KeyEvent.KEYCODE_ENTER)) { 
       // Perform action on key press 
       taskName = taskInput.getText().toString(); 

       //Problem 2.1 
       //Code for adding item at the top with mRealm? 
       mRealm.beginTransaction(); 
       createToDoItem(taskName); 
       mRealm.commitTransaction(); 


//     mRecyclerView.scrollToPosition(0); 
       taskInput.setText(null); 

       handler.postDelayed(new Runnable() { 
        @Override 
        public void run() { 
         taskInput.setFocusableInTouchMode(true); 
         taskInput.setFocusable(true); 
         taskInput.requestFocus(); 
        } 
       }, 200); 

       return true; 

      } else if (taskInput.length() == 0 && (event.getAction() == KeyEvent.ACTION_DOWN) && 
        (keyCode == KeyEvent.KEYCODE_ENTER)) { 
       taskInput.clearFocus(); 
       InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); 
       imm.hideSoftInputFromWindow(taskInput.getWindowToken(), 0); 
       return true; 
      } 

      return false; 

     } 
    }); 

    return toDoView; 
} 


//TODO creates the shopping list item in DB 
public void createToDoItem(String taskName) { 

    ListItems item = mRealm.createObject(ListItems.class); 

    long now = System.currentTimeMillis(); 
    item.setAddedTime(now); 
    item.setFragment(0); 

    item.setTaskName(taskName); 

    mRealm.copyToRealmOrUpdate(item); 
} 

public void setRecyclerView() { 
    mRecyclerView.setHasFixedSize(true); 
    LinearLayoutManager mLayoutManager = new LinearLayoutManager(getActivity()); 
    mRecyclerView.setLayoutManager(mLayoutManager); 
    mAdapter = new ToDoAdapter(getActivity(), mRealm, mResults); 
    mRecyclerView.setAdapter(mAdapter); 

    //Problem 2.3. 
    //Produces "java.lang.IllegalStateException: Cannot change whether this adapter has stable IDs while the adapter has registered observers." 
//  mAdapter.setHasStableIds(true); 
} 

private RealmChangeListener mChangeListener = new RealmChangeListener() { 
    @Override 
    public void onChange() { 
     mAdapter.updateItems(mResults); 
    } 
}; 

@Override 
public void onStart() { 
    super.onStart(); 
    mResults.addChangeListener(mChangeListener); 
} 

@Override 
public void onStop() { 
    super.onStop(); 
    mResults.removeChangeListener(mChangeListener); 
} 


} 

ToDoAdapter.java

public class ToDoAdapter extends RecyclerView.Adapter<ListItemsViewHolder> { 

private Context mContext; 
private Realm mRealm; 
private RealmResults<ListItems> mResults; 
private int focusedItem = 0; 
ToOtherFragmentCommunicator comm; 

ToDoAdapter(Context context, Realm realm, RealmResults<ListItems> mResults) { 
    this.mContext = context; 
    this.mRealm = realm; 
    updateItems(mResults); 
} 

public void updateItems(RealmResults<ListItems> mResults) { 
    this.mResults = mResults; 
    notifyDataSetChanged(); 
} 

//Problem 2.3. 
//needed for mAdapter.setHasStableIds(true); in ToDo.java 
// @Override 
// public long getItemId(int position) { 
//  if (position < mResults.size()) { 
//   return mResults.get(position).getAddedTime(); 
//  } else { 
//   return RecyclerView.NO_ID; 
//  } 
// } 

@Override 
public ListItemsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.todo_item, parent, false); 
    comm = (ToOtherFragmentCommunicator) mContext; 
    return new ListItemsViewHolder(v); 

} 

@Override 
public void onBindViewHolder(final ListItemsViewHolder holder, final int position) { 
    final ListItems items = mResults.get(position); 


    holder.taskName.setText(items.getTaskName()); 

    holder.itemView.setSelected(focusedItem == position); 
    holder.getLayoutPosition(); 

    holder.itemCheckbox.setOnCheckedChangeListener(null); 
    holder.itemCheckbox.setChecked(items.isSelected()); 

    holder.itemCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 
     @Override 
     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 

      mRealm.beginTransaction(); 
      items.setSelected(isChecked); 

      //send item to Done 
      comm.itemToOtherFragment(items.getTaskName(), 1); 

      removeItem(position); 
      mRealm.commitTransaction(); 

     } 
    }); 


} 


@Override 
public int getItemCount() { 
    return (mResults != null ? mResults.size() : 0); 
} 

private void removeItem(int position) { 
    mResults.get(position).removeFromRealm(); 
    notifyDataSetChanged(); 
} 

} 

完成。java的

public class Done extends Fragment { 

private RecyclerView mRecyclerView; 
private DoneAdapter mAdapter; 
private Calendar calendar = Calendar.getInstance(); 
private Date date = new Date(); 
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd.MM.yyyy"); 
private Realm mRealm; 
private RealmResults<ListItems> mResults; 


@Nullable 
@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
    View doneView = inflater.inflate(R.layout.done_layout, container, false); 

    mRecyclerView = (RecyclerView) doneView.findViewById(R.id.done_rv); 

    mRealm = Realm.getDefaultInstance(); 
    mResults = mRealm.where(ListItems.class).equalTo("fragment", 1).findAllAsync(); 

    setRecyclerView(); 
    mRecyclerView.setItemAnimator(null); 


    return doneView; 
} 


//TODO creates the fridge item in DB 
public void createDoneItem(String taskName) { 
    TimeZone.getDefault(); 


    ListItems item = mRealm.createObject(ListItems.class); 

    long now = System.currentTimeMillis(); 
    item.setAddedTime(now); 
    item.setFragment(1); 

    item.setTaskName(taskName); 
    item.setInputDate(simpleDateFormat.format(calendar.getTime())); 

    calendar.add(Calendar.DATE, 2); 
    item.setRenewDate(simpleDateFormat.format(calendar.getTime())); 


    //reset time to current date after adding days 
    calendar.setTime(date); 

    item.getRenewDate(); 

    mRealm.copyToRealmOrUpdate(item); 
} 


public void setRecyclerView() { 
    mRecyclerView.setHasFixedSize(true); 
    LinearLayoutManager mLayoutManager = new LinearLayoutManager(getActivity()); 
    mRecyclerView.setLayoutManager(mLayoutManager); 
    mAdapter = new DoneAdapter(getActivity(), mRealm, mResults, Done.this); 
    mRecyclerView.setAdapter(mAdapter); 
} 


private RealmChangeListener mChangeListener = new RealmChangeListener() { 
    @Override 
    public void onChange() { 
     mAdapter.updateItems(mResults); 
    } 
}; 

@Override 
public void onStart() { 
    super.onStart(); 
    mResults.addChangeListener(mChangeListener); 
} 

@Override 
public void onStop() { 
    super.onStop(); 
    mResults.removeChangeListener(mChangeListener); 
} 
} 

DoneAdapter.java

public class DoneAdapter extends RecyclerView.Adapter<ListItemsViewHolder> { 

private Context mContext; 
private Done done; 
private Realm mRealm; 
private RealmResults<ListItems> mResults; 
private int focusedItem = 0; 
protected ToOtherFragmentCommunicator comm; 


DoneAdapter(Context context, Realm realm, RealmResults<ListItems> results, Done done) { 
    this.mContext = context; 
    this.mRealm = realm; 
    this.done = done; 

    updateItems(results); 
} 

public void updateItems(RealmResults<ListItems> mResults) { 
    this.mResults = mResults; 
    notifyDataSetChanged(); 
} 

@Override 
public ListItemsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.done_item, parent, false); 
    comm = (ToOtherFragmentCommunicator) mContext; 
    return new ListItemsViewHolder(v); 
} 

@TargetApi(Build.VERSION_CODES.JELLY_BEAN) 
@Override 
public void onBindViewHolder(final ListItemsViewHolder holder, final int position) { 
    final ListItems items = mResults.get(position); 

    holder.taskName.setText(items.getTaskName()); 

    try { 
     if (items.getRenewCounter() == 1) { 
      holder.renewCounter.setText(mContext.getString(R.string.show_days_till_renew, items.getRenewCounter(), mContext.getString(R.string.day))); 
     } else { 
      holder.renewCounter.setText(mContext.getString(R.string.show_days_till_renew, items.getRenewCounter(), mContext.getString(R.string.days))); 
     } 

     holder.renewCounter.setTextColor(ContextCompat.getColor(mContext, R.color.colorAccent)); 
     if (items.getRenewCounter() <= 0) { 
      mRealm.beginTransaction(); 

      //Problem 1.1. 
      //send item back to todo list 
      comm.itemToOtherFragment(items.getTaskName(), 0); 
      // Produces "java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling" if there is no Handler 
      Handler handler = new Handler(); 
      final Runnable r = new Runnable() { 
       public void run() { 

        mRealm.beginTransaction(); 
        removeItem(position); 
        mRealm.commitTransaction(); 

       } 
      }; 
      handler.post(r); 
      mRealm.commitTransaction(); 
     } 


    } catch (ParseException e) { 
     e.printStackTrace(); 
    } 


    holder.itemView.setSelected(focusedItem == position); 
    holder.getLayoutPosition(); 

} 


@Override 
public int getItemCount() { 
    return (mResults != null ? mResults.size() : 0); 
} 

private void removeItem(int position) { 
    mResults.get(position).removeFromRealm(); 
    notifyDataSetChanged(); 
} 


} 

ListItems.java

public class ListItems extends RealmObject { 

public ListItems(long addedTime, String taskName, String inputDate, String renewDate, int fragment) { 
    this.addedTime = addedTime; 
    this.taskName = taskName; 
    this.inputDate = inputDate; 
    this.renewDate = renewDate; 
    this.fragment = fragment; 
} 

@PrimaryKey 
private long addedTime; 
private int fragment; 
@Ignore 
private long renewCounter; 
private String taskName, inputDate, renewDate; 
private boolean selected; 

public ListItems() { 
} 

public long getAddedTime() { 
    return addedTime; 
} 

public void setAddedTime(long addedTime) { 
    this.addedTime = addedTime; 
} 

public int getFragment() { 
    return fragment; 
} 

public void setFragment(int fragment) { 
    this.fragment = fragment; 
} 

public String getTaskName() { 
    return taskName; 
} 

public void setTaskName(String taskName) { 
    this.taskName = taskName; 
} 

public String getInputDate() { 
    return inputDate; 
} 

public void setInputDate(String inputDate) { 
    this.inputDate = inputDate; 
} 

public String getRenewDate() { 
    return renewDate; 
} 

public void setRenewDate(String renewDate) { 
    this.renewDate = renewDate; 
} 

public boolean isSelected() { 
    return selected; 
} 

public void setSelected(boolean selected) { 
    this.selected = selected; 
} 

public long getRenewCounter() throws ParseException { 
    TimeZone.getDefault(); 

    SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy"); 
    Date todayDate = new Date(); 
    Date exDate = dateFormat.parse(renewDate); 

    this.renewCounter = daysBetween(todayDate, exDate); 
    return renewCounter; 

} 

private static long daysBetween(Date startDate, Date endDate) { 
    Calendar sDate = getDatePart(startDate); 
    Calendar eDate = getDatePart(endDate); 

    long daysBetween = 0; 
    while (sDate.before(eDate)) { 
     sDate.add(Calendar.DAY_OF_MONTH, 1); 
     daysBetween++; 
    } 

    while (eDate.before(sDate)) { 
     eDate.add(Calendar.DAY_OF_MONTH, 1); 
     daysBetween--; 
    } 

    return daysBetween; 
} 

private static Calendar getDatePart(Date date) { 
    Calendar cal = Calendar.getInstance();  // get calendar instance 
    cal.setTime(date); 
    cal.set(Calendar.HOUR_OF_DAY, 0);   // set hour to midnight 
    cal.set(Calendar.MINUTE, 0);     // set minute in hour 
    cal.set(Calendar.SECOND, 0);     // set second in minute 
    cal.set(Calendar.MILLISECOND, 0);   // set millisecond in second 

    return cal;         // return the date part 
} 

} 

这里有一个截图上的应用程序的样子: DailyTaskRepeater

这就是它!如果有人能够帮助我解决这个问题,那对我来说就意味着这个世界(尤其是问题1.1!)。

谢谢!

回答

3

Realm支持的当前实践是添加一个索引(例如timestamp)并对您的列表进行反向排序,以便让最新的项目位于顶部,并提高您正在寻找的重新排列效果。

请考虑参考官方存储库中提供的an adapter example