1

Ich habe seit einiger Zeit einige Probleme, die ich zu lösen habe. Ich beziehe mich also auf die Genies Realm und RecyclerView in der Community. Ich bin auf einer ToDo-Liste, die abgeschlossene Aufgaben zurück in die ToDo-Liste nach 2 Tagen setzt. Die App verwendet einen ViewPager mit zwei Tabs: "TODO" & "DONE".Realm und RecyclerView Sortieren von Elementen und automatische ViewPager Fragment Kommunikation

1. RecyclerView

1,1. Ich möchte, dass die abgeschlossenen Aufgaben von Fragment 1 automatisch nach 2 Tagen an Fragment 0 zurückgesendet werden. Das Problem: Wenn der Zähler auf 0 (oder unter) ist, wird der Artikel an Fragment 0 gesendet. Wenn ich das Element in der nächsten Zeile delte bekomme ich einen Ausnahmefehler: Also habe ich die Löschfunktion in einen Handler. Dann funktioniert es aber ABER nur, wenn ONE zurückgeschickt wird. Wenn viele Gegenstände gleichzeitig zurückgesendet werden, stürzt die App ab. Wenn ich die App wieder öffne, funktioniert alles, weil es erfolgreich im Bereich gespeichert wurde, aber ein Element wird immer zweimal gespeichert. Wo ist das Problem (in DoneAdapter.java)?

2. Realm

2,1. Wenn ich ein Item zur RecyclerView (und gleichzeitig zu Realm) hinzufüge, wird das Item unten hinzugefügt. Aber ich möchte jedes neue Element an Position 0 hinzufügen. (Ich weiß, wie man dies mit einer ArrayList erreichen kann, aber ich möchte, dass die Elemente gespeichert und angezeigt werden, wenn ich die App neu öffne, also verwende ich Realm DB.) Haben Sie Vorschläge, dies zu erreichen?

2.2. Ist es möglich, später den onLongClickListener zum Ziehen und Ablegen von Objekten und zum Neuanordnen der Position mit Realm zu implementieren? (Ich möchte dies verwenden https://www.youtube.com/watch?v=tNgevYpyA9E)

2.3. Ich möchte einige nette Animationen hinzufügen, wenn ich ein Element hinzufüge und überprüfe. Realm unterstützt mRecyclerView.setItemAnimator(...); nicht, aber ich habe gehört, dass es möglich ist, indem man mAdapter.setHasStableIds(true); hinzufügt. Leider wirft es eine Ausnahme: java.lang.IllegalStateException: Cannot change whether this adapter has stable IDs while the adapter has registered observers. (Sie können dies in meinem Code unten sehen) Haben Sie irgendwelche Lösungen dafür?

(optional 1.4. Kannst du mir irgendwelche Online DBs (zB Firebase) empfehlen, die ich mit Realm oder allgemeiner synchronisieren kann: Ist es möglich, eine Online DB mit Realm zu synchronisieren? Kennst du Tutorials (Udemy, YouTube)? zum Einrichten dieses Sync-Prozesses?)

Schließlich: Ich möchte die Datenbank mit einem Hintergrund-Service jeden Mitternacht aktualisieren, so dass der Zähler im abgeschlossenen Abschnitt automatisch aktualisiert wird. Weiß jemand, wie man das auch macht? Vielleicht mit protected void onHandleIntent(Intent intent)? Wissen Sie auch, ob es eine Option im Debugging-Modus gibt, um die verstreichende Zeit zu simulieren? Hier

ist der Code:

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

} 

Fertig.

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 
} 

} 

Hier ein Screenshot, wie die App wie folgt aussieht: DailyTaskRepeater

Das ist es! Es würde mir die Welt bedeuten, wenn mir jemand dabei helfen könnte (besonders Problem 1.1!).

Vielen Dank!

Antwort

3

Die aktuelle Praxis, die Realm unterstützt, besteht darin, einen Index (z. B. Zeitstempel) hinzuzufügen und die Liste umgekehrt zu sortieren, damit das neueste Element oben angezeigt wird und der angestrebte Umordnungseffekt erzielt wird.

Bitte berücksichtigen Sie eine Referenz von an adapter example im offiziellen Repository zur Verfügung gestellt.

Verwandte Themen