Remove most animation glitches on RecyclerView; disable reordering

pull/151/head
Alinson S. Xavier 9 years ago
parent 365eb400d0
commit 1526f617c5

@ -87,6 +87,5 @@ public class ListHabitsActivity extends BaseActivity
protected void onResume()
{
super.onResume();
adapter.refresh();
}
}

@ -179,7 +179,7 @@ public class ListHabitsController
public void onToggle(@NonNull Habit habit, long timestamp)
{
commandRunner.execute(new ToggleRepetitionCommand(habit, timestamp),
null);
habit.getId());
}
private void onFirstRun()

@ -137,5 +137,6 @@ public class ListHabitsMenu extends BaseMenu
.setArchivedAllowed(showArchived)
.setCompletedAllowed(showCompleted)
.build());
adapter.refresh();
}
}

@ -20,7 +20,6 @@
package org.isoron.uhabits.ui.habits.list;
import android.content.*;
import android.content.res.*;
import android.support.annotation.*;
import android.support.v7.widget.Toolbar;
import android.view.*;
@ -84,11 +83,13 @@ public class ListHabitsRootView extends BaseRootView
public static int getCheckmarkCount(View v)
{
Resources res = v.getResources();
float labelWidth = res.getDimension(R.dimen.habitNameWidth);
float buttonWidth = res.getDimension(R.dimen.checkmarkWidth);
return Math.min(MAX_CHECKMARK_COUNT, Math.max(0,
(int) ((v.getMeasuredWidth() - labelWidth) / buttonWidth)));
// Resources res = v.getResources();
// float labelWidth = res.getDimension(R.dimen.habitNameWidth);
// float buttonWidth = res.getDimension(R.dimen.checkmarkWidth);
// return Math.min(MAX_CHECKMARK_COUNT, Math.max(0,
// (int) ((v.getMeasuredWidth() - labelWidth) / buttonWidth)));
return 5; // TODO: Fix this.
}
@Override

@ -21,6 +21,7 @@ package org.isoron.uhabits.ui.habits.list.model;
import android.support.annotation.*;
import android.support.v7.widget.*;
import android.util.*;
import android.view.*;
import org.isoron.uhabits.*;
@ -80,12 +81,6 @@ public class HabitCardListAdapter
notifyDataSetChanged();
}
@Override
public int getItemCount()
{
return cache.getHabitCount();
}
/**
* Returns the item that occupies a certain position on the list
*
@ -101,28 +96,9 @@ public class HabitCardListAdapter
}
@Override
public HabitCardViewHolder onCreateViewHolder(ViewGroup parent,
int viewType)
{
if(listView == null) return null;
View view = listView.createCardView();
return new HabitCardViewHolder(view);
}
@Override
public void onBindViewHolder(@Nullable HabitCardViewHolder holder, int position)
public int getItemCount()
{
if(holder == null) return;
if(listView == null) return;
Habit habit = cache.getHabitByPosition(position);
int score = cache.getScore(habit.getId());
int checkmarks[] = cache.getCheckmarks(habit.getId());
boolean selected = this.selected.contains(habit);
HabitCardView cardView = (HabitCardView) holder.itemView;
listView.bindCardView(cardView, habit, score, checkmarks, selected,
position);
return cache.getHabitCount();
}
@Override
@ -143,6 +119,24 @@ public class HabitCardListAdapter
return new LinkedList<>(selected);
}
/**
* Returns whether list of selected items is empty.
*
* @return true if selection is empty, false otherwise
*/
public boolean isSelectionEmpty()
{
return selected.isEmpty();
}
/**
* Notify the adapter that it has been attached to a ListView.
*/
public void onAttached()
{
cache.onAttached();
}
// @Override
// public View getView(int position,
// @Nullable View view,
@ -159,29 +153,34 @@ public class HabitCardListAdapter
// checkmarks, selected);
// }
/**
* Returns whether list of selected items is empty.
*
* @return true if selection is empty, false otherwise
*/
public boolean isSelectionEmpty()
@Override
public void onBindViewHolder(@Nullable HabitCardViewHolder holder,
int position)
{
return selected.isEmpty();
}
if (holder == null) return;
if (listView == null) return;
/**
* Notify the adapter that it has been attached to a ListView.
*/
public void onAttached()
{
cache.onAttached();
Habit habit = cache.getHabitByPosition(position);
int score = cache.getScore(habit.getId());
int checkmarks[] = cache.getCheckmarks(habit.getId());
boolean selected = this.selected.contains(habit);
Log.d("HabitCardListView",
String.format("bind pos=%d itemId=%d, habit=%d:%s", position,
holder.getItemId(), habit.getId(), habit.getName()));
HabitCardView cardView = (HabitCardView) holder.itemView;
listView.bindCardView(cardView, habit, score, checkmarks, selected,
position);
}
@Override
public void onCacheRefresh()
public HabitCardViewHolder onCreateViewHolder(ViewGroup parent,
int viewType)
{
notifyDataSetChanged();
observable.notifyListeners();
if (listView == null) return null;
View view = listView.createCardView();
return new HabitCardViewHolder(view);
}
/**
@ -192,6 +191,34 @@ public class HabitCardListAdapter
cache.onDetached();
}
@Override
public void onItemChanged(int position)
{
notifyItemChanged(position);
observable.notifyListeners();
}
@Override
public void onItemInserted(int position)
{
notifyItemInserted(position);
observable.notifyListeners();
}
@Override
public void onItemMoved(int fromPosition, int toPosition)
{
notifyItemMoved(fromPosition, toPosition);
observable.notifyListeners();
}
@Override
public void onItemRemoved(int position)
{
notifyItemRemoved(position);
observable.notifyListeners();
}
public void refresh()
{
cache.refreshAllHabits(true);
@ -212,6 +239,7 @@ public class HabitCardListAdapter
public void reorder(int from, int to)
{
cache.reorder(from, to);
notifyItemMoved(from, to);
}
public void setFilter(HabitMatcher matcher)
@ -242,6 +270,6 @@ public class HabitCardListAdapter
int k = selected.indexOf(h);
if (k < 0) selected.add(h);
else selected.remove(h);
notifyDataSetChanged();
notifyItemChanged(position);
}
}

@ -42,8 +42,6 @@ import javax.inject.*;
*/
public class HabitCardListCache implements CommandRunner.Listener
{
boolean includeArchived;
private int checkmarkCount;
private BaseTask currentFetchTask;
@ -116,7 +114,6 @@ public class HabitCardListCache implements CommandRunner.Listener
public void onAttached()
{
refreshAllHabits(true);
if (lastLoadTimestamp == null) refreshAllHabits(true);
commandRunner.addListener(this);
}
@ -151,7 +148,8 @@ public class HabitCardListCache implements CommandRunner.Listener
Habit fromHabit = data.habits.get(from);
data.habits.remove(from);
data.habits.add(to, fromHabit);
if (listener != null) listener.onCacheRefresh();
if(listener != null)
listener.onItemMoved(from, to);
}
public void setCheckmarkCount(int checkmarkCount)
@ -162,7 +160,6 @@ public class HabitCardListCache implements CommandRunner.Listener
public void setFilter(HabitMatcher matcher)
{
filteredHabits = allHabits.getFiltered(matcher);
refreshAllHabits(true);
}
public void setListener(@Nullable Listener listener)
@ -176,16 +173,19 @@ public class HabitCardListCache implements CommandRunner.Listener
*/
public interface Listener
{
/**
* Called when the data on the cache has been modified.
*/
void onCacheRefresh();
void onItemChanged(int position);
void onItemInserted(int position);
void onItemMoved(int oldPosition, int newPosition);
void onItemRemoved(int position);
}
private class CacheData
{
@NonNull
public HashMap<Long, Habit> map;
public HashMap<Long, Habit> id_to_habit;
@NonNull
public List<Habit> habits;
@ -201,7 +201,7 @@ public class HabitCardListCache implements CommandRunner.Listener
*/
public CacheData()
{
map = new HashMap<>();
id_to_habit = new HashMap<>();
habits = new LinkedList<>();
checkmarks = new HashMap<>();
scores = new HashMap<>();
@ -211,7 +211,7 @@ public class HabitCardListCache implements CommandRunner.Listener
{
int[] empty = new int[checkmarkCount];
for (Long id : map.keySet())
for (Long id : id_to_habit.keySet())
{
if (oldData.checkmarks.containsKey(id))
checkmarks.put(id, oldData.checkmarks.get(id));
@ -221,7 +221,7 @@ public class HabitCardListCache implements CommandRunner.Listener
public void copyScoresFrom(@NonNull CacheData oldData)
{
for (Long id : map.keySet())
for (Long id : id_to_habit.keySet())
{
if (oldData.scores.containsKey(id))
scores.put(id, oldData.scores.get(id));
@ -234,7 +234,7 @@ public class HabitCardListCache implements CommandRunner.Listener
for (Habit h : filteredHabits)
{
habits.add(h);
map.put(h.getId(), h);
id_to_habit.put(h.getId(), h);
}
}
}
@ -259,65 +259,83 @@ public class HabitCardListCache implements CommandRunner.Listener
newData.copyScoresFrom(data);
newData.copyCheckmarksFrom(data);
// sleep(1000);
commit();
if (!refreshScoresAndCheckmarks) return;
long dateTo = DateUtils.getStartOfDay(DateUtils.getLocalTime());
long dateFrom =
dateTo - (checkmarkCount - 1) * DateUtils.millisecondsInOneDay;
long day = DateUtils.millisecondsInOneDay;
long dateFrom = dateTo - (checkmarkCount - 1) * day;
int current = 0;
for (Habit h : newData.habits)
for(Habit h : newData.habits)
{
if (isCancelled()) return;
Long id = h.getId();
newData.scores.put(id, h.getScores().getTodayValue());
newData.checkmarks.put(id,
h.getCheckmarks().getValues(dateFrom, dateTo));
// sleep(1000);
publishProgress(current++, newData.map.size());
}
publishProgress(0);
}
@Override
protected void onPostExecute(Void aVoid)
{
if (isCancelled()) return;
lastLoadTimestamp = DateUtils.getStartOfToday();
currentFetchTask = null;
if (listener != null) listener.onCacheRefresh();
super.onPostExecute(null);
}
@Override
protected void onProgressUpdate(Integer... values)
{
if (listener != null) listener.onCacheRefresh();
}
if(listener == null) throw new RuntimeException(
"listener should have been attached");
Set<Long> before = data.id_to_habit.keySet();
Set<Long> after = newData.id_to_habit.keySet();
private void commit()
Set<Long> removed = new TreeSet<>(before);
removed.removeAll(after);
for(Long id : removed)
{
data = newData;
Habit h = data.id_to_habit.get(id);
int position = data.habits.indexOf(h);
data.habits.remove(position);
data.id_to_habit.remove(id);
data.checkmarks.remove(id);
data.scores.remove(id);
listener.onItemRemoved(position);
Log.d("HabitCardListCache", String.format("removed %d",
position));
}
private void sleep(int time)
for(int k = 0; k < newData.habits.size(); k++)
{
try
Habit h = newData.habits.get(k);
Long id = h.getId();
Habit prevHabit = data.id_to_habit.get(id);
int prevPosition = data.habits.indexOf(prevHabit);
if(prevPosition == k) continue;
if(prevPosition < 0)
{
Thread.sleep(time);
data.habits.add(k, h);
data.id_to_habit.put(id, h);
data.scores.put(id, newData.scores.get(id));
data.checkmarks.put(id, newData.checkmarks.get(id));
listener.onItemInserted(k);
Log.d("HabitCardListCache", String.format("inserted %d",
k));
}
catch (InterruptedException e)
else
{
e.printStackTrace();
data.habits.remove(prevPosition);
data.habits.add(k, h);
listener.onItemMoved(prevPosition, k);
Log.d("HabitCardListCache", String.format("moved %d %d",
prevPosition, k));
}
}
}
}
private class RefreshHabitTask extends BaseTask
@ -333,13 +351,12 @@ public class HabitCardListCache implements CommandRunner.Listener
protected void doInBackground()
{
long dateTo = DateUtils.getStartOfDay(DateUtils.getLocalTime());
long dateFrom =
dateTo - (checkmarkCount - 1) * DateUtils.millisecondsInOneDay;
long day = DateUtils.millisecondsInOneDay;
long dateFrom = dateTo - (checkmarkCount - 1) * day;
Habit h = allHabits.getById(id);
if (h == null) return;
data.map.put(id, h);
data.scores.put(id, h.getScores().getTodayValue());
data.checkmarks.put(id,
h.getCheckmarks().getValues(dateFrom, dateTo));
@ -348,7 +365,13 @@ public class HabitCardListCache implements CommandRunner.Listener
@Override
protected void onPostExecute(Void aVoid)
{
if (listener != null) listener.onCacheRefresh();
if (listener != null)
{
Habit h = data.id_to_habit.get(id);
int position = data.habits.indexOf(h);
listener.onItemChanged(position);
}
super.onPostExecute(null);
}
}

@ -68,8 +68,8 @@ public class CheckmarkButtonView extends FrameLayout
public void toggle()
{
// value = (value == Checkmark.CHECKED_EXPLICITLY ? Checkmark.UNCHECKED :
// Checkmark.CHECKED_EXPLICITLY);
value = (value == Checkmark.CHECKED_EXPLICITLY ? Checkmark.UNCHECKED :
Checkmark.CHECKED_EXPLICITLY);
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
postInvalidate();

@ -42,11 +42,10 @@ public class HabitCardListView extends RecyclerView
{
super(context, attrs);
setLongClickable(true);
setHasFixedSize(true);
setLayoutManager(new LinearLayoutManager(getContext()));
TouchHelperCallback callback = new TouchHelperCallback();
new ItemTouchHelper(callback).attachToRecyclerView(this);
// TouchHelperCallback callback = new TouchHelperCallback();
// new ItemTouchHelper(callback).attachToRecyclerView(this);
}
/**

Loading…
Cancel
Save