Android 사용자 정의 view 시리즈 99.99%QQ 사 이 드 슬라이드 삭제 효과 인 스 턴 스 코드 상세 설명

먼저 본 고 는 GitHub 에서'baoyongzhang'의 SwipeMenuListView 수정 을 바탕 으로 이 프로젝트 의 주 소 를 설명 합 니 다.
https://github.com/baoyongzhang/SwipeMenuListView
이 사 이 드 스 케 이 트 삭제 효 과 는 제 가 본 것 중 에 가장 효과 가 좋 고 유연 한 프로젝트 라 고 할 수 있 습 니 다.하나 도 없습니다!!
하지만 이 를 사용 하기 전에 두 가지 주의사항 을 제시 해 야 합 니 다.
1.이 프로젝트 는 Gradle dependence 를 지원 합 니 다.그러나 현재 작성 자가 제공 하 는 의존 주소 에 대응 하 는 프로젝트 는 최신 프로젝트 가 아 닙 니 다.의존 한 코드 는 demo 에서 사용 하 는 것 과 일치 하지 않 습 니 다.BaseSwipeListAdapter 라 는 종 류 는 다른 개발 자가 제출 한 것 이기 때문에 최신 코드 를 사용 하려 면 코드 를 다운로드 해 야 합 니 다.그리고 library 파일 을 자신의 프로젝트 에 복사 해서 사용 합 니 다.
아래 그림 은 현재 작가 가 제공 하 는 의존 주소 입 니 다.최신 코드 가 아 닙 니 다.최신 코드 를 사용 하고 싶 은 친 구 는 코드 를 직접 로 컬 로 다운로드 하 세 요.

2.두 번 째 주의사항 은 bug 라 고 할 수 있 습 니 다.만약 에 작가 가 준 demo 를 테스트 한 적 이 있다 면 어떤 아 이 템 이 끌 려 나 왔 다 면 이때 ListView 를 위로 또는 아래로 미 끄 러 뜨 려 서 끌 려 나 온 아 이 템 을 화면 에서 옮 긴 다음 에 다시 옮 기 면 끌 려 나 온 아 이 템 은 끌 려 나 오지 않 은 상태 로 바로 회 복 됩 니 다.이것 은 사용 자 를 곤 혹 스 럽 게 합 니 다.나 는 분명히 이 메뉴 를 꺼 냈 는데,왜 또 없어 졌 는 지,그리고 이 소프트웨어 가 만 든 진짜 쓰레기 같은 생각 이 들 어서,너의 소프트웨어 를 마 운 트 해제 할 수도 있다.다음 그림:

위의 두 가지 주의사항 에 대해 첫 번 째 는 할 말 이 없 는데 두 번 째 문 제 는 어떻게 합 니까?서 두 르 지 마라,이것 이 바로 우리 가 오늘 말 할 내용 이다.
우선 QQ 의 사 이 드 슬라이딩 삭제 효 과 를 연구 해 볼 수 있 습 니 다.이 쯤 되면 qq 를 열 어 구체 적 인 효 과 를 볼 수 있 습 니 다.
만약 에 아 이 템 이 끌 려 나 오 면 다른 아 이 템 에 손가락 을 올 렸 을 때 끌 려 나 온 아 이 템 을 먼저 닫 고 현재 동작 에 대한 후속 이벤트 도 응답 하지 않 습 니 다.다시 손가락 을 화면 에 올 리 지 않 으 면 관련 사건 이 울 립 니 다.만약 에 손가락 을 현재 끌 려 나 온 아 이 템 에 올 리 면 그 는 이 아 이 템 을 숨 기지 않 습 니 다.좌우 슬라이딩 이벤트 에 정상적으로 응답 할 수 있 습 니 다.
ok,QQ 의 효 과 는 우리 가 분석 을 마 쳤 습 니 다.우 리 는 그것 의 실현 원 리 를 연구 합 니 다.
1.아 이 템 이 끌 려 나 왔 다 면 다른 아 이 템 에 손가락 을 올 렸 을 때 끌 려 나 온 아 이 템 을 먼저 닫 습 니 다.어떻게 이 루어 집 니까?
우선 우 리 는 우리 가 현재 누 르 고 있 는 이 item 이 끌 려 나 온 그 item 인지 아 닌 지 를 판단 해 야 한다.그렇지 않 으 면 우 리 는 닫 아야 한다.그래 야 한다 면 상관 하지 않 아 도 된다.코드 는 다음 과 같다.

if (view instanceof SwipeMenuLayout) { 
SwipeMenuLayout touchView = (SwipeMenuLayout) view; 
if (!touchView.isOpen()) { 
mTouchView.smoothCloseMenu(); 
} 
}
2.그리고 현재 동작의 후속 사건 도 응답 하지 않 습 니 다.어떻게 실현 합 니까?
이것 은 매우 간단 합 니 다.view 의 사건 배포 원리 에 따라 어떤 터치 사건 에서 false 로 돌아 가면 이 사건 의 후속 사건 은 더 이상 그 에 게 맡 기지 않 습 니 다.즉,우리 가 ACTION 에 있다 면.DOWN 때 false 로 돌 아 왔 습 니 다.그러면 후속 ACTIONMOVE,ACTION_UP 등 이벤트 가 응답 하지 않 기 때문에 이 효 과 를 실현 하려 면 메뉴 를 닫 은 후에 false 로 돌아 가면 됩 니 다.완전한 코드 는 다음 과 같 습 니 다.

/********      ,    item         item,        item,   false.    down     , qq  ********/ 
if (view instanceof SwipeMenuLayout) { 
SwipeMenuLayout touchView = (SwipeMenuLayout) view; 
if (!touchView.isOpen()) { 
mTouchView.smoothCloseMenu(); 
return false; 
} 
}
이러한 몇 줄 코드 는 방금 분석 한 qq 효과 의 앞부분 효 과 를 실현 합 니 다.즉,한 아 이 템 이 끌 려 나 오 면 다른 아 이 템 에 손가락 을 올 렸 을 때 끌 려 나 온 아 이 템 을 직접 닫 고 현재 동작의 후속 사건 도 응답 하지 않 습 니 다.다시 손가락 을 화면 에 올 리 지 않 으 면 관련 사건 에 응답 할 수 있 습 니 다.
위의 몇 줄 의 코드 는 SwipeMenu ListView 류 의 수정 을 바탕 으로 한 것 입 니 다.완전한 수정 후의 SwipeMenu ListView 코드 는 다음 과 같 습 니 다.

package com.lanma.swipemenulistviewdemo.swipemenulistview; 
import android.content.Context; 
import android.util.AttributeSet; 
import android.util.TypedValue; 
import android.view.MotionEvent; 
import android.view.View; 
import android.view.animation.Interpolator; 
import android.widget.ListAdapter; 
import android.widget.ListView; 
/** 
* @author baoyz 
* @date 2014-8-18 
* qiang_xi   2016-09-07(  qq   ) 
*/ 
public class SwipeMenuListView extends ListView { 
private static final int TOUCH_STATE_NONE = 0; 
private static final int TOUCH_STATE_X = 1; 
private static final int TOUCH_STATE_Y = 2; 
public static final int DIRECTION_LEFT = 1; 
public static final int DIRECTION_RIGHT = -1; 
private int mDirection = 1;//swipe from right to left by default 
private int MAX_Y = 5; 
private int MAX_X = 3; 
private float mDownX; 
private float mDownY; 
private int mTouchState; 
private int mTouchPosition; 
private SwipeMenuLayout mTouchView; 
private OnSwipeListener mOnSwipeListener; 
private SwipeMenuCreator mMenuCreator; 
private OnMenuItemClickListener mOnMenuItemClickListener; 
private OnMenuStateChangeListener mOnMenuStateChangeListener; 
private Interpolator mCloseInterpolator; 
private Interpolator mOpenInterpolator; 
public SwipeMenuListView(Context context) { 
super(context); 
init(); 
} 
public SwipeMenuListView(Context context, AttributeSet attrs, int defStyle) { 
super(context, attrs, defStyle); 
init(); 
} 
public SwipeMenuListView(Context context, AttributeSet attrs) { 
super(context, attrs); 
init(); 
} 
private void init() { 
MAX_X = dp2px(MAX_X); 
MAX_Y = dp2px(MAX_Y); 
mTouchState = TOUCH_STATE_NONE; 
} 
@Override 
public void setAdapter(ListAdapter adapter) { 
super.setAdapter(new SwipeMenuAdapter(getContext(), adapter) { 
@Override 
public void createMenu(SwipeMenu menu) { 
if (mMenuCreator != null) { 
mMenuCreator.create(menu); 
} 
} 
@Override 
public void onItemClick(SwipeMenuView view, SwipeMenu menu, 
int index) { 
boolean flag = false; 
if (mOnMenuItemClickListener != null) { 
flag = mOnMenuItemClickListener.onMenuItemClick( 
view.getPosition(), menu, index); 
} 
if (mTouchView != null && !flag) { 
mTouchView.smoothCloseMenu(); 
} 
} 
}); 
} 
public void setCloseInterpolator(Interpolator interpolator) { 
mCloseInterpolator = interpolator; 
} 
public void setOpenInterpolator(Interpolator interpolator) { 
mOpenInterpolator = interpolator; 
} 
public Interpolator getOpenInterpolator() { 
return mOpenInterpolator; 
} 
public Interpolator getCloseInterpolator() { 
return mCloseInterpolator; 
} 
@Override 
public boolean onInterceptTouchEvent(MotionEvent ev) { 
//      ,               swip,                
int action = ev.getAction(); 
switch (action) { 
case MotionEvent.ACTION_DOWN: 
mDownX = ev.getX(); 
mDownY = ev.getY(); 
boolean handled = super.onInterceptTouchEvent(ev); 
mTouchState = TOUCH_STATE_NONE; 
mTouchPosition = pointToPosition((int) ev.getX(), (int) ev.getY()); 
View view = getChildAt(mTouchPosition - getFirstVisiblePosition()); 
//                  ,    open   
if (view instanceof SwipeMenuLayout) { 
//          . 
if (mTouchView != null && mTouchView.isOpen() && !inRangeOfView(mTouchView.getMenuView(), ev)) { 
return true; 
} 
mTouchView = (SwipeMenuLayout) view; 
mTouchView.setSwipeDirection(mDirection); 
} 
//       view 
if (mTouchView != null && mTouchView.isOpen() && view != mTouchView) { 
handled = true; 
} 
if (mTouchView != null) { 
mTouchView.onSwipe(ev); 
} 
return handled; 
case MotionEvent.ACTION_MOVE: 
float dy = Math.abs((ev.getY() - mDownY)); 
float dx = Math.abs((ev.getX() - mDownX)); 
if (Math.abs(dy) > MAX_Y || Math.abs(dx) > MAX_X) { 
//     down          TOUCH_STATE_NONE     true   onTouchEvent           
if (mTouchState == TOUCH_STATE_NONE) { 
if (Math.abs(dy) > MAX_Y) { 
mTouchState = TOUCH_STATE_Y; 
} else if (dx > MAX_X) { 
mTouchState = TOUCH_STATE_X; 
if (mOnSwipeListener != null) { 
mOnSwipeListener.onSwipeStart(mTouchPosition); 
} 
} 
} 
return true; 
} 
} 
return super.onInterceptTouchEvent(ev); 
} 
@Override 
public boolean onTouchEvent(MotionEvent ev) { 
if (ev.getAction() != MotionEvent.ACTION_DOWN && mTouchView == null) 
return super.onTouchEvent(ev); 
int action = ev.getAction(); 
switch (action) { 
case MotionEvent.ACTION_DOWN: 
int oldPos = mTouchPosition; 
mDownX = ev.getX(); 
mDownY = ev.getY(); 
mTouchState = TOUCH_STATE_NONE; 
mTouchPosition = pointToPosition((int) ev.getX(), (int) ev.getY()); 
/*******       *********/ 
View view = getChildAt(mTouchPosition - getFirstVisiblePosition()); 
if (mTouchPosition == oldPos && mTouchView != null 
&& mTouchView.isOpen()) { 
/********      ,    item         item,        item,   false.    down     , qq  ********/ 
if (view instanceof SwipeMenuLayout) { 
SwipeMenuLayout touchView = (SwipeMenuLayout) view; 
if (!touchView.isOpen()) { 
mTouchView.smoothCloseMenu(); 
return false; 
} 
} 
/***************************/ 
mTouchState = TOUCH_STATE_X; 
mTouchView.onSwipe(ev); 
return true; 
} 
if (mTouchView != null && mTouchView.isOpen()) { 
mTouchView.smoothCloseMenu(); 
mTouchView = null; 
// return super.onTouchEvent(ev); 
// try to cancel the touch event 
MotionEvent cancelEvent = MotionEvent.obtain(ev); 
cancelEvent.setAction(MotionEvent.ACTION_CANCEL); 
onTouchEvent(cancelEvent); 
if (mOnMenuStateChangeListener != null) { 
mOnMenuStateChangeListener.onMenuClose(oldPos); 
} 
return true; 
} 
if (view instanceof SwipeMenuLayout) { 
mTouchView = (SwipeMenuLayout) view; 
mTouchView.setSwipeDirection(mDirection); 
} 
if (mTouchView != null) { 
mTouchView.onSwipe(ev); 
} 
break; 
case MotionEvent.ACTION_MOVE: 
//     header,   header    
mTouchPosition = pointToPosition((int) ev.getX(), (int) ev.getY()) - getHeaderViewsCount(); 
//            ,    ,   mTouchView    ,          swip view 
//   mTouchView swip 。                 view 
if (!mTouchView.getSwipEnable() || mTouchPosition != mTouchView.getPosition()) { 
break; 
} 
float dy = Math.abs((ev.getY() - mDownY)); 
float dx = Math.abs((ev.getX() - mDownX)); 
if (mTouchState == TOUCH_STATE_X) { 
if (mTouchView != null) { 
mTouchView.onSwipe(ev); 
} 
getSelector().setState(new int[]{0}); 
ev.setAction(MotionEvent.ACTION_CANCEL); 
super.onTouchEvent(ev); 
return true; 
} else if (mTouchState == TOUCH_STATE_NONE) { 
if (Math.abs(dy) > MAX_Y) { 
mTouchState = TOUCH_STATE_Y; 
} else if (dx > MAX_X) { 
mTouchState = TOUCH_STATE_X; 
if (mOnSwipeListener != null) { 
mOnSwipeListener.onSwipeStart(mTouchPosition); 
} 
} 
} 
break; 
case MotionEvent.ACTION_UP: 
if (mTouchState == TOUCH_STATE_X) { 
if (mTouchView != null) { 
boolean isBeforeOpen = mTouchView.isOpen(); 
mTouchView.onSwipe(ev); 
boolean isAfterOpen = mTouchView.isOpen(); 
if (isBeforeOpen != isAfterOpen && mOnMenuStateChangeListener != null) { 
if (isAfterOpen) { 
mOnMenuStateChangeListener.onMenuOpen(mTouchPosition); 
} else { 
mOnMenuStateChangeListener.onMenuClose(mTouchPosition); 
} 
} 
if (!isAfterOpen) { 
mTouchPosition = -1; 
mTouchView = null; 
} 
} 
if (mOnSwipeListener != null) { 
mOnSwipeListener.onSwipeEnd(mTouchPosition); 
} 
ev.setAction(MotionEvent.ACTION_CANCEL); 
super.onTouchEvent(ev); 
return true; 
} 
break; 
} 
return super.onTouchEvent(ev); 
} 
public void smoothOpenMenu(int position) { 
if (position >= getFirstVisiblePosition() 
&& position <= getLastVisiblePosition()) { 
View view = getChildAt(position - getFirstVisiblePosition()); 
if (view instanceof SwipeMenuLayout) { 
mTouchPosition = position; 
if (mTouchView != null && mTouchView.isOpen()) { 
mTouchView.smoothCloseMenu(); 
} 
mTouchView = (SwipeMenuLayout) view; 
mTouchView.setSwipeDirection(mDirection); 
mTouchView.smoothOpenMenu(); 
} 
} 
} 
public void smoothCloseMenu() { 
if (mTouchView != null && mTouchView.isOpen()) { 
mTouchView.smoothCloseMenu(); 
} 
} 
private int dp2px(int dp) { 
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, 
getContext().getResources().getDisplayMetrics()); 
} 
public void setMenuCreator(SwipeMenuCreator menuCreator) { 
this.mMenuCreator = menuCreator; 
} 
public void setOnMenuItemClickListener( 
OnMenuItemClickListener onMenuItemClickListener) { 
this.mOnMenuItemClickListener = onMenuItemClickListener; 
} 
public void setOnSwipeListener(OnSwipeListener onSwipeListener) { 
this.mOnSwipeListener = onSwipeListener; 
} 
public void setOnMenuStateChangeListener(OnMenuStateChangeListener onMenuStateChangeListener) { 
mOnMenuStateChangeListener = onMenuStateChangeListener; 
} 
public static interface OnMenuItemClickListener { 
boolean onMenuItemClick(int position, SwipeMenu menu, int index); 
} 
public static interface OnSwipeListener { 
void onSwipeStart(int position); 
void onSwipeEnd(int position); 
} 
public static interface OnMenuStateChangeListener { 
void onMenuOpen(int position); 
void onMenuClose(int position); 
} 
public void setSwipeDirection(int direction) { 
mDirection = direction; 
} 
/** 
*            view  
* 
* @param view 
* @param ev 
* @return 
*/ 
public static boolean inRangeOfView(View view, MotionEvent ev) { 
int[] location = new int[2]; 
view.getLocationOnScreen(location); 
int x = location[0]; 
int y = location[1]; 
if (ev.getRawX() < x || ev.getRawX() > (x + view.getWidth()) || ev.getRawY() < y || ev.getRawY() > (y + view.getHeight())) { 
return false; 
} 
return true; 
} 
}
이렇게 보면 qq 를 실현 하 는 그 효 과 는 매우 간단 하지 않 습 니까?그러나 원작 자가 잘 쓴 덕분에 제 가 수정 하기 가 더욱 쉽 습 니 다.
그 나 저 나 누 른 아 이 템 이 이미 끌 려 나 온 아 이 템 이 아니 었 을 때 그 효 과 는 이미 이 루어 졌 습 니 다.누 른 아 이 템 이 바로 끌 려 나 온 아 이 템 이 라면 효 과 는 어떻게 이 루어 질 까요?
사실은 이 효과 에 대해 원작 자 는 이미 실현 되 었 지만 실현 이 좋 지 않 고 문제 가 존재 한다.나 는 여기 서 이 문 제 를 복원 했다.
문 제 는 현재 당 겨 진 아 이 템 에서 좌우 로 미 끄 러 질 때 손가락 을 들 어 올 리 는 순간 미 끄 러 지 는 방향 이 당 겨 진 방향 으로 미 끄 러 질 확률 이 높 습 니 다.이 당 겨 진 아 이 템 은 닫 힙 니 다.밤 을 들 어 보 세 요.예 를 들 어 왼쪽 으로 미 끄 러 지 는 것 은 당 겨 진 방향 입 니 다.이미 당 겨 진 아 이 템 에서 좌우 로 미 끄 러 질 때.그리고 손가락 을 들 어 올 리 기 전 당신 은 왼쪽으로 미 끄 러 졌 습 니 다.이치 에 따 르 면 이 아 이 템 은 열 린 상태 에 있 을 것 입 니 다.그러나 실제로 손가락 을 들 었 을 때 이 아 이 템 은 닫 혔 습 니 다.그래서 이것 은 사용자 체험 에 상당 한 영향 을 주 는 문제 입 니 다.하나의 아 이 템 은 바로 Swipe Menu Layout 입 니 다.Swipe Menu Layout 류 에서 onSwipe 방법 이 있 습 니 다.이 방법의 매개 변수 인 MotionEvent 는 SwipeMenuListView 의 onTouchEvent 방법 에서 전 달 된 것 입 니 다.전에 말씀 드 렸 듯 이 손가락 을 들 어야 문제 가 생 길 수 있 습 니 다.그러면 저 희 는 ACTION 을 직접 보 겠 습 니 다.UP 의 실현 코드,
다음 코드 에 문제 가 있 습 니 다.

if ((isFling || Math.abs(mDownX - event.getX()) > (mMenuView.getWidth() / 2)) && 
Math.signum(mDownX - event.getX()) == mSwipeDirection) { 
// open 
smoothOpenMenu(); 
} else { 
// close 
smoothCloseMenu(); 
return false; 
} 
분석 해 보 겠 습 니 다.isFliing 은 보지 않 아 도 됩 니 다.문 제 는 여기 서 나 온 것 이 아 닙 니 다.우 리 는 뒤의 판단 논 리 를 봅 니 다.X 축 방향 에서 미 끄 러 지 는 거리의 절대 치 는 메뉴 너비 의 절반 보다 크 고 미 끄 러 지 는 방향 이 열 린 방향 으로 미 끄 러 질 때 하나의 item 이 열 립 니 다.그렇지 않 으 면 다른 모든 상황 이 else 문 구 를 직접 들 어가 item 을 닫 습 니 다.문 제 는 바로 여기에 나타 납 니 다.위 에서 말 한 문제 도 이 로 인해 발생 한 것 입 니 다.한 가지 상황 을 생각해 보 세 요.현재 의 item 은 이미 끌 려 나 온 것 이기 때문에 만약 에 우리 의 미끄럼 거리 가 메뉴 너비 의 절반 을 초과 하지 않 았 지만 우리 가 미 끄 러 지 는 방향 은 item 이 끌 려 나 온 방향 입 니 다.이 치 를 따 지면 우리 의 item 은 계속 끌 려 나 온 상태 로 표시 해 야 합 니 다.그러나 코드 에서 의 판단 논리 에 따 르 면 이런 상황 은 else 문구 에 직접 들 어 가 는 것 이다.즉,이 item 을 직접 닫 는 것 이다.그래서 여기 서 의 논리 적 판단 에 큰 문제 가 있다.우리 가 원 하 는 효 과 는:당신 이 끌 려 나 온 이상 당신 이 계속 끌 어 내 는 방향 으로 미 끄 러 지 는 것 이 라면 나 는 else 에 들 어가 지 않 고 if 에서 미 끄 러 지 는 거 리 를 직접 판단 하 는 것 이다.이런 논리 가 정확 하 다.그리고 첫 번 째 미끄럼 을 할 때 문제 가 발생 하지 않도록 우 리 는 첫 번 째 미끄럼 을 위해 해당 하 는 조작 을 해 야 한다.
그래서 수 정 된 코드 논 리 는 다음 과 같다.

if ((isFling || Math.signum(mDownX - event.getX()) == mSwipeDirection)) { 
// open 
/**************     ****        item                ,item          ******************/ 
if (Math.abs(mDownX - event.getX()) > (mMenuView.getWidth() / 2)) { 
smoothOpenMenu(); 
} else { 
//  item   ,                   
if (!isOpen()) { 
smoothCloseMenu(); 
} 
} 
/*******************************************/ 
} else { 
// close 
smoothCloseMenu(); 
return false; 
} 
이렇게 하면 우 리 는 qq 의 두 번 째 효 과 를 완벽 하 게 실현 할 수 있 습 니 다.즉,당신 의 손가락 이 현재 당 겨 진 item 에 놓 이면 그 는 이 item 을 숨 기지 않 고 좌우 미끄럼 사건 에 정상적으로 응답 할 수 있 습 니 다.
수 정 된 SwipeMenu Layout 류 의 코드 는 다음 과 같 습 니 다.

package com.lanma.swipemenulistviewdemo.swipemenulistview; 
import android.content.Context; 
import android.support.v4.view.GestureDetectorCompat; 
import android.support.v4.widget.ScrollerCompat; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.util.TypedValue; 
import android.view.GestureDetector.OnGestureListener; 
import android.view.GestureDetector.SimpleOnGestureListener; 
import android.view.MotionEvent; 
import android.view.View; 
import android.view.animation.Interpolator; 
import android.widget.AbsListView; 
import android.widget.FrameLayout; 
/** 
* @author baoyz 
* @date 2014-8-23 
* qiang_xi   2016-09-07 
*/ 
public class SwipeMenuLayout extends FrameLayout { 
private static final int CONTENT_VIEW_ID = 1; 
private static final int MENU_VIEW_ID = 2; 
private static final int STATE_CLOSE = 0; 
private static final int STATE_OPEN = 1; 
private int mSwipeDirection; 
private View mContentView; 
private SwipeMenuView mMenuView; 
private int mDownX; 
private int state = STATE_CLOSE; 
private GestureDetectorCompat mGestureDetector; 
private OnGestureListener mGestureListener; 
private boolean isFling; 
private int MIN_FLING = dp2px(15); 
private int MAX_VELOCITYX = -dp2px(500); 
private ScrollerCompat mOpenScroller; 
private ScrollerCompat mCloseScroller; 
private int mBaseX; 
private int position; 
private Interpolator mCloseInterpolator; 
private Interpolator mOpenInterpolator; 
private boolean mSwipEnable = true; 
public SwipeMenuLayout(View contentView, SwipeMenuView menuView) { 
this(contentView, menuView, null, null); 
} 
public SwipeMenuLayout(View contentView, SwipeMenuView menuView, 
Interpolator closeInterpolator, Interpolator openInterpolator) { 
super(contentView.getContext()); 
mCloseInterpolator = closeInterpolator; 
mOpenInterpolator = openInterpolator; 
mContentView = contentView; 
mMenuView = menuView; 
mMenuView.setLayout(this); 
init(); 
} 
// private SwipeMenuLayout(Context context, AttributeSet attrs, int 
// defStyle) { 
// super(context, attrs, defStyle); 
// } 
private SwipeMenuLayout(Context context, AttributeSet attrs) { 
super(context, attrs); 
} 
private SwipeMenuLayout(Context context) { 
super(context); 
} 
public int getPosition() { 
return position; 
} 
public void setPosition(int position) { 
this.position = position; 
mMenuView.setPosition(position); 
} 
public void setSwipeDirection(int swipeDirection) { 
mSwipeDirection = swipeDirection; 
} 
private void init() { 
setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT, 
LayoutParams.WRAP_CONTENT)); 
mGestureListener = new SimpleOnGestureListener() { 
@Override 
public boolean onDown(MotionEvent e) { 
isFling = false; 
return true; 
} 
@Override 
public boolean onFling(MotionEvent e1, MotionEvent e2, 
float velocityX, float velocityY) { 
// TODO 
if (Math.abs(e1.getX() - e2.getX()) > MIN_FLING 
&& velocityX < MAX_VELOCITYX) { 
isFling = true; 
} 
// Log.i("byz", MAX_VELOCITYX + ", velocityX = " + velocityX); 
return super.onFling(e1, e2, velocityX, velocityY); 
} 
}; 
mGestureDetector = new GestureDetectorCompat(getContext(), 
mGestureListener); 
// mScroller = ScrollerCompat.create(getContext(), new 
// BounceInterpolator()); 
if (mCloseInterpolator != null) { 
mCloseScroller = ScrollerCompat.create(getContext(), 
mCloseInterpolator); 
} else { 
mCloseScroller = ScrollerCompat.create(getContext()); 
} 
if (mOpenInterpolator != null) { 
mOpenScroller = ScrollerCompat.create(getContext(), 
mOpenInterpolator); 
} else { 
mOpenScroller = ScrollerCompat.create(getContext()); 
} 
LayoutParams contentParams = new LayoutParams( 
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 
mContentView.setLayoutParams(contentParams); 
if (mContentView.getId() < 1) { 
mContentView.setId(CONTENT_VIEW_ID); 
} 
mMenuView.setId(MENU_VIEW_ID); 
mMenuView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, 
LayoutParams.WRAP_CONTENT)); 
addView(mContentView); 
addView(mMenuView); 
// if (mContentView.getBackground() == null) { 
// mContentView.setBackgroundColor(Color.WHITE); 
// } 
// in android 2.x, MenuView height is MATCH_PARENT is not work. 
// getViewTreeObserver().addOnGlobalLayoutListener( 
// new OnGlobalLayoutListener() { 
// @Override 
// public void onGlobalLayout() { 
// setMenuHeight(mContentView.getHeight()); 
// // getViewTreeObserver() 
// // .removeGlobalOnLayoutListener(this); 
// } 
// }); 
} 
@Override 
protected void onAttachedToWindow() { 
super.onAttachedToWindow(); 
} 
@Override 
protected void onSizeChanged(int w, int h, int oldw, int oldh) { 
super.onSizeChanged(w, h, oldw, oldh); 
} 
public boolean onSwipe(MotionEvent event) { 
mGestureDetector.onTouchEvent(event); 
switch (event.getAction()) { 
case MotionEvent.ACTION_DOWN: 
mDownX = (int) event.getX(); 
isFling = false; 
break; 
case MotionEvent.ACTION_MOVE: 
// Log.i("byz", "downX = " + mDownX + ", moveX = " + event.getX()); 
int dis = (int) (mDownX - event.getX()); 
if (state == STATE_OPEN) { 
dis += mMenuView.getWidth() * mSwipeDirection; 
} 
swipe(dis); 
break; 
case MotionEvent.ACTION_UP: 
if ((isFling || Math.signum(mDownX - event.getX()) == mSwipeDirection)) { 
// open 
/**************     ****        item                ,item          ******************/ 
if (Math.abs(mDownX - event.getX()) > (mMenuView.getWidth() / 2)) { 
smoothOpenMenu(); 
} else { 
//  item   ,                   
if (!isOpen()) { 
smoothCloseMenu(); 
} 
} 
/*******************************************/ 
} else { 
// close 
smoothCloseMenu(); 
return false; 
} 
break; 
} 
return true; 
} 
public boolean isOpen() { 
return state == STATE_OPEN; 
} 
@Override 
public boolean onTouchEvent(MotionEvent event) { 
return super.onTouchEvent(event); 
} 
private void swipe(int dis) { 
if (!mSwipEnable) { 
return; 
} 
if (Math.signum(dis) != mSwipeDirection) { 
dis = 0; 
} else if (Math.abs(dis) > mMenuView.getWidth()) { 
dis = mMenuView.getWidth() * mSwipeDirection; 
} 
mContentView.layout(-dis, mContentView.getTop(), 
mContentView.getWidth() - dis, getMeasuredHeight()); 
if (mSwipeDirection == SwipeMenuListView.DIRECTION_LEFT) { 
mMenuView.layout(mContentView.getWidth() - dis, mMenuView.getTop(), 
mContentView.getWidth() + mMenuView.getWidth() - dis, 
mMenuView.getBottom()); 
} else { 
mMenuView.layout(-mMenuView.getWidth() - dis, mMenuView.getTop(), 
-dis, mMenuView.getBottom()); 
} 
} 
@Override 
public void computeScroll() { 
if (state == STATE_OPEN) { 
if (mOpenScroller.computeScrollOffset()) { 
swipe(mOpenScroller.getCurrX() * mSwipeDirection); 
postInvalidate(); 
} 
} else { 
if (mCloseScroller.computeScrollOffset()) { 
swipe((mBaseX - mCloseScroller.getCurrX()) * mSwipeDirection); 
postInvalidate(); 
} 
} 
} 
public void smoothCloseMenu() { 
state = STATE_CLOSE; 
if (mSwipeDirection == SwipeMenuListView.DIRECTION_LEFT) { 
mBaseX = -mContentView.getLeft(); 
mCloseScroller.startScroll(0, 0, mMenuView.getWidth(), 0, 350); 
} else { 
mBaseX = mMenuView.getRight(); 
mCloseScroller.startScroll(0, 0, mMenuView.getWidth(), 0, 350); 
} 
postInvalidate(); 
} 
public void smoothOpenMenu() { 
if (!mSwipEnable) { 
return; 
} 
state = STATE_OPEN; 
if (mSwipeDirection == SwipeMenuListView.DIRECTION_LEFT) { 
mOpenScroller.startScroll(-mContentView.getLeft(), 0, mMenuView.getWidth(), 0, 350); 
} else { 
mOpenScroller.startScroll(mContentView.getLeft(), 0, mMenuView.getWidth(), 0, 350); 
} 
postInvalidate(); 
} 
public void closeMenu() { 
if (mCloseScroller.computeScrollOffset()) { 
mCloseScroller.abortAnimation(); 
} 
if (state == STATE_OPEN) { 
state = STATE_CLOSE; 
swipe(0); 
} 
} 
public void openMenu() { 
if (!mSwipEnable) { 
return; 
} 
if (state == STATE_CLOSE) { 
state = STATE_OPEN; 
swipe(mMenuView.getWidth() * mSwipeDirection); 
} 
} 
public View getContentView() { 
return mContentView; 
} 
public SwipeMenuView getMenuView() { 
return mMenuView; 
} 
private int dp2px(int dp) { 
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, 
getContext().getResources().getDisplayMetrics()); 
} 
@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
mMenuView.measure(MeasureSpec.makeMeasureSpec(0, 
MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec( 
getMeasuredHeight(), MeasureSpec.EXACTLY)); 
} 
@Override 
protected void onLayout(boolean changed, int l, int t, int r, int b) { 
mContentView.layout(0, 0, getMeasuredWidth(), 
mContentView.getMeasuredHeight()); 
if (mSwipeDirection == SwipeMenuListView.DIRECTION_LEFT) { 
mMenuView.layout(getMeasuredWidth(), 0, 
getMeasuredWidth() + mMenuView.getMeasuredWidth(), 
mContentView.getMeasuredHeight()); 
} else { 
mMenuView.layout(-mMenuView.getMeasuredWidth(), 0, 
0, mContentView.getMeasuredHeight()); 
} 
} 
public void setMenuHeight(int measuredHeight) { 
Log.i("byz", "pos = " + position + ", height = " + measuredHeight); 
LayoutParams params = (LayoutParams) mMenuView.getLayoutParams(); 
if (params.height != measuredHeight) { 
params.height = measuredHeight; 
mMenuView.setLayoutParams(mMenuView.getLayoutParams()); 
} 
} 
public void setSwipEnable(boolean swipEnable) { 
mSwipEnable = swipEnable; 
} 
public boolean getSwipEnable() { 
return mSwipEnable; 
} 
} 
그러면 우리 의 제목 이름 은 99.99%로 사 이 드 스 케 이 트 삭제 효 과 를 실현 하기 때문에 효과 도 를 살 펴 보고 99.99%의 것 인지 아 닌 지 를 살 펴 보 자.
QQ 효과 도:
 
수 정 된 효과 그림:

위의 두 장의 효과 도 는 아무것도 보이 지 않 을 수 있 습 니 다.특히 우리 가 위 에서 말 한 그 몇 가지 효 과 는 주로 화면 을 gif 로 전환 한 후에 카드 재생 속도 가 빠 르 기 때문에 qq 의 효과 와 우리 자신의 효 과 를 비교 하려 면 demo 를 다운로드 하여 스스로 비교 하 세 요.저 는 고 친 후의 효과 와 QQ 의 효 과 는 대체적으로 같다 고 말 할 수 밖 에 없습니다.가가~~~

좋은 웹페이지 즐겨찾기