Drag and drop to rearrange filter criteria

pull/996/head
Alex Baker 4 years ago
parent 1a05278ab0
commit 410af772f2

@ -1,5 +1,6 @@
package com.todoroo.astrid.core
import android.content.Context
import android.view.View
import android.widget.ImageView
import android.widget.TextView
@ -10,8 +11,14 @@ import butterknife.OnClick
import org.tasks.Callback
import org.tasks.R
import org.tasks.locale.Locale
import org.tasks.preferences.ResourceResolver
class CriterionViewHolder(itemView: View, private val locale: Locale, private val onClick: Callback<String>) : RecyclerView.ViewHolder(itemView) {
class CriterionViewHolder(
private val context: Context,
itemView: View,
private val locale: Locale,
private val onClick: Callback<String>)
: RecyclerView.ViewHolder(itemView) {
@BindView(R.id.divider)
lateinit var divider: View
@ -67,4 +74,13 @@ class CriterionViewHolder(itemView: View, private val locale: Locale, private va
@OnClick(R.id.row)
fun onClick() = this.onClick.call(criterion.id)
fun setMoving(moving: Boolean) {
if (moving) {
row.setBackgroundColor(ResourceResolver.getData(context, R.attr.colorControlHighlight))
} else {
row.setBackgroundResource(ResourceResolver.getResourceId(context, R.attr.selectableItemBackground))
row.background.jumpToCurrentState()
}
}
}

@ -2,11 +2,15 @@ package com.todoroo.astrid.core;
import static com.google.common.collect.Lists.transform;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.AsyncDifferConfig;
import androidx.recyclerview.widget.AsyncListDiffer;
import androidx.recyclerview.widget.ListUpdateCallback;
import androidx.recyclerview.widget.RecyclerView;
import com.google.common.collect.ImmutableList;
import java.util.List;
@ -14,7 +18,8 @@ import org.tasks.Callback;
import org.tasks.R;
import org.tasks.locale.Locale;
public class CustomFilterAdapter extends RecyclerView.Adapter<CriterionViewHolder> {
public class CustomFilterAdapter extends RecyclerView.Adapter<CriterionViewHolder> implements
ListUpdateCallback {
private final Callback<String> onClick;
private final Locale locale;
@ -24,16 +29,16 @@ public class CustomFilterAdapter extends RecyclerView.Adapter<CriterionViewHolde
List<CriterionInstance> objects, Locale locale, Callback<String> onClick) {
this.locale = locale;
this.onClick = onClick;
differ = new AsyncListDiffer<>(this, new CriterionDiffCallback());
differ = new AsyncListDiffer<>(this, new AsyncDifferConfig.Builder<>(new CriterionDiffCallback()).build());
submitList(objects);
}
@NonNull
@Override
public CriterionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view =
LayoutInflater.from(parent.getContext()).inflate(R.layout.custom_filter_row, parent, false);
return new CriterionViewHolder(view, locale, onClick);
Context context = parent.getContext();
View view = LayoutInflater.from(context).inflate(R.layout.custom_filter_row, parent, false);
return new CriterionViewHolder(context, view, locale, onClick);
}
@Override
@ -53,4 +58,24 @@ public class CustomFilterAdapter extends RecyclerView.Adapter<CriterionViewHolde
private List<CriterionInstance> getItems() {
return differ.getCurrentList();
}
@Override
public void onInserted(int position, int count) {
notifyItemRangeInserted(position, count);
}
@Override
public void onRemoved(int position, int count) {
notifyItemRangeRemoved(position, count);
}
@Override
public void onMoved(int fromPosition, int toPosition) {
notifyDataSetChanged();
}
@Override
public void onChanged(int position, int count, @Nullable Object payload) {
notifyItemRangeChanged(position, count);
}
}

@ -0,0 +1,39 @@
package com.todoroo.astrid.core
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import org.tasks.Callback2
class CustomFilterItemTouchHelper(private val onMove: Callback2<Int, Int>, private val onClear: Runnable) : ItemTouchHelper.Callback() {
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
return if (viewHolder.adapterPosition > 0) makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0) else 0
}
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
(viewHolder as CriterionViewHolder).setMoving(true);
}
}
override fun onMove(
recyclerView: RecyclerView, src: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
val toPosition = target.adapterPosition
if (toPosition == 0) {
return false
}
onMove.call(src.adapterPosition, toPosition);
return true
}
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
(viewHolder as CriterionViewHolder).setMoving(false)
onClear.run()
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}
}

@ -0,0 +1,5 @@
package org.tasks;
public interface Callback2<T1, T2> {
void call(T1 t1, T2 t2);
}

@ -21,6 +21,7 @@ import android.widget.EditText;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
@ -44,6 +45,7 @@ import com.todoroo.astrid.api.PermaSql;
import com.todoroo.astrid.api.TextInputCriterion;
import com.todoroo.astrid.core.CriterionInstance;
import com.todoroo.astrid.core.CustomFilterAdapter;
import com.todoroo.astrid.core.CustomFilterItemTouchHelper;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.data.Task;
@ -116,8 +118,11 @@ public class FilterSettingsActivity extends BaseListSettingsActivity {
criteria.add(instance);
}
adapter = new CustomFilterAdapter(criteria, locale, this::onClick);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
new ItemTouchHelper(new CustomFilterItemTouchHelper(this::onMove, this::updateList))
.attachToRecyclerView(recyclerView);
fab.setExtended(isNew() || adapter.getItemCount() <= 1);
@ -126,6 +131,12 @@ public class FilterSettingsActivity extends BaseListSettingsActivity {
updateTheme();
}
private void onMove(int from, int to) {
CriterionInstance criterion = criteria.remove(from);
criteria.add(to, criterion);
adapter.notifyItemMoved(from, to);
}
private void onClick(String replaceId) {
CriterionInstance criterionInstance = find(criteria, c -> c.getId().equals(replaceId));

@ -1,46 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/row"
android:layout_width="fill_parent"
android:layout_height="55dip"
android:background="?attr/selectableItemBackground"
android:orientation="horizontal">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/window_background"
android:orientation="vertical">
<View
android:id="@+id/divider"
style="@style/horizontal_divider"
android:visibility="gone"
android:layout_alignParentTop="true" />
<RelativeLayout
android:id="@+id/row"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="horizontal">
<TextView
android:id="@+id/filter_count"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentEnd="true"
android:padding="@dimen/keyline_first"
android:gravity="center_vertical|end" />
<View
android:id="@+id/divider"
style="@style/horizontal_divider"
android:visibility="gone"
android:layout_alignParentTop="true" />
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:tint="@color/icon_tint_with_alpha"
android:padding="@dimen/keyline_first"
android:src="@drawable/ic_outline_add_24px"
android:scaleType="center" />
<TextView
android:id="@+id/filter_count"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentEnd="true"
android:padding="@dimen/keyline_first"
android:gravity="center_vertical|end" />
<TextView
android:id="@+id/name"
style="@style/TextAppearance"
android:layout_toEndOf="@id/icon"
android:layout_toStartOf="@id/filter_count"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingStart="@dimen/keyline_first"
android:paddingEnd="@dimen/keyline_first"
android:gravity="start|center_vertical"
android:textAlignment="viewStart"
android:textSize="18sp" />
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:tint="@color/icon_tint_with_alpha"
android:padding="@dimen/keyline_first"
android:src="@drawable/ic_outline_add_24px"
android:scaleType="center" />
<TextView
android:id="@+id/name"
style="@style/TextAppearance"
android:layout_toEndOf="@id/icon"
android:layout_toStartOf="@id/filter_count"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="@dimen/keyline_first"
android:gravity="start|center_vertical"
android:textAlignment="viewStart"
android:textSize="18sp" />
</RelativeLayout>
</RelativeLayout>
</LinearLayout>

Loading…
Cancel
Save