Created
May 12, 2014 12:30
-
-
Save lzsucceed/570ee8bdde36f3363dad to your computer and use it in GitHub Desktop.
TouchInterceptorGridView.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.lzy.music.parts; | |
/* | |
* Copyright (C) 2013 TInoue | |
* | |
* 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. | |
*/ | |
/* | |
* Original source 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. | |
*/ | |
import java.util.ArrayList; | |
import java.util.Timer; | |
import java.util.TimerTask; | |
import com.lzy.music.list.task.PostExecutor; | |
import android.app.Activity; | |
import android.content.Context; | |
import android.graphics.Bitmap; | |
import android.graphics.PixelFormat; | |
import android.graphics.Rect; | |
import android.graphics.drawable.Drawable; | |
import android.util.AttributeSet; | |
import android.util.Log; | |
import android.view.GestureDetector; | |
import android.view.Gravity; | |
import android.view.MotionEvent; | |
import android.view.View; | |
import android.view.ViewConfiguration; | |
import android.view.WindowManager; | |
import android.widget.AdapterView; | |
import android.widget.GridView; | |
import android.widget.ImageView; | |
public class TouchInterceptorGridView extends GridView { | |
private ImageView mDragView; | |
private WindowManager mWindowManager; | |
private WindowManager.LayoutParams mWindowParams; | |
/** | |
* At which position is the item currently being dragged. Note that this takes | |
* in to account header items. | |
*/ | |
private int mDragPos; | |
/** | |
* At which position was the item being dragged originally | |
*/ | |
private int mSrcDragPos; | |
private int mDragPointX; // at what x offset inside the item did the user | |
// grab it | |
private int mDragPointY; // at what y offset inside the item did the user | |
// grab it | |
private int mXOffset; // the difference between screen coordinates and | |
// coordinates in this view | |
private int mYOffset; // the difference between screen coordinates and | |
// coordinates in this view | |
private DragListener mDragListener; | |
private DropListener mDropListener; | |
private RemoveListener mRemoveListener; | |
private int mUpperBound; | |
private int mLowerBound; | |
private int mHeight; | |
private GestureDetector mGestureDetector; | |
private static final int FLING = 0; | |
private static final int SLIDE = 1; | |
private static final int TRASH = 2; | |
private int mRemoveMode = -1; | |
private Rect mTempRect = new Rect(); | |
private Bitmap mDragBitmap; | |
private final int mTouchSlop; | |
private Drawable mTrashcan; | |
public ArrayList<Integer> viewsheight = new ArrayList<Integer>(); | |
private Rect srcBound; | |
private View transparentView; | |
private View srcChild; | |
public TouchInterceptorGridView(Context context, AttributeSet attrs) { | |
super(context, attrs); | |
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); | |
mRemoveMode = FLING; | |
} | |
public TouchInterceptorGridView(Context context, AttributeSet attrs, int defStyle) { | |
super(context, attrs, defStyle); | |
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); | |
} | |
public static final int CUSTOM = 1; | |
public static final int NORMAL = 0; | |
public static final int OFF = -1; | |
private int mDragMode = OFF; | |
public void setDragMode(int mode) { | |
mDragMode = mode; | |
} | |
@Override | |
public boolean onInterceptTouchEvent(MotionEvent ev) { | |
if (mDragListener != null || mDropListener != null) { | |
final int x = (int) ev.getX(); | |
final int y = (int) ev.getY(); | |
final int itemnum = pointToPosition(x, y); | |
final View item = getChildAt(itemnum - getFirstVisiblePosition()); | |
switch (ev.getAction()) { | |
case MotionEvent.ACTION_MOVE: | |
switch (mDragMode) { | |
case CUSTOM: | |
if (!dragRequested) { | |
break; | |
} | |
item.setDrawingCacheEnabled(true); | |
Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache()); | |
Log.d("to", "createBitmap"); | |
item.setDrawingCacheEnabled(false); | |
startDragging(bitmap, x, y); | |
mDragPos = itemnum; | |
mSrcDragPos = mDragPos; | |
mHeight = getHeight(); | |
int touchSlop = mTouchSlop; | |
mUpperBound = Math.min(y - touchSlop, mHeight / 3); | |
mLowerBound = Math.max(y + touchSlop, mHeight * 2 / 3); | |
break; | |
} | |
case MotionEvent.ACTION_DOWN: | |
if (itemnum == AdapterView.INVALID_POSITION) { | |
break; | |
} | |
for (int i = 0; i < viewsheight.size() - 1; i++) { | |
viewsheight.set(i, getChildAt(i).getHeight()); | |
} | |
mDragPointX = x - item.getLeft(); | |
mDragPointY = y - item.getTop(); | |
mXOffset = ((int) ev.getRawX()) - x; | |
mYOffset = ((int) ev.getRawY()) - y; | |
switch (mDragMode) { | |
case OFF: | |
// The left side of the item is the grabber for dragging the | |
// item | |
if (item != null) { | |
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()); | |
Log.d("to", "createBitmap"); | |
item.setDrawingCacheEnabled(false); | |
startDragging(bitmap, x, y); | |
mDragPos = itemnum; | |
mSrcDragPos = mDragPos; | |
mHeight = getHeight(); | |
int touchSlop = mTouchSlop; | |
mUpperBound = Math.min(y - touchSlop, mHeight / 3); | |
mLowerBound = Math.max(y + touchSlop, mHeight * 2 / 3); | |
return false; | |
} | |
case CUSTOM: | |
afterDrag = new PostExecutor() { | |
@Override | |
public void onPostExecute(Object result) { | |
item.setDrawingCacheEnabled(true); | |
Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache()); | |
Log.d("to", "createBitmap"); | |
item.setDrawingCacheEnabled(false); | |
startDragging(bitmap, x, y); | |
mDragPos = itemnum; | |
mSrcDragPos = mDragPos; | |
mHeight = getHeight(); | |
int touchSlop = mTouchSlop; | |
mUpperBound = Math.min(y - touchSlop, mHeight / 3); | |
mLowerBound = Math.max(y + touchSlop, mHeight * 2 / 3); | |
} | |
}; | |
return false; | |
default: | |
break; | |
} | |
stopDragging(); | |
break; | |
} | |
} | |
return super.onInterceptTouchEvent(ev); | |
} | |
PostExecutor afterDrag; | |
private boolean dragRequested; | |
public void requestDragging(int pos) { | |
if (afterDrag != null) { | |
// preparation | |
if (transparentView != null) { | |
transparentView.setVisibility(VISIBLE); | |
transparentView = null; | |
} | |
int getFirst = getFirstVisiblePosition(); | |
int adjustPos = pos - getFirstVisiblePosition(); | |
srcBound = new Rect(); | |
srcChild = getChildAt(adjustPos); | |
srcChild.getHitRect(srcBound); | |
srcChild.setVisibility(INVISIBLE); | |
afterDrag.onPostExecute(null); | |
dragRequested = true; | |
} | |
} | |
private Timer scrollTimer; | |
private int prevSpeed; | |
private void unExpandViews(boolean deletion) { | |
for (int i = 0;; i++) { | |
// Log.d("to","unExpandViews"); | |
View v = getChildAt(i); | |
if (v == null) { | |
if (deletion) { | |
// HACK force update of mItemCount | |
Log.d("to", "hackpoint"); | |
int position = getFirstVisiblePosition(); | |
int y = getChildAt(0).getTop(); | |
setAdapter(getAdapter()); | |
// TODO listview 特有の命令 setSelectionFromTop(position, y); | |
// end hack | |
} | |
try { | |
layoutChildren(); // force children to be recreated where | |
// needed | |
v = getChildAt(i); | |
} catch (IllegalStateException ex) { | |
// layoutChildren throws this sometimes, presumably because | |
// we're | |
// in the process of being torn down but are still getting | |
// touch | |
// events | |
} | |
if (v == null) { | |
return; | |
} | |
} | |
v.setVisibility(View.VISIBLE); | |
} | |
} | |
private void dragView(int x, int y) { | |
if (mRemoveMode == SLIDE) { | |
float alpha = 1.0f; | |
int width = mDragView.getWidth(); | |
if (x > width / 2) { | |
alpha = ((float) (width - x)) / (width / 2); | |
} | |
mWindowParams.alpha = alpha; | |
} | |
if (mRemoveMode == FLING || mRemoveMode == TRASH) { | |
mWindowParams.x = x - mDragPointX + mXOffset; | |
} else { | |
mWindowParams.x = 0; | |
} | |
mWindowParams.y = y - mDragPointY + mYOffset; | |
mWindowManager.updateViewLayout(mDragView, mWindowParams); | |
if (mTrashcan != null) { | |
int width = mDragView.getWidth(); | |
if (y > getHeight() * 3 / 4) { | |
mTrashcan.setLevel(2); | |
} else if (width > 0 && x > width / 4) { | |
mTrashcan.setLevel(1); | |
} else { | |
mTrashcan.setLevel(0); | |
} | |
} | |
} | |
private void adjustScrollBounds(int y) { | |
if (y >= mHeight / 3) { | |
mUpperBound = mHeight / 3; | |
} | |
if (y <= mHeight * 2 / 3) { | |
mLowerBound = mHeight * 2 / 3; | |
} | |
} | |
@Override | |
public boolean onTouchEvent(MotionEvent ev) { | |
// Log.d("touch","ontouchevent"); | |
if (mGestureDetector != null) { | |
mGestureDetector.onTouchEvent(ev); | |
} | |
if ((mDragListener != null || mDropListener != null) && mDragView != null) { | |
int action = ev.getAction(); | |
switch (action) { | |
case MotionEvent.ACTION_UP: | |
if (scrollTimer != null) { | |
scrollTimer.cancel(); | |
} | |
case MotionEvent.ACTION_CANCEL: | |
if (scrollTimer != null) { | |
scrollTimer.cancel(); | |
} | |
Rect r = mTempRect; | |
Log.d("to", "action_cancel"); | |
mDragView.getDrawingRect(r); | |
stopDragging(); | |
if (mRemoveMode == SLIDE && ev.getX() > r.right * 3 / 4) { | |
if (mRemoveListener != null) { | |
// mRemoveListener.remove(mSrcDragPos); | |
} | |
Log.d("to", "unex true"); | |
unExpandViews(true); | |
} else { | |
if (mDropListener != null && mDragPos >= 0 && mDragPos < getCount()) { | |
dragRequested = false; | |
mDropListener.drop(mSrcDragPos, mDragPos); | |
} | |
Log.d("to", "unex false"); | |
unExpandViews(false); | |
} | |
break; | |
case MotionEvent.ACTION_DOWN: | |
// preparation | |
if (transparentView != null) { | |
transparentView.setVisibility(VISIBLE); | |
transparentView = null; | |
} | |
// Before dragging, saving srcBound to use for getting item's | |
// position user points later. | |
srcBound = new Rect(); | |
int cldCount = getChildCount(); | |
int srcPos = 0; | |
for (int i = cldCount - 1; i >= 0; i--) { | |
srcChild = getChildAt(i); | |
srcChild.getHitRect(srcBound); | |
if (srcBound.contains((int) ev.getX(), (int) ev.getY())) { | |
srcPos = i; | |
srcChild.setVisibility(INVISIBLE); | |
break; | |
} | |
} | |
Log.d("to", "srcPos" + srcPos + "getx: " + ev.getX() + " gety: " + ev.getY()); | |
break; | |
case MotionEvent.ACTION_MOVE: | |
int x = (int) ev.getX(); | |
int y = (int) ev.getY(); | |
dragView(x, y); | |
// getItemForPosition-like func which also can deal with various | |
// height items; | |
Rect rect = new Rect(); | |
int itemnum = INVALID_POSITION; | |
int cCount = getChildCount(); | |
View child = null; | |
for (int i = cCount - 1; i >= 0; i--) { | |
child = getChildAt(i); | |
// tell rect child's bound | |
child.getHitRect(rect); | |
// know which is bigger bound | |
boolean isBigger = false; | |
if (srcBound.height() < rect.height()) { | |
isBigger = true; | |
} | |
if (rect.contains(x, y)) { | |
if (mDragPos < itemnum) { | |
if (isBigger) { | |
int sub = rect.height() - srcBound.height(); | |
Rect check = new Rect(rect.left, rect.top + sub, rect.right, rect.bottom); | |
if (check.contains(x, y)) { | |
itemnum = getFirstVisiblePosition() + i; | |
} | |
} else { | |
itemnum = getFirstVisiblePosition() + i; | |
} | |
} else if (itemnum < mDragPos) { | |
if (isBigger) { | |
int sub = rect.height() - srcBound.height(); | |
Rect check = new Rect(rect.left, rect.top, rect.right, rect.bottom - sub); | |
if (check.contains(x, y)) | |
itemnum = getFirstVisiblePosition() + i; | |
} else { | |
itemnum = getFirstVisiblePosition() + i; | |
} | |
} | |
break; | |
} | |
} | |
if (itemnum >= 0) { | |
if (action == MotionEvent.ACTION_DOWN || itemnum != mDragPos) { | |
// inform posFrom and posTo to listener | |
if (mDragListener != null) { | |
if (transparentView != null) { | |
transparentView.setVisibility(VISIBLE); | |
} | |
srcChild.setVisibility(VISIBLE); | |
transparentView = child; | |
transparentView.setVisibility(INVISIBLE); | |
// users have to implement process exchanging | |
// adapter's item in listener | |
mDragListener.drag(mDragPos, itemnum); | |
} | |
} | |
mDragPos = itemnum; | |
// doExpansion(); | |
} | |
int speed = 0; | |
adjustScrollBounds(y); | |
if (y > mLowerBound) { | |
speed = y > (mHeight + mLowerBound) / 2 ? 16 : 4; | |
} else if (y < mUpperBound) { | |
// scroll the list down a bit | |
speed = y < mUpperBound / 2 ? -16 : -4; | |
if (getFirstVisiblePosition() == 0 && getChildAt(0).getTop() >= getPaddingTop()) { | |
// if we're already at the top, don't try to scroll, | |
// because | |
// it causes the framework to do some extra drawing | |
// that messes | |
// up our animation | |
speed = 0; | |
} | |
} | |
if (speed != 0) { | |
// smoothScrollBy(speed, 30); | |
if (prevSpeed == 0 || prevSpeed != speed) { | |
prevSpeed = speed; | |
doSmoothScroll(speed * 4, 30); | |
} | |
} else { | |
if (scrollTimer != null) { | |
prevSpeed = 0; | |
scrollTimer.cancel(); | |
} | |
} | |
break; | |
} | |
return true; | |
} | |
return super.onTouchEvent(ev); | |
} | |
private Activity ac; | |
public void setActivity(Activity ac) { | |
this.ac = ac; | |
} | |
private void doSmoothScroll(final int speed, final int duration) { | |
if (scrollTimer != null) { | |
scrollTimer.cancel(); | |
scrollTimer = null; | |
} | |
scrollTimer = new Timer(); | |
scrollTimer.schedule(new TimerTask() { | |
@Override | |
public void run() { | |
ac.runOnUiThread(new Runnable() { | |
@Override | |
public void run() { | |
smoothScrollBy(speed, duration); | |
} | |
}); | |
} | |
}, 0, 10); | |
} | |
private void startDragging(Bitmap bm, int x, int y) { | |
stopDragging(); | |
mWindowParams = new WindowManager.LayoutParams(); | |
mWindowParams.gravity = Gravity.TOP | Gravity.LEFT; | |
mWindowParams.x = x - mDragPointX + mXOffset; | |
mWindowParams.y = y - mDragPointY + mYOffset; | |
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 | |
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; | |
mWindowParams.format = PixelFormat.TRANSLUCENT; | |
mWindowParams.windowAnimations = 0; | |
Context context = getContext(); | |
ImageView v = new ImageView(context); | |
// int backGroundColor = | |
// context.getResources().getColor(R.color.); | |
// background color when dragging item | |
v.setBackgroundColor(0x00FF00); | |
// v.setBackgroundColor(backGroundColor); | |
// v.setBackgroundResource(R.drawable.); | |
v.setPadding(0, 0, 0, 0); | |
v.setImageBitmap(bm); | |
mDragBitmap = bm; | |
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); | |
mWindowManager.addView(v, mWindowParams); | |
mDragView = v; | |
} | |
private void stopDragging() { | |
if (mDragView != null) { | |
mDragView.setVisibility(GONE); | |
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); | |
wm.removeView(mDragView); | |
mDragView.setImageDrawable(null); | |
mDragView = null; | |
} | |
if (mDragBitmap != null) { | |
mDragBitmap.recycle(); | |
mDragBitmap = null; | |
} | |
if (mTrashcan != null) { | |
mTrashcan.setLevel(0); | |
} | |
} | |
public void setTrashcan(Drawable trash) { | |
mTrashcan = trash; | |
mRemoveMode = TRASH; | |
} | |
public void setDragListener(DragListener l) { | |
mDragListener = l; | |
} | |
public void setDropListener(DropListener l) { | |
mDropListener = l; | |
} | |
public void setRemoveListener(RemoveListener l) { | |
mRemoveListener = l; | |
} | |
public interface DragListener { | |
void drag(int from, int to); | |
} | |
public interface DropListener { | |
void drop(int from, int to); | |
} | |
public interface RemoveListener { | |
void remove(int which); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment