public class CleerScrollHorizontalView extends CleerDividerLinearLayout {
private static final String TAG = "Cleer.ScrollView";
private static final boolean DEBUG = true;
private static final int MAX_DRAG_LENGTH = 316;
private static final int START_SCROLL_LENGTH = MAX_DRAG_LENGTH / 2;
private static int DRAG_NOT_CLICK_SIGN_LENGTH;
private static final int SCROLL_ANIM_DURATION = 850;
private static final int COLOR_RED_NORMAL = 0xffe70909;
private static final int COLOR_RED_PRESSED = 0x5fe70909;
private Scroller mScroller;
private Paint deleteBgPaint;
private Paint deleteIconPaint;
private Bitmap deleteBitmap;
private int layoutWidth;
private int layoutHeight;
private int deleteWidth;
private int deleteHeight;
private boolean deleteClickSign = false;
private boolean itemExceptDeleteClickSign = false;
private boolean itemClickSign = false;
private boolean deleteClickAnimation = false;
private boolean needShowDeleteView = false;
private int currX;
private int prevX;
private int origX;
private boolean intercept;
//For log
private String logTag = TAG;
private OnDeleteClickListener listener;
private OnClickListener onClickListener;
public void setLogTag(String tag) {
logTag = tag;
}
public interface OnDeleteClickListener {
void onClick();
}
public void setOnDeleteClickListener(OnDeleteClickListener listener) {
this.listener = listener;
}
public void setOnClickListener(OnClickListener listener) {
onClickListener = listener;
}
public CleerScrollHorizontalView(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context, new DecelerateInterpolator());
deleteBgPaint = new Paint();
deleteIconPaint = new Paint();
deleteBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon_item_delete);
DRAG_NOT_CLICK_SIGN_LENGTH = ViewConfiguration.get(context).getScaledTouchSlop();
//Force layout to invoke onDraw
setWillNotDraw(false);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
layoutWidth = this.getMeasuredWidth();
layoutHeight = this.getMeasuredHeight();
deleteWidth = deleteBitmap.getWidth();
deleteHeight = deleteBitmap.getHeight();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
deleteBgPaint.setColor(deleteClickAnimation ? COLOR_RED_PRESSED : COLOR_RED_NORMAL);
//Draw deleteBg
canvas.drawRect(layoutWidth, 0, layoutWidth + MAX_DRAG_LENGTH, layoutHeight, deleteBgPaint);
//Draw deleteIcon
int deleteLeft = layoutWidth + (MAX_DRAG_LENGTH - deleteWidth) / 2;
int deleteTop = (layoutHeight - deleteHeight) / 2;
canvas.drawBitmap(deleteBitmap, deleteLeft, deleteTop, deleteIconPaint);
}
private boolean isTouchPointInDeleteView(int x, int y) {
return x > layoutWidth && x <= layoutWidth + MAX_DRAG_LENGTH
&& y >= 0 && y <= layoutHeight;
}
private boolean isTouchPointInItemNotDeleteView(int x, int y) {
return x >= 0 && x <= layoutWidth
&& y >= 0 && y <= layoutHeight;
}
/** Hide the deleteView */
private void hideDeleteView() {
startScroll(getScrollX(), 0, -getScrollX(), 0, SCROLL_ANIM_DURATION);
}
/** Hide all the layouts except itself */
private void triggerInitAllOthersLayouts() {
if (getParent() instanceof ViewGroup) {
ViewGroup parent = (ViewGroup) getParent();
for (int i = 0; i < parent.getChildCount(); ++i) {
View child = parent.getChildAt(i);
if (child instanceof CleerScrollHorizontalView && !child.equals(this)) {
CleerScrollHorizontalView c = (CleerScrollHorizontalView) child;
c.hideDeleteView();
}
}
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
if (DEBUG)
Log.v("dispatchTouchEvent", "logTag->" + logTag + ", ACTION_DOWN->" + ev.getX());
triggerInitAllOthersLayouts();
mScroller.forceFinished(true);
origX = currX = prevX = (int) ev.getX();
needShowDeleteView = false;
deleteClickSign = isTouchPointInDeleteView((int) ev.getX() + getScrollX(), (int) ev.getY());
itemExceptDeleteClickSign = !deleteClickSign;
itemClickSign = true;
//For deleteView rendering, type:ACTION_DOWN
deleteClickAnimation = deleteClickSign;
invalidate();
intercept = false;
break;
}
case MotionEvent.ACTION_MOVE: {
if (DEBUG) Log.v("dispatchTouchEvent", "logTag->" + logTag + ", ACTION_MOVE");
//Scroll animation
currX = (int) ev.getX();
if (intercept) {
int dOffset = -(currX - prevX);
if (getScrollX() + dOffset < 0) {
if (DEBUG)
Log.v("dispatchTouchEvent", "logTag->" + logTag + ", ACTION_MOVE" + ", move type one");
scrollTo(0, 0);
} else if (getScrollX() + dOffset > MAX_DRAG_LENGTH) {
if (DEBUG)
Log.v("dispatchTouchEvent", "logTag->" + logTag + ", ACTION_MOVE" + ", move type two");
scrollTo(MAX_DRAG_LENGTH, 0);
} else {
if (DEBUG)
Log.v("dispatchTouchEvent", "logTag->" + logTag + ", ACTION_MOVE" + ", move type three->" + dOffset);
scrollBy(dOffset, 0);
}
}
//For deleteView rendering, type:ACTION_MOVE
deleteClickAnimation = isTouchPointInDeleteView((int) ev.getX() + getScrollX(), (int) ev.getY());
invalidate();
if (Math.abs(currX - origX) >= DRAG_NOT_CLICK_SIGN_LENGTH) {
//Disallow parent to intercept MotionEvent
getParent().requestDisallowInterceptTouchEvent(true);
intercept = true;
}
prevX = currX;
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
if (DEBUG) Log.v("dispatchTouchEvent", "logTag->" + logTag + ", ACTION_UP");
currX = (int) ev.getX();
if (Math.abs(currX - origX) >= DRAG_NOT_CLICK_SIGN_LENGTH) {
itemClickSign = false;
itemExceptDeleteClickSign = false;
}
if (deleteClickSign && isTouchPointInDeleteView((int) ev.getX() + getScrollX(), (int) ev.getY())) {
if (DEBUG) Log.v(TAG, "logTag->" + logTag + " deleteView clicked");
hideDeleteView();
if (listener != null) {
listener.onClick();
}
itemClickSign = true;
}
//For deleteView rendering, type:ACTION_UP
deleteClickAnimation = false;
invalidate();
//Scroll animation
if (getScrollX() > 0 && getScrollX() < START_SCROLL_LENGTH) {
needShowDeleteView = false;
}
if (getScrollX() > START_SCROLL_LENGTH) {
needShowDeleteView = true;
}
if (currX > origX) {
needShowDeleteView = false;
}
if (needShowDeleteView) {
if (DEBUG)
Log.v("dispatchTouchEvent", "logTag->" + logTag + ", ACTION_UP" + ", up to changed");
mScroller.forceFinished(true);
startScroll(getScrollX(), 0, MAX_DRAG_LENGTH - getScrollX(), 0, SCROLL_ANIM_DURATION);
} else {
if (DEBUG)
Log.v("dispatchTouchEvent", "logTag->" + logTag + ", ACTION_UP" + ", up to origin2");
mScroller.forceFinished(true);
startScroll(getScrollX(), 0, -getScrollX(), 0, SCROLL_ANIM_DURATION);
}
if (needShowDeleteView && itemClickSign) {
if (DEBUG)
Log.v("dispatchTouchEvent", "logTag->" + logTag + ", ACTION_UP" + ", up to origin3");
mScroller.forceFinished(true);
startScroll(getScrollX(), 0, -getScrollX(), 0, SCROLL_ANIM_DURATION);
}
break;
}
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (!itemClickSign) {
return true;
}
return super.onInterceptTouchEvent(ev) || intercept;
}
@Override
public boolean performClick() {
boolean r = super.performClick();
if (onClickListener != null) {
onClickListener.onClick(this);
return true;
}
return r;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (!intercept)
super.onTouchEvent(ev);
if (ev.getAction() == MotionEvent.ACTION_UP) {
if (itemExceptDeleteClickSign
&& isTouchPointInItemNotDeleteView((int) ev.getX() + getScrollX(), (int) ev.getY())
&& !isTouchPointInDeleteView((int) ev.getX() + getScrollX(), (int) ev.getY())) {
if (DEBUG) Log.v(TAG, "logTag->" + logTag + " itemExceptDeleteView clicked");
performClick();
itemExceptDeleteClickSign = false;
}
}
return true;
}
private void startScroll(int startX, int startY, int dx, int dy, int duration) {
int readDuration = Math.abs((int) (1.0f * duration / MAX_DRAG_LENGTH * dx));
mScroller.startScroll(startX, startY, dx, dy, readDuration);
invalidate();
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), 0);
postInvalidate();
}
}
}
复制代码
左滑显示删除按钮
最新推荐文章于 2023-02-27 09:58:10 发布