mirror of https://github.com/tasks/tasks
Drag and drop recycler view for task list
parent
d63424e0de
commit
3a5c61d680
@ -1,530 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2010 CommonsWare, LLC
|
||||
* Portions Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.todoroo.astrid.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Vibrator;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import com.todoroo.astrid.utility.Flags;
|
||||
|
||||
import org.tasks.R;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class DraggableListView extends ListView {
|
||||
|
||||
private static final int SWIPE_THRESHOLD = 40;
|
||||
|
||||
private static final int MOVEMENT_THRESHOLD = 30;
|
||||
|
||||
// --- drag status
|
||||
private float mTouchStartX, mTouchCurrentX, mTouchStartY, mTouchCurrentY;
|
||||
private boolean mDragging = false;
|
||||
|
||||
private int mDragPos; // which item is being dragged
|
||||
private int mFirstDragPos; // where was the dragged item originally
|
||||
private Point mDragPoint; // at what offset inside the item did the user grab it
|
||||
private Point mCoordOffset; // the difference between screen coordinates and coordinates in this view
|
||||
|
||||
// --- drag drawing
|
||||
private ImageView mDragView;
|
||||
private WindowManager mWindowManager;
|
||||
private WindowManager.LayoutParams mWindowParams;
|
||||
private int mUpperBound;
|
||||
private int mLowerBound;
|
||||
private int mHeight;
|
||||
private final Rect mTempRect = new Rect();
|
||||
private Bitmap mDragBitmap;
|
||||
private final int mTouchSlop;
|
||||
private int dragndropBackgroundColor = 0x00000000;
|
||||
|
||||
// --- listeners
|
||||
private DropListener mDropListener;
|
||||
private SwipeListener mSwipeListener;
|
||||
private GrabberClickListener mClickListener;
|
||||
|
||||
// --- other instance variables
|
||||
private int mItemHeightNormal = -1;
|
||||
private Thread dragThread = null;
|
||||
|
||||
// --- constructors
|
||||
|
||||
public DraggableListView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public DraggableListView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
|
||||
|
||||
if (attrs != null) {
|
||||
TypedArray a = getContext().obtainStyledAttributes(attrs,
|
||||
R.styleable.TouchListView, 0, 0);
|
||||
|
||||
mItemHeightNormal = a.getDimensionPixelSize(
|
||||
R.styleable.TouchListView_normal_height, 0);
|
||||
dragndropBackgroundColor = a.getColor(
|
||||
R.styleable.TouchListView_dragndrop_background, 0x00000000);
|
||||
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
setSelector(R.drawable.none);
|
||||
}
|
||||
|
||||
public void setItemHightNormal(int itemHeightNormal) {
|
||||
this.mItemHeightNormal = itemHeightNormal;
|
||||
}
|
||||
|
||||
/*
|
||||
* pointToPosition() doesn't consider invisible views, but we need to, so
|
||||
* implement a slightly different version.
|
||||
*/
|
||||
private int myPointToPosition(int x, int y) {
|
||||
Rect frame = mTempRect;
|
||||
final int count = getChildCount();
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
final View child = getChildAt(i);
|
||||
child.getHitRect(frame);
|
||||
if (frame.contains(x, y)) {
|
||||
return getFirstVisiblePosition() + i;
|
||||
}
|
||||
}
|
||||
return INVALID_POSITION;
|
||||
}
|
||||
|
||||
private int getItemForPosition(int y) {
|
||||
int adjustedy = y - mDragPoint.y - (mItemHeightNormal / 2);
|
||||
int pos = myPointToPosition(0, adjustedy);
|
||||
if (pos >= 0) {
|
||||
if (pos <= mFirstDragPos) {
|
||||
pos += 1;
|
||||
}
|
||||
} else if (adjustedy < 0) {
|
||||
pos = 0;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
private void adjustScrollBounds(int y) {
|
||||
if (y >= mHeight / 3) {
|
||||
mUpperBound = mHeight / 3;
|
||||
}
|
||||
if (y <= mHeight * 2 / 3) {
|
||||
mLowerBound = mHeight * 2 / 3;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore size and visibility for all list items
|
||||
*/
|
||||
private void unExpandViews() {
|
||||
for (int i = 0;; i++) {
|
||||
View v = getChildAt(i);
|
||||
if (v == null) {
|
||||
layoutChildren(); // force children to be recreated where needed
|
||||
v = getChildAt(i);
|
||||
if (v == null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ViewGroup.LayoutParams params = v.getLayoutParams();
|
||||
params.height = LayoutParams.WRAP_CONTENT;
|
||||
v.setLayoutParams(params);
|
||||
v.setVisibility(View.VISIBLE);
|
||||
v.setPadding(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust visibility and size to make it appear as though an item is being
|
||||
* dragged around and other items are making room for it: If dropping the
|
||||
* item would result in it still being in the same place, then make the
|
||||
* dragged listitem's size normal, but make the item invisible. Otherwise,
|
||||
* if the dragged list item is still on screen, make it as small as possible
|
||||
* and expand the item below the insert point. If the dragged item is not on
|
||||
* screen, only expand the item below the current insert point.
|
||||
*/
|
||||
private void doExpansion() {
|
||||
int childnum = mDragPos - getFirstVisiblePosition();
|
||||
if (mDragPos > mFirstDragPos) {
|
||||
childnum++;
|
||||
}
|
||||
|
||||
View first = getChildAt(mFirstDragPos - getFirstVisiblePosition());
|
||||
|
||||
for (int i = 0;; i++) {
|
||||
View vv = getChildAt(i);
|
||||
if (vv == null) {
|
||||
break;
|
||||
}
|
||||
int height = LayoutParams.WRAP_CONTENT;
|
||||
int marginTop = 0;
|
||||
int visibility = View.VISIBLE;
|
||||
if (vv.equals(first)) {
|
||||
// processing the item that is being dragged
|
||||
if (mDragPos == mFirstDragPos) {
|
||||
// hovering over the original location
|
||||
visibility = View.INVISIBLE;
|
||||
} else {
|
||||
// not hovering over it
|
||||
height = 1;
|
||||
}
|
||||
} else if (i == childnum) {
|
||||
if (mDragPos < getCount() - 1) {
|
||||
marginTop = mItemHeightNormal;
|
||||
height = 2 * mItemHeightNormal;
|
||||
}
|
||||
}
|
||||
|
||||
ViewGroup.LayoutParams params = vv.getLayoutParams();
|
||||
params.height = height;
|
||||
vv.setLayoutParams(params);
|
||||
vv.setVisibility(visibility);
|
||||
vv.setPadding(0, marginTop, 0, 0);
|
||||
}
|
||||
// Request re-layout since we changed the items layout
|
||||
// and not doing this would cause bogus hitbox calculation
|
||||
// in myPointToPosition
|
||||
layoutChildren();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
mTouchCurrentX = ev.getX();
|
||||
mTouchCurrentY = ev.getY();
|
||||
|
||||
int action = ev.getAction();
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
if(mDragging) {
|
||||
stopDragging();
|
||||
} else {
|
||||
if (dragThread != null && mClickListener != null) {
|
||||
dragThread.interrupt();
|
||||
dragThread = null;
|
||||
if (action == MotionEvent.ACTION_UP) {
|
||||
mClickListener.onClick(viewAtPosition());
|
||||
}
|
||||
}
|
||||
|
||||
else if (mSwipeListener != null &&
|
||||
Math.abs(mTouchCurrentY - mTouchStartY) < MOVEMENT_THRESHOLD) {
|
||||
int dragPos = pointToPosition((int)mTouchCurrentX, (int)mTouchCurrentY);
|
||||
if (mTouchCurrentX > mTouchStartX + SWIPE_THRESHOLD) {
|
||||
mSwipeListener.swipeRight(dragPos);
|
||||
} else if (mTouchCurrentX < mTouchStartX - SWIPE_THRESHOLD) {
|
||||
mSwipeListener.swipeLeft(dragPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(dragThread != null) {
|
||||
dragThread.interrupt();
|
||||
dragThread = null;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
dragThread = new Thread(new DragRunnable(ev));
|
||||
dragThread.start();
|
||||
stopDragging();
|
||||
|
||||
mTouchStartX = ev.getX();
|
||||
mTouchStartY = ev.getY();
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if(mDragging) {
|
||||
dragView(ev);
|
||||
}
|
||||
|
||||
// detect scrolling
|
||||
if(dragThread != null && (Math.abs(mTouchCurrentX - mTouchStartX) > MOVEMENT_THRESHOLD ||
|
||||
Math.abs(mTouchCurrentY - mTouchStartY) > MOVEMENT_THRESHOLD)) {
|
||||
dragThread.interrupt();
|
||||
dragThread = null;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if(mDragging) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onTouchEvent(ev);
|
||||
}
|
||||
|
||||
private View viewAtPosition() {
|
||||
int itemNum = pointToPosition((int) mTouchCurrentX, (int) mTouchCurrentY);
|
||||
|
||||
if (itemNum == AdapterView.INVALID_POSITION) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getChildAt(itemNum - getFirstVisiblePosition());
|
||||
}
|
||||
|
||||
// --- drag logic
|
||||
|
||||
private class DragRunnable implements Runnable {
|
||||
|
||||
private final MotionEvent ev;
|
||||
|
||||
public DragRunnable(MotionEvent ev) {
|
||||
this.ev = ev;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(300L);
|
||||
|
||||
post(() -> initiateDrag(ev));
|
||||
|
||||
Thread.sleep(1000L);
|
||||
|
||||
post(() -> {
|
||||
stopDragging();
|
||||
dragThread = null;
|
||||
Vibrator v = (Vibrator) getContext().getSystemService(
|
||||
Context.VIBRATOR_SERVICE);
|
||||
v.vibrate(50);
|
||||
mClickListener.onLongClick(viewAtPosition());
|
||||
});
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
// bye!
|
||||
Timber.v(e, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initiateDrag(MotionEvent ev) {
|
||||
int x = (int) mTouchCurrentX;
|
||||
int y = (int) mTouchCurrentY;
|
||||
int itemNum = pointToPosition(x, y);
|
||||
|
||||
if (itemNum == AdapterView.INVALID_POSITION) {
|
||||
return;
|
||||
}
|
||||
|
||||
View item = getChildAt(itemNum - getFirstVisiblePosition());
|
||||
|
||||
mDragPoint = new Point(x - item.getLeft(), y - item.getTop());
|
||||
mCoordOffset = new Point((int)ev.getRawX() - x, (int)ev.getRawY() - y);
|
||||
|
||||
item.setDrawingCacheEnabled(true);
|
||||
|
||||
// Create a copy of the drawing cache so that it does not get
|
||||
// recycled by the framework when the list tries to clean up memory
|
||||
Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
|
||||
item.setDrawingCacheEnabled(false);
|
||||
|
||||
Rect listBounds = new Rect();
|
||||
|
||||
getGlobalVisibleRect(listBounds, null);
|
||||
|
||||
startDragging(bitmap, listBounds.left, y);
|
||||
mDragPos = itemNum;
|
||||
mFirstDragPos = mDragPos;
|
||||
mHeight = getHeight();
|
||||
|
||||
int touchSlop = mTouchSlop;
|
||||
mUpperBound = Math.min(y - touchSlop, mHeight / 3);
|
||||
mLowerBound = Math.max(y + touchSlop, mHeight * 2 / 3);
|
||||
|
||||
Vibrator v = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
v.vibrate(50);
|
||||
}
|
||||
|
||||
private void startDragging(Bitmap bm, int x, int y) {
|
||||
mWindowParams = new WindowManager.LayoutParams();
|
||||
mWindowParams.gravity = Gravity.TOP | Gravity.START;
|
||||
mWindowParams.x = x;
|
||||
mWindowParams.y = y - mDragPoint.y + mCoordOffset.y;
|
||||
|
||||
mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
|
||||
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
|
||||
mWindowParams.format = PixelFormat.TRANSLUCENT;
|
||||
mWindowParams.windowAnimations = 0;
|
||||
|
||||
ImageView v = new ImageView(getContext());
|
||||
v.setBackgroundColor(dragndropBackgroundColor);
|
||||
v.setImageBitmap(bm);
|
||||
mDragBitmap = bm;
|
||||
|
||||
mWindowManager = (WindowManager) getContext().getSystemService(
|
||||
Context.WINDOW_SERVICE);
|
||||
mWindowManager.addView(v, mWindowParams);
|
||||
mDragView = v;
|
||||
mDragging = true;
|
||||
Flags.set(Flags.TLFP_NO_INTERCEPT_TOUCH);
|
||||
}
|
||||
|
||||
private void dragView(MotionEvent ev) {
|
||||
int x = (int) ev.getX();
|
||||
int y = (int) ev.getY();
|
||||
|
||||
mWindowParams.y = y - mDragPoint.y + mCoordOffset.y;
|
||||
|
||||
if (mDragPos == mFirstDragPos && x > mTouchStartX + SWIPE_THRESHOLD) {
|
||||
mDragView.setPadding(30, 1, 0, 1);
|
||||
} else if (mDragPos == mFirstDragPos && x < mTouchStartX - SWIPE_THRESHOLD) {
|
||||
mDragView.setPadding(-30, 2, 0, 2);
|
||||
} else {
|
||||
mDragView.setPadding(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
mWindowManager.updateViewLayout(mDragView, mWindowParams);
|
||||
|
||||
int itemnum = getItemForPosition(y);
|
||||
if (itemnum >= 0) {
|
||||
if (ev.getAction() == MotionEvent.ACTION_DOWN
|
||||
|| itemnum != mDragPos) {
|
||||
mDragPos = itemnum;
|
||||
doExpansion();
|
||||
}
|
||||
int speed = 0;
|
||||
adjustScrollBounds(y);
|
||||
if (y > mLowerBound) {
|
||||
// scroll the list up a bit
|
||||
speed = y > (mHeight + mLowerBound) / 2 ? 16 : 4;
|
||||
} else if (y < mUpperBound) {
|
||||
// scroll the list down a bit
|
||||
speed = y < mUpperBound / 2 ? -16 : -4;
|
||||
}
|
||||
if (speed != 0) {
|
||||
int ref = pointToPosition(0, mHeight / 2);
|
||||
if (ref == AdapterView.INVALID_POSITION) {
|
||||
// we hit a divider or an invisible view, check
|
||||
// somewhere else
|
||||
ref = pointToPosition(0, mHeight / 2
|
||||
+ getDividerHeight() + 64);
|
||||
}
|
||||
View v = getChildAt(ref - getFirstVisiblePosition());
|
||||
if (v != null) {
|
||||
int pos = v.getTop();
|
||||
setSelectionFromTop(ref, pos - speed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void stopDragging() {
|
||||
if (mDragBitmap != null) {
|
||||
mDragBitmap.recycle();
|
||||
mDragBitmap = null;
|
||||
}
|
||||
|
||||
unExpandViews();
|
||||
|
||||
if (mDragView != null) {
|
||||
WindowManager wm = (WindowManager) getContext().getSystemService(
|
||||
Context.WINDOW_SERVICE);
|
||||
wm.removeView(mDragView);
|
||||
mDragView.setImageDrawable(null);
|
||||
mDragView = null;
|
||||
}
|
||||
|
||||
if(mDragging) {
|
||||
if (mSwipeListener != null && mDragPos == mFirstDragPos) {
|
||||
if (mTouchCurrentX > mTouchStartX + SWIPE_THRESHOLD) {
|
||||
mSwipeListener.swipeRight(mFirstDragPos);
|
||||
} else if (mTouchCurrentX < mTouchStartX - SWIPE_THRESHOLD) {
|
||||
mSwipeListener.swipeLeft(mFirstDragPos);
|
||||
}
|
||||
} else if(mDropListener != null && mDragPos != mFirstDragPos &&
|
||||
mDragPos >= 0 && mDragPos < getCount()) {
|
||||
if(mFirstDragPos < mDragPos) {
|
||||
mDragPos++;
|
||||
}
|
||||
mDropListener.drop(mFirstDragPos, mDragPos);
|
||||
}
|
||||
}
|
||||
|
||||
mDragging = false;
|
||||
Flags.checkAndClear(Flags.TLFP_NO_INTERCEPT_TOUCH);
|
||||
}
|
||||
|
||||
// --- getters and setters
|
||||
|
||||
public void setDropListener(DropListener l) {
|
||||
mDropListener = l;
|
||||
}
|
||||
|
||||
public void setSwipeListener(SwipeListener l) {
|
||||
mSwipeListener = l;
|
||||
}
|
||||
|
||||
public void setClickListener(GrabberClickListener listener) {
|
||||
this.mClickListener = listener;
|
||||
}
|
||||
|
||||
public void setDragndropBackgroundColor(int color) {
|
||||
this.dragndropBackgroundColor = color;
|
||||
}
|
||||
|
||||
@Override
|
||||
final public void addHeaderView(View v, Object data, boolean isSelectable) {
|
||||
throw new RuntimeException(
|
||||
"Headers are not supported with TouchListView");
|
||||
}
|
||||
|
||||
@Override
|
||||
final public void addHeaderView(View v) {
|
||||
throw new RuntimeException(
|
||||
"Headers are not supported with TouchListView");
|
||||
}
|
||||
|
||||
public interface GrabberClickListener {
|
||||
void onClick(View v);
|
||||
void onLongClick(View v);
|
||||
}
|
||||
|
||||
public interface DropListener {
|
||||
void drop(int from, int to);
|
||||
}
|
||||
public interface SwipeListener {
|
||||
void swipeLeft(int which);
|
||||
void swipeRight(int which);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,134 @@
|
||||
package org.tasks.tasklist;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Canvas;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.todoroo.andlib.data.TodorooCursor;
|
||||
import com.todoroo.astrid.adapter.TaskAdapter;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.utility.Flags;
|
||||
|
||||
import org.tasks.R;
|
||||
|
||||
public class TaskListRecyclerAdapter extends RecyclerView.Adapter<ViewHolder> {
|
||||
|
||||
private final Context context;
|
||||
private final TaskAdapter adapter;
|
||||
private final ViewHolderFactory viewHolderFactory;
|
||||
private final ItemTouchHelper itemTouchHelper;
|
||||
|
||||
public TaskListRecyclerAdapter(Context context, TaskAdapter adapter, ViewHolderFactory viewHolderFactory) {
|
||||
this.context = context;
|
||||
this.adapter = adapter;
|
||||
this.viewHolderFactory = viewHolderFactory;
|
||||
itemTouchHelper = new ItemTouchHelper(new ItemTouchHelperCallback());
|
||||
}
|
||||
|
||||
public void applyToRecyclerView(RecyclerView recyclerView) {
|
||||
recyclerView.setAdapter(this);
|
||||
itemTouchHelper.attachToRecyclerView(recyclerView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
ViewGroup view = (ViewGroup) LayoutInflater.from(context)
|
||||
.inflate(R.layout.task_adapter_row_simple, parent, false);
|
||||
return viewHolderFactory.newViewHolder(view, adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
Cursor cursor = adapter.getCursor();
|
||||
cursor.moveToPosition(position);
|
||||
holder.bindView((TodorooCursor<Task>) cursor);
|
||||
holder.setIndent(adapter.getIndent(holder.task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return adapter.getCount();
|
||||
}
|
||||
|
||||
private class ItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
||||
|
||||
private int from = -1;
|
||||
private int to = -1;
|
||||
|
||||
@Override
|
||||
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
||||
if (!adapter.isManuallySorted()) {
|
||||
return makeMovementFlags(0, 0);
|
||||
}
|
||||
|
||||
ViewHolder vh = (ViewHolder) viewHolder;
|
||||
int indentFlags = 0;
|
||||
if (vh.isIndented()) {
|
||||
indentFlags |= ItemTouchHelper.LEFT;
|
||||
}
|
||||
int position = vh.getAdapterPosition();
|
||||
if (position > 0 && adapter.canIndent(position, vh.task)) {
|
||||
indentFlags |= ItemTouchHelper.RIGHT;
|
||||
}
|
||||
return makeMovementFlags(ItemTouchHelper.UP | ItemTouchHelper.DOWN, indentFlags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
|
||||
super.onSelectedChanged(viewHolder, actionState);
|
||||
Flags.set(Flags.TLFP_NO_INTERCEPT_TOUCH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
|
||||
int fromPosition = source.getAdapterPosition();
|
||||
int toPosition = target.getAdapterPosition();
|
||||
if (from == -1) {
|
||||
from = fromPosition;
|
||||
}
|
||||
to = toPosition;
|
||||
notifyItemMoved(fromPosition, toPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) {
|
||||
return .2f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
|
||||
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
|
||||
float shiftSize = ((ViewHolder) viewHolder).getShiftSize();
|
||||
dX = Math.max(-shiftSize, Math.min(shiftSize, dX));
|
||||
}
|
||||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
||||
super.clearView(recyclerView, viewHolder);
|
||||
|
||||
if (from >= 0 && from != to) {
|
||||
if (from < to) {
|
||||
to++;
|
||||
}
|
||||
adapter.moved(from, to);
|
||||
}
|
||||
from = -1;
|
||||
to = -1;
|
||||
Flags.checkAndClear(Flags.TLFP_NO_INTERCEPT_TOUCH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
|
||||
adapter.indented(
|
||||
viewHolder.getAdapterPosition(),
|
||||
direction == ItemTouchHelper.RIGHT ? 1 : -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
** Copyright (c) 2012 Todoroo Inc
|
||||
**
|
||||
** See the file "LICENSE" for the full license governing this code.
|
||||
-->
|
||||
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/row"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="1px"
|
||||
app:cardBackgroundColor="?attr/asContentBackground"
|
||||
app:cardCornerRadius="0dp"
|
||||
app:cardElevation="?attr/card_elevation"
|
||||
app:cardPreventCornerOverlap="false">
|
||||
|
||||
<include layout="@layout/task_adapter_row_body" />
|
||||
|
||||
</android.support.v7.widget.CardView>
|
||||
@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/rowBody"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:paddingBottom="5dp"
|
||||
android:paddingTop="5dp">
|
||||
|
||||
<com.todoroo.astrid.ui.CheckableImageView
|
||||
android:id="@+id/completeBox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:gravity="center"
|
||||
android:paddingEnd="@dimen/keyline_second"
|
||||
android:paddingLeft="@dimen/keyline_first"
|
||||
android:paddingRight="@dimen/keyline_second"
|
||||
android:paddingStart="@dimen/keyline_first"
|
||||
android:scaleType="center" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/task_row"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toEndOf="@id/completeBox"
|
||||
android:layout_toRightOf="@id/completeBox"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- row 1 -->
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="100"
|
||||
android:ellipsize="end"
|
||||
android:gravity="start|center_vertical"
|
||||
android:maxLines="1"
|
||||
android:paddingEnd="@dimen/keyline_first"
|
||||
android:paddingLeft="0dp"
|
||||
android:paddingRight="@dimen/keyline_first"
|
||||
android:paddingStart="0dp"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/taskActionContainer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="end|center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:paddingEnd="@dimen/keyline_first"
|
||||
android:paddingLeft="0dp"
|
||||
android:paddingRight="@dimen/keyline_first"
|
||||
android:paddingStart="0dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/taskActionIcon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top"
|
||||
android:alpha="?attr/alpha_secondary"
|
||||
android:scaleType="center"
|
||||
android:tint="?attr/icon_tint" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- row 2 -->
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tag_block"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:ellipsize="none"
|
||||
android:maxLines="1"
|
||||
android:paddingEnd="@dimen/keyline_first"
|
||||
android:paddingLeft="0dp"
|
||||
android:paddingRight="@dimen/keyline_first"
|
||||
android:paddingStart="0dp"
|
||||
android:singleLine="true" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/due_date"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignLeft="@id/tag_block"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignStart="@id/tag_block"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingEnd="@dimen/keyline_first"
|
||||
android:paddingLeft="0dp"
|
||||
android:paddingRight="@dimen/keyline_first"
|
||||
android:paddingStart="0dp"
|
||||
android:singleLine="true" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
@ -1,16 +1,20 @@
|
||||
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<org.tasks.ui.DraggableSwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/swipe_layout"
|
||||
style="@style/task_list_container"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:elevation="@dimen/elevation_task_list"
|
||||
tools:ignore="UnusedAttribute">
|
||||
|
||||
<ListView
|
||||
android:id="@android:id/list"
|
||||
style="@style/TaskListView"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" />
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars="vertical"
|
||||
android:scrollbarStyle="outsideOverlay"
|
||||
android:paddingBottom="@dimen/task_list_footer_height"
|
||||
android:elevation="@dimen/elevation_task_list"
|
||||
android:background="?attr/asWindowBackground"
|
||||
android:clipToPadding="false"/>
|
||||
|
||||
</android.support.v4.widget.SwipeRefreshLayout>
|
||||
</org.tasks.ui.DraggableSwipeRefreshLayout>
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
<org.tasks.ui.DraggableSwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/swipe_layout"
|
||||
style="@style/task_list_container"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="@dimen/elevation_task_list"
|
||||
tools:ignore="UnusedAttribute">
|
||||
|
||||
<com.todoroo.astrid.ui.DraggableListView
|
||||
android:id="@android:id/list"
|
||||
style="@style/TaskListView"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:drawSelectorOnTop="false"/>
|
||||
|
||||
</org.tasks.ui.DraggableSwipeRefreshLayout>
|
||||
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/task_list_footer_height"
|
||||
android:orientation="vertical" />
|
||||
Loading…
Reference in New Issue