Realm和RecyclerView项目排序和自动ViewPager片段通信

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Realm和RecyclerView项目排序和自动ViewPager片段通信相关的知识,希望对你有一定的参考价值。

我现在面临一些问题,但是我很难解决。所以我指的是社区中的Realm和RecyclerView天才。我正在使用ToDo-List,它将完成的任务在2天后重新设置为ToDo-List。该应用程序使用带有两个选项卡的ViewPager:“TODO”和“DONE”。

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。当我将一个Item添加到RecyclerView(同时添加到Realm)时,该项将添加到底部。但我想在位置0添加每个新项目。(我知道如何用ArrayList实现这个,但我希望在重新打开应用程序时存储和显示项目,所以我使用的是Realm DB。)你有任何建议来实现这一目标吗?

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

2.3。我想在添加和检查项目时添加一些不错的动画。 Realm不支持mRecyclerView.setItemAnimator(...);但我听说可以添加mAdapter.setHasStableIds(true);。不幸的是它引发了一个例外:java.lang.IllegalStateException: Cannot change whether this adapter has stable IDs while the adapter has registered observers.(你可以在下面的代码中看到这个)你有任何解决方案吗?

(可选1.4。你能推荐我可以与Realm同步的任何在线数据库(例如Firebase)吗?或者更普遍的是:是否可以将在线数据库与Realm同步?你知道任何教程(Udemy,YouTube)来设置这个吗?同步过程?)

最后:我想每隔午夜使用后台服务更新数据库,因此已完成部分中的计数器会自动更新。有谁知道怎么做?也许与protected void onHandleIntent(Intent intent)?你也知道调试模式中是否有一个选项可以模拟通过时间?

这是代码:

main activity.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);
    }
}
}

to do.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);
}


}

to do adapter.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();
}

}

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

done adapter.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();
}


}

list items.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!),这对我来说意味着世界。

谢谢!

答案

Realm支持的当前做法是添加索引(例如时间戳)并对列表进行反向排序,以使最新项目位于顶部,并获得您正在寻找的重新排列效果。

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

以上是关于Realm和RecyclerView项目排序和自动ViewPager片段通信的主要内容,如果未能解决你的问题,请参考以下文章

将水平 RecyclerView 作为项目放置在垂直 RecyclerView 中时的自动滚动问题

无需长按即可在recyclerView中重新排序项目

使用数据库对 RecyclerView 项目进行排序

AdapterView 和 RecyclerView 的连续滚动

如何使用 Realm 进行排序?

使用 Realm 按多个属性排序