package com.bigfat.listviewdragdemo;
/*
* Copyright (c) 2014-2015 Zhang Hai
*
* 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.
*/
import android.animation.ObjectAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.DragEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
// TODO: Provide scrolling at edge, see
// https://github.com/justasm/DragLinearLayout/blob/master/library/src/main/java/com/jmedeisis/draglinearlayout/DragLinearLayout.java
// and
// https://github.com/nhaarman/ListViewAnimations/blob/master/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/dragdrop/DragAndDropHandler.java
public class DragUtils {
private static final String TAG = DragUtils.class.getSimpleName();
public static int screenWidth;
public static int screenHeight;
public static int pageEdgeVisibleWidth;//非当前page边缘可见宽度
public static int pageMargin;//每一页左/右边距
public static int pageScrollWidth;//滚动时每一页的滚动距离
public static int pageExchangeDelay = 800;//拖拽至边界时切换页面的响应延迟
public static int itemExchangeDelay = 500;//item切换响应延迟
private static Runnable runnable = null;
static {
screenWidth = App.getContext().getResources().getDisplayMetrics().widthPixels;
screenHeight = App.getContext().getResources().getDisplayMetrics().heightPixels;
pageEdgeVisibleWidth = App.getContext().getResources().getDimensionPixelOffset(R.dimen.page_edge_visible_width);
pageMargin = App.getContext().getResources().getDimensionPixelOffset(R.dimen.page_margin);
pageScrollWidth = screenWidth - pageMargin * 2 - pageEdgeVisibleWidth * 2;
}
private DragUtils() {
}
public static void setupDragSort(final MyHorizontalScrollView scrollView, View view, final RecyclerView recyclerView) {
final SimpleRecyclerViewAdapter adapter = (SimpleRecyclerViewAdapter) recyclerView.getAdapter();
final LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager();
final ArrayList<String> data = adapter.getData();
view.setOnDragListener(new View.OnDragListener() {
@Override
public boolean onDrag(final View view, final DragEvent event) {
final ViewGroup viewGroup = (ViewGroup) view.getParent();
final DragState dragState = (DragState) event.getLocalState();
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
if (view == dragState.view) {
adapter.setDragPosition(viewGroup.indexOfChild(view));
dragState.view.setVisibility(View.INVISIBLE);
}
if (runnable != null) {
recyclerView.removeCallbacks(runnable);
runnable = null;
}
return true;
case DragEvent.ACTION_DRAG_LOCATION:
if (view != dragState.view) {
final int position = manager.getPosition(view);
//拖拽到达列表边界时,令RecyclerView滚动
if (position >= manager.findLastCompletelyVisibleItemPosition() - 1) {//向下滚
recyclerView.scrollBy(0, view.getHeight());
} else if (position <= manager.findFirstCompletelyVisibleItemPosition() + 1) {//向上滚
recyclerView.scrollBy(0, -view.getHeight());
}
//处理View拖拽交换
if (runnable != null) {
recyclerView.removeCallbacks(runnable);
runnable = null;
}
runnable = new Runnable() {
@Override
public void run() {
// Log.i(TAG, "position--->" + position);
// Log.i(TAG, "dragState.position--->" + dragState.position);
String text = data.get(dragState.position);
data.remove(dragState.position);
data.add(position, text);
adapter.setDragPosition(position);
adapter.notifyItemRemoved(dragState.position);
adapter.notifyItemInserted(position);
if (position == 0) {
recyclerView.scrollToPosition(0);
}
dragState.position = position;
}
};
recyclerView.postDelayed(runnable, itemExchangeDelay);
}
break;
case DragEvent.ACTION_DRAG_ENDED:
Log.i(TAG, "ACTION_DRAG_ENDED");
if (runnable != null) {
recyclerView.removeCallbacks(runnable);
runnable = null;
}
adapter.setDragPosition(-1);
adapter.notifyDataSetChanged();
break;
}
return true;
}
});
view.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
view.startDrag(null, new View.DragShadowBuilder(view), new DragState(view, DragViewType.TASK, manager.getPosition(view)), 0);
scrollView.removeDragEvent(DragViewType.SECTION);
return true;
}
});
}
public static void setupDragSortHorizontal(final MyHorizontalScrollView scrollView, View view, final DragViewType type) {
view.setOnDragListener(new View.OnDragListener() {
@Override
public boolean onDrag(final View view, final DragEvent event) {
final ViewGroup viewGroup = (ViewGroup) view.getParent();
final DragState dragState = (DragState) event.getLocalState();
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
if (view == dragState.view) {
view.setVisibility(View.INVISIBLE);
}
if (runnable != null) {
scrollView.removeCallbacks(runnable);
runnable = null;
}
return true;
case DragEvent.ACTION_DRAG_LOCATION:
if (view == dragState.view) {
if ((event.getX() > view.getWidth() / 8 * 7)
|| (event.getX() < view.getWidth() / 8)) {
final int index = event.getX() > view.getWidth() / 2 ? viewGroup.indexOfChild(view) + 1 : viewGroup.indexOfChild(view) - 1;
if (viewGroup.getChildAt(index) != null) {
if (runnable == null) {
runnable = new Runnable() {
@Override
public void run() {
Log.i(TAG, "runnable--->scrollToPage--->" + index);
scrollView.scrollToPage(index);
scrollView.postDelayed(new Runnable() {
@Override
public void run() {
swapHorizontalViews(viewGroup, viewGroup.getChildAt(index), index, dragState);
}
}, 50);
runnable = null;
}
};
scrollView.postDelayed(runnable, pageExchangeDelay);
}
}
} else if (runnable != null) {
scrollView.removeCallbacks(runnable);
runnable = null;
}
}
break;
case DragEvent.ACTION_DRAG_ENDED:
if (view == dragState.view) {
view.setVisibility(View.VISIBLE);
}
if (runnable != null) {
scrollView.removeCallbacks(runnable);
runnable = null;
}
scrollView.initDragEvent();
break;
}
return true;
}
});
view.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
scrollView.removeDragEvent(DragViewType.TASK);
view.startDrag(null, new View.DragShadowBuilder(view), new DragState(view, type, ((ViewGroup) view.getParent()).indexOfChild(view)), 0);
return true;
}
});
}
public static int getDuration(View view) {
return view.getResources().getInteger(android.R.integer.config_shortAnimTime);
}
public static void postOnPreDraw(View view, final Runnable runnable) {
final ViewTreeObserver observer = view.getViewTreeObserver();
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if (observer.isAlive()) {
observer.removeOnPreDrawListener(this);
}
runnable.run();
return true;
}
});
}
public static void swapViewGroupChildren(ViewGroup firstGroup, ViewGroup secondGroup, View firstView, View secondView) {
int firstIndex = firstGroup.indexOfChild(firstView);
int secondIndex = secondGroup.indexOfChild(secondView);
if (firstIndex < secondIndex) {
secondGroup.removeViewAt(secondIndex);
firstGroup.removeViewAt(firstIndex);
firstGroup.addView(secondView, firstIndex);
secondGroup.addView(firstView, secondIndex);
} else {
firstGroup.removeViewAt(firstIndex);
secondGroup.removeViewAt(secondIndex);
secondGroup.addView(firstView, secondIndex);
firstGroup.addView(secondView, firstIndex);
}
}
private static void swapHorizontalViews(ViewGroup viewGroup, final View view, int position,
DragUtils.DragState dragState) {
final float viewX = view.getX();
swapHorizontalViewGroupChildren(viewGroup, view, dragState.view);
dragState.position = position;
DragUtils.postOnPreDraw(view, new Runnable() {
@Override
public void run() {
ObjectAnimator animator = ObjectAnimator
.ofFloat(view, View.X, viewX, view.getLeft())
.setDuration(DragUtils.getDuration(view));
animator.setInterpolator(new LinearInterpolator());
animator.start();
}
});
}
public static void swapHorizontalViewGroupChildren(ViewGroup viewGroup, View firstView, View secondView) {
int firstIndex = viewGroup.indexOfChild(firstView);
int secondIndex = viewGroup.indexOfChild(secondView);
if (firstIndex < secondIndex) {
viewGroup.removeViewAt(secondIndex);
viewGroup.removeViewAt(firstIndex);
viewGroup.addView(secondView, firstIndex);
viewGroup.addView(firstView, secondIndex);
} else {
viewGroup.removeViewAt(firstIndex);
viewGroup.removeViewAt(secondIndex);
viewGroup.addView(firstView, secondIndex);
viewGroup.addView(secondView, firstIndex);
}
}
public enum DragViewType {
SECTION,//阶段
TASK,//任务
}
public static interface DragListener {
public void onDragStarted();
public void onDragEnded();
}
public static interface OnDragDeletedListener {
public void onDragDeleted();
}
public static class DragState {
public View view;
public DragViewType type;
public int position;
public DragState(View view, DragViewType type, int position) {
this.view = view;
this.type = type;
this.position = position;
}
}
}