[FastDev4Android 프레임 워 크 개발] 신기 ViewDragHelper 완전 해석 의 상세 한 해석 으로 QQQ5. X 사 이 드 스 케 이 트 쿨 링 효과 실현 (34)
(1). 머리말:
[좋 은 소식] 개인 사이트 가 이미 온라인 으로 운영 되 고 있 습 니 다. 뒤의 블 로그 와 기술 건어물 등 멋 진 글 이 동시에 업 데 이 트 될 것 입 니 다. 소장 에 관심 을 가 져 주 십시오.http://www.lcode.org
요 며칠 동안 녹화 실전 프로젝트 를 업데이트 하고 있 으 며 전체 프레임 워 크 는 QQ5. X 사 이 드 스 케 이 트 효 과 를 모방 한 것 이다.그러면 Google 의 일반적인 방법 은 ViewGroup 을 사용자 정의 하거나 오픈 소스 프로젝트 인 MenuDrawer 나 Google 이 제공 하 는 컨트롤 DrawerLayout 등 을 사용 하 는 것 입 니 다.이러한 컨트롤 의 많은 효 과 는 기본적으로 onInterceptTouchEvent 와 onTouchEvent 를 실현 하 는 두 가지 방법 으로 이 루어 집 니 다. 또한 실현 하고 자 하 는 효과 에 따라 사용자 정의 처 리 를 합 니 다. 예 를 들 어 다 중 터치 처리, 가속도 측정 과 제어 등 입 니 다.일반적으로 이렇게 하 는 것 은 일반 개발 자 에 게 도 매우 강 한 프로그램 과 논리 개발 능력 이 필요 하 다. 다행히 안 드 로 이 드 개발 프레임 워 크 는 우리 에 게 구성 요소 인 View DragHelper 를 제공 해 주 었 다.지난 번 에 우 리 는 ViewGragHelper 의 기본 사용 에 대해 설명 (클릭 진입) 을 했 습 니 다. 오늘 은 QQQ5. X 사 이 드 스 케 이 트 효 과 를 실현 하 는 인 스 턴 스 (오픈 소스 프로젝트 주소 클릭 진입) 를 분석 합 니 다.
구체 적 인 코드 는 아래 항목 에 올 라 왔 습 니 다. 스타 와 포크 에 오신 것 을 환영 합 니 다.
https://github.com/jiangqqlmj/ViewDragHelperTest
FastDev4Android 프레임 워 크 항목 주소:https://github.com/jiangqqlmj/FastDev4Android
(2). ViewDragHelper 의 기본 사용
앞에서 우 리 는 View DragHelper 의 기본 적 인 사용 방법 을 배 웠 고 안에 있 는 몇 가지 방법의 용도 도 알 게 되 었 습 니 다. 다음은 기본 적 인 사용 절 차 를 복습 하 겠 습 니 다.View DragHelper 를 사용 하여 하위 View 드래그 이동 을 실현 하 는 절 차 는 다음 과 같 습 니 다.
ViewDragHelper 인 스 턴 스 만 들 기 (Callback 에 전송) 이벤트 차단 처리 방법 onInterceptTouch 와 onTouch Event 재 작성 Callback 을 실현 하고 그 중의 관련 방법 try Capture View 와 수평 또는 수직 방향 으로 이동 하 는 거리 방법 을 실현 합 니 다.
좀 더 구체 적 으로 분석 하면 앞의 블 로 그 를 보 거나 오늘 은 구체 적 인 사례 를 통 해 설명 하 겠 습 니 다.
(3). QQQ5. X 측면 미끄럼 효과 에 대한 분석:
본 격 버 전 QQ 의 사 이 드 스 케 이 트 효 과 는 다음 과 같 습 니 다.
위 를 살 펴 보면 우 리 는 두 개의 View 로 이해 할 수 있 습 니 다. 하 나 는 바닥 이 왼쪽 기능 View 에 해당 하고 다른 하 나 는 상층 의 주요 기능 내용 View 입 니 다. 우리 가 위 에서 상층 View 를 끌 거나 좌우 로 미 끄 러 질 때 상층 과 하층부의 View 는 상응 하 게 미 끄 러 지고 View 의 큰 변 화 를 하 는 동시에 관련 애니메이션 도 추가 합 니 다.물론 상부 의 View 를 클릭 하면 사 이 드 메뉴 를 열거 나 닫 을 수 있 습 니 다.
(4). 측면 미끄럼 효과 사용자 정의 구성 요소 구현
1. 우선 FrameLayout 에 통합 하여 사용자 정의 뷰 를 만 듭 니 다. 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 를 초기 화 할 때 만 들 고 ViewDragHelper 의 정적 방법 을 사용 합 니 다.
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 의 이벤트 방법 을 다시 쓰 고 터치 이 벤트 를 차단 하여 ViewDragHelper 내부 에 처리 하여 이동 서브 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 isattempting to capture
* @param pointerId ID of the pointerattempting the capture
* @return
*/
@Override
public boolean tryCaptureView(View child, 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 tocheck
* @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(intmainLeft) {
if (dragListener == null) {
return;
}
float percent = mainLeft / (float)range;
// , View
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();
// 60%
range = (int) (width * 0.6f);
}
이 방법 에서 우 리 는 실시 간 으로 너비 와 높이, 그리고 수평 거 리 를 끌 어 당 길 수 있다.
6. 위의 모든 핵심 코드 는 View DragHelper 를 사용 하여 하위 컨트롤 View 드래그 이동 을 실현 하 는 방법 입 니 다. 그러나 우리 의 측면 미끄럼 효과 에 따라 애니메이션 과 미끄럼 과정 에서 View 의 크기 조정 효 과 를 실현 해 야 하기 때문에 우 리 는 애니메이션 오픈 소스 라 이브 러 리 를 도 입 했 습 니 다. 이 라 이브 러 리 는 Github 에서 다운로드 하여 사용 할 수 있 습 니 다.물론 뒤에 나 는 이 라 이브 러 리 의 사용 을 단독으로 꺼 내 서 문장 한 편 을 써 서 설명 할 것 이다.기대 해 주세요 ~
그리고 앞 에 계 산 된 백분율 에 따라 View 보기 크기 를 조정 합 니 다.
/**
* , View
* @param percent
*/
private void animateView(float percent) {
float f1 = 1 - percent * 0.3f;
//vg_main
ViewHelper.setScaleX(vg_main, f1);
//vg_main ,
ViewHelper.setScaleY(vg_main, f1);
// X
ViewHelper.setTranslationX(vg_left,-vg_left.getWidth() / 2.3f + vg_left.getWidth() / 2.3f * percent);
//vg_left
ViewHelper.setScaleX(vg_left, 0.5f +0.5f * percent);
//vg_left
ViewHelper.setScaleY(vg_left, 0.5f +0.5f * percent);
//vg_left
ViewHelper.setAlpha(vg_left, percent);
if (isShowShadow) {
//
ViewHelper.setScaleX(iv_shadow, f1* 1.4f * (1 - percent * 0.12f));
ViewHelper.setScaleY(iv_shadow, f1* 1.85f * (1 - percent * 0.12f));
}
getBackground().setColorFilter(evaluate(percent, Color.BLACK,Color.TRANSPARENT), Mode.SRC_OVER);
}
private Integer evaluate(float fraction,Object startValue, Integer endValue) {
int startInt = (Integer) startValue;
int startA = (startInt >> 24)& 0xff;
int startR = (startInt >> 16)& 0xff;
int startG = (startInt >> 8)& 0xff;
int startB = startInt & 0xff;
int endInt = (Integer) endValue;
int endA = (endInt >> 24) &0xff;
int endR = (endInt >> 16) &0xff;
int endG = (endInt >> 8) &0xff;
int endB = endInt & 0xff;
return (int) ((startA + (int) (fraction* (endA - startA))) << 24)
| (int) ((startR + (int)(fraction * (endR - startR))) << 16)
| (int) ((startG + (int)(fraction * (endG - startG))) << 8)
| (int) ((startB + (int)(fraction * (endB - startB))));
}
7. 물론 위 에 있 는 것 외 에 한 가지 효과 가 부족 하 다. 우리 가 미 끄 러 지 는 과정 에서 손가락 이 풀 리 면 상식 적 으로 view 는 이동 하지 않 을 것 이다. 그러면 여기 서 우 리 는 가속도 가 필요 하 다. 우리 가 풀 린 후에 도 일정한 속 도 를 유지 할 수 있다. 어떻게 실현 해 야 할 까?정 답 은 컴퓨터 스크롤 () 을 실현 하 는 방법 이다.
/**
* , ,
*/
@Override
public void computeScroll() {
if (dragHelper.continueSettling(true)){
ViewCompat.postInvalidateOnAnimation(this);
}
}
OK 위 에 있 는 DragLayout 에 대한 핵심 코드 는 많 지 않 습 니 다. 다음은 DragLayout 류 를 사용 하여 사 이 드 스 케 이 트 효 과 를 실현 합 니 다!
(5). 측면 미끄럼 효과 구성 요소 사용
1. 먼저 사용 한 레이아웃 파일 은 다음 과 같 습 니 다.
<com.chinaztt.widget.DragLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/ic_main_left_bg"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="30dp"
android:paddingLeft="30dp"
android:paddingTop="50dp"
>
<LinearLayout
android:id="@+id/ll1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<ImageView
android:id="@+id/iv_bottom"
android:layout_width="70dp"
android:layout_height="70dp"
android:src="@mipmap/ic_launcher" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_gravity="center_vertical"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="20dp"
android:text=" :jiangqqlmj"
android:textColor="#ffffff"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="20dp"
android:text="QQ:781931404"
android:textColor="#ffffff"
android:textSize="16sp" />
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/tv_mail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="[email protected]"
android:textColor="#ffffff"
android:textSize="15sp"/>
<!-- -->
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/tv_mail"
android:layout_below="@id/ll1"
android:layout_marginBottom="30dp"
android:layout_marginTop="20dp"
android:cacheColorHint="#00000000"
android:listSelector="@null"
android:divider="@null"
android:scrollbars="none"
android:textColor="#ffffff" />
</RelativeLayout>
<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:background="#e7abff"
android:gravity="bottom"
>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="49dp"
>
<ImageView
android:id="@+id/iv_icon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:scaleType="fitXY"
android:src="@mipmap/ic_launcher"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ViewDragHelper "
android:layout_centerInParent="true"
android:textColor="#ffffff"
android:textSize="20sp"
/>
</RelativeLayout>
</RelativeLayout>
<!-- Fragment-->
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:id="@+id/iv_noimg"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" ..."
/>
</FrameLayout>
</LinearLayout>
</com.chinaztt.widget.CustomRelativeLayout>
</com.chinaztt.widget.DragLayout>
이 레이아웃 파일 의 부모 층 View 는 바로 DragLayout 입 니 다. 그 다음 에 내부 에 두 개의 RelativeLayout 레이아웃 이 있 는데 각각 왼쪽 View (왼쪽 기능) 와 주 Main View 를 충당 합 니 다.마지막 으로 Activity 에서 DragLayout 컨트롤 을 가 져 오고 이벤트 모니터 (DragListener) 의 구체 적 인 코드 를 다음 과 같이 추가 합 니 다.
public class MainActivity extends BaseActivity {
privateDragLayout dl;
privateListView lv;
privateTextView tv_noimg;
privateImageView iv_icon, iv_bottom;
@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);
tv_noimg= (TextView) findViewById(R.id.iv_noimg);
lv= (ListView) findViewById(R.id.lv);
lv.setAdapter(new ArrayAdapter<String>(MainActivity.this,
R.layout.item_text,new String[] { "item 01", "item 01",
"item01", "item 01", "item 01", "item 01",
"item01", "item 01", "item 01", "item 01",
"item01", "item 01", "item 01", "item 01",
"item01", "item 01", "item 01",
"item01", "item 01", "item 01", "item 01"}));
lv.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1,
intposition, long arg3) {
Toast.makeText(MainActivity.this,"ClickItem "+position,Toast.LENGTH_SHORT).show();
}
});
iv_icon.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
dl.open();
}
});
}
@Override
protectedvoid onResume() {
super.onResume();
}
}
최종 실행 효 과 는 다음 과 같 습 니 다.
여기 밑 에 View DragHelper 류 가 필요 하기 때문에 여러분 이 사용 할 때 V4 패 키 지 를 가 져 와 야 합 니 다. 그런데 저 는 View DragHelper 류 의 소스 코드 를 프로젝트 에 직접 복사 합 니 다.
(6). dragLayout 소스 코드 설명
위 에 서 는 주로 DragLayout 의 구체 적 인 실현 을 분 석 했 습 니 다. 하지만 저도 DragLayout 에 주석 이 있 는 모든 소스 코드 를 붙 여서 DragLayout 의 구체 적 인 실현 코드 를 잘 알 수 있 도록 하 겠 습 니 다.
/**
* ViewRragHelper
*/
public class 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.CallbackdragHelperCallback = 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 tocheck
* @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();
// 60%
range = (int) (width * 0.6f);
}
/**
* 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(int mainLeft) {
if (dragListener == null) {
return;
}
float percent = mainLeft / (float)range;
// , View
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();
}
}
/**
* , View
* @param percent
*/
private void animateView(float percent) {
float f1 = 1 - percent * 0.3f;
//vg_main
ViewHelper.setScaleX(vg_main, f1);
//vg_main ,
ViewHelper.setScaleY(vg_main, f1);
// X
ViewHelper.setTranslationX(vg_left,-vg_left.getWidth() / 2.3f + vg_left.getWidth() / 2.3f * percent);
//vg_left
ViewHelper.setScaleX(vg_left, 0.5f +0.5f * percent);
//vg_left
ViewHelper.setScaleY(vg_left, 0.5f +0.5f * percent);
//vg_left
ViewHelper.setAlpha(vg_left, percent);
if (isShowShadow) {
//
ViewHelper.setScaleX(iv_shadow, f1* 1.4f * (1 - percent * 0.12f));
ViewHelper.setScaleY(iv_shadow, f1* 1.85f * (1 - percent * 0.12f));
}
getBackground().setColorFilter(evaluate(percent, Color.BLACK,Color.TRANSPARENT), Mode.SRC_OVER);
}
private Integer evaluate(float fraction,Object startValue, Integer endValue) {
int startInt = (Integer) startValue;
int startA = (startInt >> 24)& 0xff;
int startR = (startInt >> 16)& 0xff;
int startG = (startInt >> 8)& 0xff;
int startB = startInt & 0xff;
int endInt = (Integer) endValue;
int endA = (endInt >> 24) &0xff;
int endR = (endInt >> 16) &0xff;
int endG = (endInt >> 8) &0xff;
int endB = endInt & 0xff;
return (int) ((startA + (int) (fraction* (endA - startA))) << 24)
| (int) ((startR + (int)(fraction * (endR - startR))) << 16)
| (int) ((startG + (int)(fraction * (endG - startG))) << 8)
| (int) ((startB + (int)(fraction * (endB - startB))));
}
/**
* , ,
*/
@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). 최종 총화
오늘 우 리 는 View DragHelper 를 통 해 QQ5. x 사 이 드 스 케 이 트 효과 와 유사 한 구성 요소 의 효 과 를 설명 합 니 다. 또한 이 항목 에서 도 몰입 식 상태의 효 과 를 사 용 했 습 니 다.이 효과 의 구체 적 인 실현 에 대해 서 는 제 다른 글 을 보 실 수 있 습 니 다.
이번 구체 적 인 실례 주석 을 달 았 던 모든 코드 가 Github 프로젝트 에 업로드 되 었 습 니 다.또한 Github 사이트 에 가서 클론 이나 다운로드 탐색 을 하 는 것 을 환영 합 니 다.
https://github.com/jiangqqlmj/ViewDragHelperTest
스타 와 포크 의 전체 오픈 소스 빠 른 개발 프레임 워 크 프로젝트 를 환영 합 니 다 ~
동시에 감사 드 립 니 다.https://github.com/BlueMor/DragLayout
오리지널 을 존중 하고 전재 하 시 려 면: From Sky 주 청 (http://blog.csdn.net/developer_jiangqq) 권리 침 해 는 반드시 추궁 해 야 한다!
나의 구독 번 호 를 주목 하고 매일 모 바 일 개발 기술 (Android / IOS), 프로젝트 관리 와 블 로그 글 을 공유 합 니 다!
나의 웨 이 보 를 주목 하면 더 많은 멋 진 내용 을 얻 을 수 있다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin의 기초 - 2부지난 글에서는 Kotlin이 무엇인지, Kotlin의 특징, Kotlin에서 변수 및 데이터 유형을 선언하는 방법과 같은 Kotlin의 기본 개념에 대해 배웠습니다. 유형 변환은 데이터 변수의 한 유형을 다른 데이터...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.