Android 는 ViewDragHelper 를 사용 하여 QQQ6.X 최신 버 전 사 이 드 슬라이드 인터페이스 효과 인 스 턴 스 코드 를 구현 합 니 다.

(1).머리말:
이틀 동안 QQ 는 중대 한 업데이트(6.X)를 진 행 했 습 니 다.특히 UI 스타일 에서 이전 파란색 에서 흰색 으로 바 뀌 었 고 사 이 드 스 케 이 트 효과 도 약간 바 뀌 었 습 니 다.그러면 QQ6.X 버 전의 사 이 드 스 케 이 트 인터페이스 효 과 를 모방 해 보 겠 습 니 다.오늘 도 우 리 는 신기 뷰 드 래 곤 헬 퍼 로 이 루어 진다.
이번 인 스 턴 스 의 구체 적 인 코드 는 아래 항목 에 올 라 왔 습 니 다.star 와 fork 에 오신 것 을 환영 합 니 다.
https://github.com/jiangqqlmj/DragHelper4QQ
FastDev4Android 프레임 워 크 항목 주소:https://github.com/jiangqqlmj/FastDev4Android
(2).ViewGragHelper 의 기본 사용
앞에서 우 리 는 ViewGragHelper 의 기본 적 인 사용 방법 을 배 웠 고 안에 있 는 몇 가지 방법의 용도 도 알 게 되 었 습 니 다.다음은 기본 적 인 사용 절 차 를 복습 하 겠 습 니 다.ViewGragHelper 를 사용 하여 하위 View 드래그 이동 을 실현 하 는 절 차 는 다음 과 같 습 니 다.
ViewGragHelper 인 스 턴 스 만 들 기(Callback 에 전송)
이벤트 차단 처리 방법 재 작성 onInterceptTouch 와 onTouch Event
Callback 을 실현 하고 그 중의 관련 방법 try Capture View 와 수평 또는 수직 방향 으로 이동 하 는 거리 방법 을 실현 합 니 다.
좀 더 구체 적 으로 분석 하면 앞의 블 로 그 를 보 거나 오늘 은 구체 적 인 사례 를 통 해 설명 하 겠 습 니 다.
(3).QQQ5.X 측면 미끄럼 효과 에 대한 분석:
본 격 버 전 QQ 의 사 이 드 스 케 이 트 효 과 는 다음 과 같 습 니 다.
 
위 를 살 펴 보면 우 리 는 두 개의 View 로 이해 할 수 있 습 니 다.하 나 는 바닥 이 왼쪽 기능 View 에 해당 하고 다른 하 나 는 상층 의 주요 기능 내용 View 입 니 다.우리 가 위 에서 상층 View 를 끌 거나 좌우 로 미 끄 러 질 때 상층 과 하층부의 View 는 상응 하 게 미 끄 러 지고 View 의 큰 변 화 를 하 는 동시에 관련 애니메이션 도 추가 합 니 다.물론 상부 의 View 를 클릭 하면 사 이 드 메뉴 를 열거 나 닫 을 수 있 습 니 다.
(4).측면 미끄럼 효과 사용자 정의 구성 요소 구현
1.우선 FrameLayout 에 통합 하여 사용자 정의 View DragLayout 를 만 듭 니 다.내부 에서 정 의 된 변 수 는 다음 과 같 습 니 다.(주로 설정 류,제스처,ViewDragHelper 인 스 턴 스,화면 너비,끌 어 당 기 는 하위 보기 View 등 을 포함 합 니 다)

//         
private boolean isShowShadow = true; 
//      
private GestureDetectorCompat gestureDetector; 
//          
private ViewDragHelper dragHelper; 
//      
private DragListener dragListener; 
//        
private int range; 
//   
private int width; 
//   
private int height; 
//main     ViewGroup        
private int mainLeft; 
private Context context; 
private ImageView iv_shadow; 
//     
private RelativeLayout vg_left; 
//  (     ) 
private CustomRelativeLayout vg_main; 
그 다음 에 내부 에서 리 셋 인터페이스 가 끌 어 당 기 는 과정 에서 일부 페이지 가 열 리 고 닫 히 며 미 끄 러 지 는 이벤트 리 셋 을 처리 하 는 것 을 정의 했다.

/** 
*          
*/ 
public interface DragListener { 
//     
public void onOpen(); 
//     
public void onClose(); 
//        
public void onDrag(float percent); 
}
2.ViewDragHelper 인 스 턴 스 를 만 들 기 시 작 했 습 니 다.사용자 정의 View DragLayout 를 초기 화 할 때 만 들 고 ViewGragHelper 의 정적 방법 을 사용 합 니 다.

public DragLayout(Context context,AttributeSet attrs, int defStyle) { 
super(context, attrs, defStyle); 
gestureDetector = new GestureDetectorCompat(context, new YScrollDetector()); 
dragHelper =ViewDragHelper.create(this, dragHelperCallback); 
} 
그 중에서 create()방법 을 만 들 때 dragHelperCallBack 리 셋 클래스 가 들 어 왔 습 니 다.네 번 째 시 에 말씀 드 리 겠 습 니 다.
3.이 어 ViewGroup 의 이벤트 방법 을 다시 써 서 터치 사건 을 차단 하여 ViewGragHelper 내부 에 처리 하여 이동 서브 View 보 기 를 끌 어 당 기 는 목적 을 달성 해 야 합 니 다.

/** 
*        
* @param ev 
* @return 
*/ 
@Override 
public boolean onInterceptTouchEvent(MotionEvent ev) { 
return dragHelper.shouldInterceptTouchEvent(ev) &&gestureDetector.onTouchEvent(ev); 
} 
/** 
*         ViewDragHelper     
* @param e 
* @return 
*/ 
@Override 
public boolean onTouchEvent(MotionEvent e){ 
try { 
dragHelper.processTouchEvent(e); 
} catch (Exception ex) { 
ex.printStackTrace(); 
} 
return false; 
} 
여기 서 우 리 는 onInterceptTouchEvent 에서 이 벤트 를 부모 컨트롤 에서 하위 View 로 이동 시 킨 다음 에 onTouchEvent 방법 에서 ViewDragHelper 가 소비 처 리 를 하도록 차단 합 니 다.
4.View DragHelper.Callback 을 만 드 는 인 스 턴 스 를 사용자 정의 하기 시작 합 니 다.dragHelper Callback 은 추상 적 인 방법 try Capture View 를 실현 하고 다음 과 같은 몇 가지 방법 을 다시 써 서 사 이 드 스 케 이 트 기능 을 실현 합 니 다.다음은 하나씩 살 펴 보 겠 습 니 다.

/** 
*       View 
* @param child Child the user is attempting to capture 
* @param pointerId ID of the pointer attempting the capture 
* @return 
*/ 
@Override 
public boolean tryCaptureView(Viewchild, int pointerId) { 
return true; 
} 
뷰 그룹(이 예:DragLayout)의 모든 하위 뷰 를 차단 하고 트 루 로 돌아 가 모든 하위 뷰 를 드래그 하여 이동 할 수 있 음 을 표시 합 니 다.

/** 
*        
* @param child Child view beingdragged 
* @param left Attempted motion alongthe X axis 
* @param dx Proposed change inposition for left 
* @return 
*/ 
@Override 
public int clampViewPositionHorizontal(View child, int left, int dx) { 
if (mainLeft + dx < 0) { 
return 0; 
} else if (mainLeft + dx >range) { 
return range; 
} else { 
return left; 
} 
}
이 방법 을 실현 하 는 것 은 수평 방향 이 미 끄 러 지 는 것 을 나타 내 는 동시에 방법 에서 경계 값 을 판단 할 것 이다.예 를 들 어 위의 main view 가 왼쪽 으로 경 계 를 이동 한 것 을 제외 하고 바로 0 으로 돌아 가 왼쪽 가장 왼쪽 에 x=0 만 있 음 을 나타 낸다.그리고 오른쪽으로 이동 하면 오른쪽으로 가장 거리 가 range 로 변 하 는 것 으로 판단 되 며,range 의 초기 화 이후 에 말씀 드 리 겠 습 니 다.이 두 가지 상황 을 제외 하고 바로 left 로 돌아 가면 된다.

/** 
*               
*@param child Child view to check      
* @return 
*/ 
@Override 
public int getViewHorizontalDragRange(View child) { 
return width; 
} 
이 방법 은 Callback 내부 에서 기본적으로 0 으로 되 돌아 가기 때문에 이 방법 이 필요 합 니 다.즉,view 의 click 이벤트 가 true 라면 전체 하위 View 가 끌 어 당 겨 이동 할 수 없 는 상황 이 발생 할 수 있 습 니 다.그러면 여 기 는 left view 너비 로 바로 돌아 가 수평 방향 으로 미 끄 러 지 는 가장 먼 거 리 를 나타 낸다.

/** 
*      View,            ,                         
* @param releasedChild 
* @param xvel 
* @param yvel 
*/ 
@Override 
public void onViewReleased(View releasedChild, float xvel, float yvel) { 
super.onViewReleased(releasedChild,xvel, yvel); 
if (xvel > 0) { 
open(); 
} else if (xvel < 0) { 
close(); 
} else if (releasedChild == vg_main&& mainLeft > range * 0.3) { 
open(); 
} else if (releasedChild == vg_left&& mainLeft > range * 0.7) { 
open(); 
} else { 
close(); 
} 
} 
이 방법 은 드래그 자 View 가 손가락 을 움 직 여 놓 을 때 호출 됩 니 다.이것 은 왼쪽으로 이동 하고 오른쪽으로 이동 하 는 의 도 를 판단 하여 man view(상부 보기)를 열거 나 닫 습 니 다.다음은 실현 의 마지막 방법:onView Position Changed

/** 
*  View               
* @param changedView View whoseposition changed 
* @param left New X coordinate of theleft edge of the view 
* @param top New Y coordinate of thetop edge of the view 
* @param dx Change in X position fromthe last call 
* @param dy Change in Y position fromthe last call 
*/ 
@Override 
public void onViewPositionChanged(View changedView, int left, int top, 
int dx, int dy) { 
if (changedView == vg_main) { 
mainLeft = left; 
} else { 
mainLeft = mainLeft + left; 
} 
if (mainLeft < 0) { 
mainLeft = 0; 
} else if (mainLeft > range) { 
mainLeft = range; 
} 
if (isShowShadow) { 
iv_shadow.layout(mainLeft, 0,mainLeft + width, height); 
} 
if (changedView == vg_left) { 
vg_left.layout(0, 0, width,height); 
vg_main.layout(mainLeft, 0,mainLeft + width, height); 
} 
dispatchDragEvent(mainLeft); 
} 
}; 
이 방법 은 이동 서브 뷰 를 끌 어 당 기 는 과정 에서 리 셋 을 하고 이동 좌표 위치 에 따라 left view 와 main view 를 다시 정의 하 는 것 입 니 다.dispathDragEvent()방법 을 동시에 호출 하여 드래그 이벤트 관련 처 리 를 하고 배포 하 는 동시에 상태 에 따라 인 터 페 이 스 를 되 돌려 줍 니 다.

/** 
*          
* @param mainLeft 
*/ 
private void dispatchDragEvent(int mainLeft) { 
if (dragListener == null) { 
return; 
} 
float percent = mainLeft / (float)range; 
//           
animateView(percent); 
//           
dragListener.onDrag(percent); 
Status lastStatus = status; 
if (lastStatus != getStatus()&& status == Status.Close) { 
dragListener.onClose(); 
} else if (lastStatus != getStatus()&& status == Status.Open) { 
dragListener.onOpen(); 
} 
} 
이 방법 에는 float percent=mainLeft/(float)range 코드 가 있 습 니 다.1 퍼센트 까지 계산 해서 나중에 쓸 게 요.
5.하위 View 레이아웃 의 초기 화 및 너비 와 수평 미끄럼 거리의 크기 설정 방법:

/** 
*          
*           
*/ 
@Override 
protected void onFinishInflate() { 
super.onFinishInflate(); 
if (isShowShadow) { 
iv_shadow = new ImageView(context); 
iv_shadow.setImageResource(R.mipmap.shadow); 
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 
addView(iv_shadow, 1, lp); 
} 
//     
vg_left = (RelativeLayout)getChildAt(0); 
//  ( )   
vg_main = (CustomRelativeLayout)getChildAt(isShowShadow ? 2 : 1); 
vg_main.setDragLayout(this); 
vg_left.setClickable(true); 
vg_main.setClickable(true); 
} 
그리고 컨트롤 크기 가 바 뀌 는 방법:

@Override 
protected void onSizeChanged(int w, int h,int oldw, int oldh) { 
super.onSizeChanged(w, h, oldw, oldh); 
width = vg_left.getMeasuredWidth(); 
height = vg_left.getMeasuredHeight(); 
//                    80% 
range = (int) (width *0.8f); 
} 
이 방법 에서 우 리 는 실시 간 으로 너비 와 높이,그리고 수평 거 리 를 끌 어 당 길 수 있다.
6.위의 모든 핵심 코드 는 View DragHelper 를 사용 하여 하위 컨트롤 View 드래그 이동 을 실현 하 는 방법 입 니 다.그러나 우리 의 측면 미끄럼 효과 에 따라 애니메이션 과 미끄럼 과정 에서 View 의 크기 조정 효 과 를 실현 해 야 하기 때문에 우 리 는 애니메이션 오픈 소스 라 이브 러 리 를 도입 하 였 습 니 다.

그리고 앞 에 계 산 된 백분율 에 따라 View 보기 크기 를 조정 합 니 다.

/** 
*           ,       
* @param percent 
*/ 
private void animateView(float percent) { 
float f1 = 1 - percent * 0.5f; 
ViewHelper.setTranslationX(vg_left,-vg_left.getWidth() / 2.5f + vg_left.getWidth() / 2.5f * percent); 
if (isShowShadow) { 
//             
ViewHelper.setScaleX(iv_shadow, f1* 1.2f * (1 - percent * 0.10f)); 
ViewHelper.setScaleY(iv_shadow, f1* 1.85f * (1 - percent * 0.10f)); 
} 
} 
7.물론 위 에 있 는 것 외 에 한 가지 효과 가 부족 하 다.우리 가 미 끄 러 지 는 과정 에서 손가락 이 풀 리 면 상식 적 으로 view 는 이동 하지 않 을 것 이다.그러면 여기 서 우 리 는 가속도 가 필요 하 다.우리 가 풀 린 후에 도 일정한 속 도 를 유지 할 수 있다.어떻게 실현 해 야 할 까?정 답 은 컴퓨터 스크롤()을 실현 하 는 방법 이다.

/** 
*     ,          ,            
*/ 
@Override 
public void computeScroll() { 
if (dragHelper.continueSettling(true)){ 
ViewCompat.postInvalidateOnAnimation(this); 
} 
} 
OK 위 에 있 는 DragLayout 에 대한 핵심 코드 는 많 지 않 습 니 다.다음은 DragLayout 류 를 사용 하여 사 이 드 스 케 이 트 효 과 를 실현 합 니 다!
(5).측면 미끄럼 효과 구성 요소 사용
1.먼저 사용 한 레이아웃 파일 은 다음 과 같 습 니 다.

<com.chinaztt.widget.DragLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
android:id="@+id/dl" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:background="@android:color/transparent" 
> 
<!--        --> 
<includelayoutincludelayout="@layout/left_view_layout"/> 
<!--         --> 
<com.chinaztt.widget.CustomRelativeLayout 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:background="#FFFFFF" 
> 
<LinearLayout 
android:layout_width="fill_parent" 
android:layout_height="fill_parent" 
android:orientation="vertical" 
> 
<RelativeLayout 
android:id="@+id/rl_title" 
android:layout_width="match_parent" 
android:layout_height="49dp" 
android:gravity="bottom" 
android:background="@android:color/holo_orange_light" 
> 
<includelayoutincludelayout="@layout/common_top_bar_layout"/> 
</RelativeLayout> 
<!--        Fragment--> 
<FrameLayout 
android:layout_width="fill_parent" 
android:layout_height="fill_parent" 
> 
<fragment 
android:id="@+id/main_info_fragment" 
class="com.chinaztt.fragment.OneFragment" 
android:layout_width="fill_parent" 
android:layout_height="fill_parent"/> 
</FrameLayout> 
</LinearLayout> 
</com.chinaztt.widget.CustomRelativeLayout> 
</com.chinaztt.widget.DragLayout> 
이 레이아웃 파일 의 부모 층 View 는 DragLayout 입 니 다.그리고 내부 에 두 개의 RelativeLayout 레이아웃 이 있 습 니 다.각각 다음 레이아웃 과 윗 층 의 메 인 레이아웃 을 충당 합 니 다.
2.아래 메뉴 레이아웃 을 살 펴 보 겠 습 니 다.여 기 는 제 가 left 라 고 적 었 습 니 다.view_layot.xml 파일 은 주로 세 조각 으로 나 뉘 는데 첫 번 째 상단 은 이미지 개인 기본 정보 레이아웃 이 고 중간 은 기능 입구 목록 이 며 아래쪽 은 설정 등 기능 입 니 다.구체 적 인 레이아웃 코드 는 다음 과 같 습 니 다.

<RelativeLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:app="http://schemas.android.com/apk/res-auto" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:paddingTop="70dp" 
android:background="@drawable/sidebar_bg" 
> 
<LinearLayout 
android:id="@+id/ll1" 
android:paddingLeft="30dp" 
android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
android:orientation="vertical"> 
<!--  ,    --> 
<LinearLayout 
android:layout_width="match_parent" 
android:layout_height="70dp" 
android:orientation="horizontal" 
android:gravity="center_vertical" 
> 
<com.chinaztt.widget.RoundAngleImageView 
android:id="@+id/iv_bottom" 
android:layout_width="50dp" 
android:layout_height="50dp" 
android:scaleType="fitXY" 
android:src="@drawable/icon_logo" 
app:roundWidth="25dp" 
app:roundHeight="25dp"/> 
<LinearLayout 
android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
android:gravity="center_vertical" 
android:layout_gravity="center_vertical" 
android:orientation="vertical"> 
<RelativeLayout 
android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
> 
<TextView 
android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:layout_centerVertical="true" 
android:layout_marginLeft="15dp" 
android:text="  :jiangqqlmj" 
android:textColor="@android:color/black" 
android:textSize="15sp" /> 
<ImageButton 
android:layout_alignParentRight="true" 
android:layout_centerVertical="true" 
android:layout_marginRight="100dp" 
android:layout_width="22dp" 
android:layout_height="22dp" 
android:background="@drawable/qrcode_selector"/> 
</RelativeLayout> 
<TextView 
android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:layout_gravity="center_vertical" 
android:layout_marginLeft="15dp" 
android:text="QQ:781931404" 
android:textColor="@android:color/black" 
android:textSize="13sp" /> 
</LinearLayout> 
</LinearLayout> 
<LinearLayout 
android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
android:gravity="center_vertical" 
android:orientation="horizontal"> 
<ImageView 
android:layout_width="17dp" 
android:layout_height="17dp" 
android:scaleType="fitXY" 
android:src="@drawable/sidebar_signature_nor"/> 
<TextView 
android:layout_marginLeft="5dp" 
android:textSize="13sp" 
android:textColor="#676767" 
android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
android:text="     !"/> 
</LinearLayout> 
</LinearLayout> 
<!--     --> 
<includelayoutincludelayout="@layout/left_view_bottom_layout" 
android:id="@+id/bottom_view" 
/> 
<!--    --> 
<ListView 
android:id="@+id/lv" 
android:layout_width="match_parent" 
android:layout_height="wrap_content" 
android:layout_above="@id/bottom_view" 
android:layout_below="@id/ll1" 
android:layout_marginBottom="30dp" 
android:layout_marginTop="70dp" 
android:cacheColorHint="#00000000" 
android:listSelector="@drawable/lv_click_selector" 
android:divider="@null" 
android:scrollbars="none" 
android:textColor="#ffffff"/> 
</RelativeLayout> 
이 레이아웃 은 비교적 간단 합 니 다.상부 의 주요 내용 레이아웃 에 대해 이쪽 에 상단 네 비게 이 션 표시 줄 과 중의 Fragment 내용 정 보 를 놓 고 후기 에 여러분 의 기능 확장 을 남 겨 두 면 됩 니 다.
3.메 인 액 티 비 티 는 다음 과 같이 사용 합 니 다.

public class MainActivity extends BaseActivity { 
private DragLayout dl; 
private ListView lv; 
private ImageView iv_icon, iv_bottom; 
private QuickAdapter<ItemBean> quickAdapter; 
@Override 
protected void onCreate(Bundle savedInstanceState) { 
super.onCreate(savedInstanceState); 
setContentView(R.layout.activity_main); 
setStatusBar(); 
initDragLayout(); 
initView(); 
} 
private void initDragLayout() { 
dl = (DragLayout) findViewById(R.id.dl); 
dl.setDragListener(new DragLayout.DragListener() { 
//        
@Override 
public void onOpen() { 
} 
//        
@Override 
public void onClose() { 
} 
//        
@Override 
public void onDrag(float percent) { 
ViewHelper.setAlpha(iv_icon, 1 - percent); 
} 
}); 
} 
private void initView() { 
iv_icon = (ImageView) findViewById(R.id.iv_icon); 
iv_bottom = (ImageView) findViewById(R.id.iv_bottom); 
lv = (ListView) findViewById(R.id.lv); 
lv.setAdapter(quickAdapter=new QuickAdapter<ItemBean>(this,R.layout.item_left_layout, ItemDataUtils.getItemBeans()) { 
@Override 
protected void convert(BaseAdapterHelper helper, ItemBean item) { 
helper.setImageResource(R.id.item_img,item.getImg()) 
.setText(R.id.item_tv,item.getTitle()); 
} 
}); 
lv.setOnItemClickListener(new OnItemClickListener() { 
@Override 
public void onItemClick(AdapterView<?> arg0, View arg1, 
int position, long arg3) { 
Toast.makeText(MainActivity.this,"Click Item "+position,Toast.LENGTH_SHORT).show(); 
} 
}); 
iv_icon.setOnClickListener(new OnClickListener() { 
@Override 
public void onClick(View arg0) { 
dl.open(); 
} 
}); 
} 
} 
컨트롤 을 초기 화하 고 미끄럼 모니터 를 설정 하 며 왼쪽 메뉴 기능 목록 설정 을 하면 됩 니 다.하지만 위 에 서 는 QuickAdapter 의 사용 을 보 셔 야 합 니 다.BaseAdapter Helper 프레임 워 크 에 사용 해 야 합 니 다.프로젝트 build.gradle 에서 다음 과 같은 설정 을 해 야 합 니 다.

BaseAdapter 사용 에 대한 구체 적 인 설명 블 로그 주 소 는 다음 과 같 습 니 다.
4.정식 운행 효 과 는 다음 과 같다.

5.여기 밑 에 ViewDragHelper 클래스 가 필요 하기 때문에 여러분 이 사용 할 때 V4 패 키 지 를 가 져 와 야 합 니 다.그런데 저 는 ViewGragHelper 클래스 의 소스 코드 를 프로젝트 에 직접 복사 합 니 다.

(6).dragLayout 소스 코드 설명
위 에 서 는 주로 DragLayout 의 구체 적 인 실현 을 분 석 했 습 니 다.하지만 저도 DragLayout 에 주석 이 있 는 모든 소스 코드 를 붙 여서 DragLayout 의 구체 적 인 실현 코드 를 잘 알 수 있 도록 하 겠 습 니 다.

/** 
*  ViewRragHelper         
*/ 
publicclass DragLayout extends FrameLayout { 
private boolean isShowShadow = true; 
//      
private GestureDetectorCompat gestureDetector; 
//          
private ViewDragHelper dragHelper; 
//      
private DragListener dragListener; 
//        
private int range; 
//   
private int width; 
//   
private int height; 
//main     ViewGroup        
private int mainLeft; 
private Context context; 
private ImageView iv_shadow; 
//     
private RelativeLayout vg_left; 
//  (     ) 
private CustomRelativeLayout vg_main; 
//           
private Status status = Status.Close; 
public DragLayout(Context context) { 
this(context, null); 
} 
public DragLayout(Context context,AttributeSet attrs) { 
this(context, attrs, 0); 
this.context = context; 
} 
public DragLayout(Context context,AttributeSet attrs, int defStyle) { 
super(context, attrs, defStyle); 
gestureDetector = new GestureDetectorCompat(context, new YScrollDetector()); 
dragHelper =ViewDragHelper.create(this, dragHelperCallback); 
} 
class YScrollDetector extends SimpleOnGestureListener { 
@Override 
public boolean onScroll(MotionEvent e1,MotionEvent e2, float dx, float dy) { 
return Math.abs(dy) <=Math.abs(dx); 
} 
} 
/** 
*    View     ,  Callback        
*/ 
private ViewDragHelper.Callback dragHelperCallback = new ViewDragHelper.Callback() { 
/** 
*        
* @param child Child view beingdragged 
* @param left Attempted motion alongthe X axis 
* @param dx Proposed change inposition for left 
* @return 
*/ 
@Override 
public int clampViewPositionHorizontal(View child, int left, int dx) { 
if (mainLeft + dx < 0) { 
return 0; 
} else if (mainLeft + dx >range) { 
return range; 
} else { 
return left; 
} 
} 
/** 
*       View 
* @param child Child the user isattempting to capture 
* @param pointerId ID of the pointerattempting the capture 
* @return 
*/ 
@Override 
public boolean tryCaptureView(View child, int pointerId) { 
return true; 
} 
/** 
*               
*@param child Child view to check      
* @return 
*/ 
@Override 
public int getViewHorizontalDragRange(View child) { 
return width; 
} 
/** 
*      View,            ,                         
* @param releasedChild 
* @param xvel 
* @param yvel 
*/ 
@Override 
public void onViewReleased(View releasedChild, float xvel, float yvel) { 
super.onViewReleased(releasedChild,xvel, yvel); 
if (xvel > 0) { 
open(); 
} else if (xvel < 0) { 
close(); 
} else if (releasedChild == vg_main&& mainLeft > range * 0.3) { 
open(); 
} else if (releasedChild == vg_left&& mainLeft > range * 0.7) { 
open(); 
} else { 
close(); 
} 
} 
/** 
*  View               
* @param changedView View whoseposition changed 
* @param left New X coordinate of theleft edge of the view 
* @param top New Y coordinate of thetop edge of the view 
* @param dx Change in X position fromthe last call 
* @param dy Change in Y position fromthe last call 
*/ 
@Override 
public void onViewPositionChanged(View changedView, int left, int top, 
int dx, int dy) { 
if (changedView == vg_main) { 
mainLeft = left; 
} else { 
mainLeft = mainLeft + left; 
} 
if (mainLeft < 0) { 
mainLeft = 0; 
} else if (mainLeft > range) { 
mainLeft = range; 
} 
if (isShowShadow) { 
iv_shadow.layout(mainLeft, 0,mainLeft + width, height); 
} 
if (changedView == vg_left) { 
vg_left.layout(0, 0, width,height); 
vg_main.layout(mainLeft, 0,mainLeft + width, height); 
} 
dispatchDragEvent(mainLeft); 
} 
}; 
/** 
*          
*/ 
public interface DragListener { 
//     
public void onOpen(); 
//     
public void onClose(); 
//        
public void onDrag(float percent); 
} 
public void setDragListener(DragListener dragListener) { 
this.dragListener = dragListener; 
} 
/** 
*          
*           
*/ 
@Override 
protected void onFinishInflate() { 
super.onFinishInflate(); 
if (isShowShadow) { 
iv_shadow = new ImageView(context); 
iv_shadow.setImageResource(R.mipmap.shadow); 
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 
addView(iv_shadow, 1, lp); 
} 
//     
vg_left = (RelativeLayout)getChildAt(0); 
//  ( )   
vg_main = (CustomRelativeLayout)getChildAt(isShowShadow ? 2 : 1); 
vg_main.setDragLayout(this); 
vg_left.setClickable(true); 
vg_main.setClickable(true); 
} 
public ViewGroup getVg_main() { 
return vg_main; 
} 
public ViewGroup getVg_left() { 
return vg_left; 
} 
@Override 
protected void onSizeChanged(int w, int h,int oldw, int oldh) { 
super.onSizeChanged(w, h, oldw, oldh); 
width = vg_left.getMeasuredWidth(); 
height = vg_left.getMeasuredHeight(); 
//                    80% 
range = (int) (width * 0.8f); 
} 
/** 
*     left main          
* @param changed 
* @param left 
* @param top 
* @param right 
* @param bottom 
*/ 
@Override 
protected void onLayout(boolean changed,int left, int top, int right, int bottom) { 
vg_left.layout(0, 0, width, height); 
vg_main.layout(mainLeft, 0, mainLeft +width, height); 
} 
/** 
*        
* @param ev 
* @return 
*/ 
@Override 
public boolean onInterceptTouchEvent(MotionEvent ev) { 
returndragHelper.shouldInterceptTouchEvent(ev) &&gestureDetector.onTouchEvent(ev); 
} 
/** 
*         ViewDragHelper     
* @param e 
* @return 
*/ 
@Override 
public boolean onTouchEvent(MotionEvent e){ 
try { 
dragHelper.processTouchEvent(e); 
} catch (Exception ex) { 
ex.printStackTrace(); 
} 
return false; 
} 
/** 
*          
* @param mainLeft 
*/ 
private void dispatchDragEvent(intmainLeft) { 
if (dragListener == null) { 
return; 
} 
float percent = mainLeft / (float)range; 
//       
animateView(percent); 
//           
dragListener.onDrag(percent); 
Status lastStatus = status; 
if (lastStatus != getStatus()&& status == Status.Close) { 
dragListener.onClose(); 
} else if (lastStatus != getStatus()&& status == Status.Open) { 
dragListener.onOpen(); 
} 
} 
/** 
*           ,       
* @param percent 
*/ 
private void animateView(float percent) { 
float f1 = 1 - percent * 0.5f; 
ViewHelper.setTranslationX(vg_left,-vg_left.getWidth() / 2.5f + vg_left.getWidth() / 2.5f * percent); 
if (isShowShadow) { 
//             
ViewHelper.setScaleX(iv_shadow, f1* 1.2f * (1 - percent * 0.10f)); 
ViewHelper.setScaleY(iv_shadow, f1* 1.85f * (1 - percent * 0.10f)); 
} 
} 
/** 
*     ,          ,            
*/ 
@Override 
public void computeScroll() { 
if (dragHelper.continueSettling(true)){ 
ViewCompat.postInvalidateOnAnimation(this); 
} 
} 
/** 
*     (  ,  ,  ) 
*/ 
public enum Status { 
Drag, Open, Close 
} 
/** 
*        
* @return 
*/ 
public Status getStatus() { 
if (mainLeft == 0) { 
status = Status.Close; 
} else if (mainLeft == range) { 
status = Status.Open; 
} else { 
status = Status.Drag; 
} 
return status; 
} 
public void open() { 
open(true); 
} 
public void open(boolean animate) { 
if (animate) { 
//     
if(dragHelper.smoothSlideViewTo(vg_main, range, 0)) { 
ViewCompat.postInvalidateOnAnimation(this); 
} 
} else { 
vg_main.layout(range, 0, range * 2,height); 
dispatchDragEvent(range); 
} 
} 
public void close() { 
close(true); 
} 
public void close(boolean animate) { 
if (animate) { 
//     
if(dragHelper.smoothSlideViewTo(vg_main, 0, 0)) { 
ViewCompat.postInvalidateOnAnimation(this); 
} 
} else { 
vg_main.layout(0, 0, width,height); 
dispatchDragEvent(0); 
} 
} 
} 
(7).최종 총화
오늘 우 리 는 QQ 최신 버 전 QQ6.X 효 과 를 실현 하 는 동시에 View DragHelper,BaseAdapter Helper 의 활용 을 사 용 했 습 니 다.구체 적 인 이 지식 점 의 사용 방법 은 제 블 로그 에 설명 한 글 을 업데이트 하 였 습 니 다.여러분 이 보 시 는 것 을 환영 합 니 다.

좋은 웹페이지 즐겨찾기