轉載請標明出處:
http://blog.csdn.net/developer_jiangqq/article/details/50033453
本文出自:【江清清的博客】
(一).前言:
【好消息】個人網站已經上線運行,後面博客以及技術乾貨等精彩文章會同步更新,請大家關注收藏:http://www.lcode.org
這幾天正在更新錄製實戰項目,整體框架是採用仿照QQ5.X側滑效果的。那麼我們一般的做法就是自定義ViewGroup或者採用開源項目MenuDrawer或者Google提供的控件DrawerLayout等方式來實現。這些的控件的很多效果基本上都是採用實現onInterceptTouchEvent和onTouchEvent這兩個方法進行實現,而且都是根據要實現的效果做自定義處理例如:多點觸控處理,加速度方面檢測以及控制等等。一般這樣做作於普通開發人員來講也是需要很強的程序與邏輯開發能力,幸好Android開發框架給我們提供了一個組件ViewDragHelper。那麼今天我們來具體實現和分析一下ViewDragHelper
具體代碼已經上傳到下面的項目中,歡迎各位去star和fork一下。
FastDev4Android框架項目地址:https://github.com/jiangqqlmj/FastDev4Android
(二).ViewDragHelper基本介紹:
1.官方介紹如下:
對於自定義ViewGroup而言這邊的ViewDragHelper是一個很不錯的實用程序類。它給我們提供一系列的方法和相關狀態,讓我們可以進行拖拽移動或者重新定位ViewGroup中子視圖View。ViewDragHelper是一個簡化View的拖拽操作的幫助類,使用起來比較簡單與方便,一般我們只需要實現幾個方法和一個CallBack類就可以實現拖動的View。
2.在使用過程中我們一般會通過ViewDragHelper.Callback來進行連接ViewDragHelper和View。ViewDragHelper提供了一個靜態方法讓我們來創建實例,使用ViewDragHelper我們可以控制拖動的方向以及檢測是否已經拖動到屏幕的邊緣等相關操作。
3.ViewGragHelper.Callback中的相關方法說明:
Callback中的方法如下,其他包括一個抽象方法tryCaptureView()和十二個一般方法組成
tryCaptureView(View,int) | 傳遞當前觸摸的子View實例,如果當前的子View需要進行拖拽移動返回true |
clampViewPositionHorizontal | 決定拖拽的View在水平方向上面移動到的位置 |
clampViewPositionVertical | 決定拖拽的View在垂直方向上面移動到的位置 |
getViewHorizontalDragRange | 返回一個大於0的數,然後纔會在水平方向移動 |
getViewVerticalDragRange | 返回一個大於0的數,然後纔會在垂直方向移動 |
onViewDragStateChanged | 拖拽狀態發生變化回調 |
onViewPositionChanged | 當拖拽的View的位置發生變化的時候回調(特指capturedview) |
onViewCaptured | 捕獲captureview的時候回調 |
onViewReleased | 當拖拽的View手指釋放的時候回調 |
onEdgeTouched | 當觸摸屏幕邊界的時候回調 |
onEdgeLock | 是否鎖住邊界 |
onEdgeDrageStarted | 在邊緣滑動的時候可以設置滑動另一個子View跟着滑動 |
getOrderedChildIndex |
|
上面簡單講解了其中的一些方法說明,下面我們就需要使用這些方法以及ViewDragHelper本身提供的初始化方法以及設置方法來簡要說明使用ViewDragHelper使用流程。
(三).ViewDragHelper流程實例:
下面我們開始具體來使用ViewDragHelper了。
1.獲取ViewGragHelper實例:我們通過使用ViewDragHelper的一個靜態方法來創建實例:
public static ViewDragHelper create(ViewGroup forParent, float sensitivity, Callback cb) {
final ViewDragHelper helper =create(forParent, cb);
helper.mTouchSlop = (int)(helper.mTouchSlop * (1 / sensitivity));
return helper;
}
- 參數1.一個ViewGroup,也就是拖動子View的父控件(ViewGroup)
- 參數2.靈敏度一般設置成1.0f,表示靈敏度最敏感
- 參數3.拖拽回調,用來處理拖動的位置等相關操作
具體使用如下:
mDragHelper =ViewDragHelper.create(this, 1.0f, new DragHelperCallback());
2.繼承ViewGragHelper.Callback類實現一個抽象方法:tryCaptureView()然後在內部進行處理需要捕獲的子View(用於拖拽操作和移動)
/**
* 進行捕獲攔截,那些View可以進行drag操作
* @param child
* @param pointerId
* @return 直接返回true,攔截所有的VIEW
*/
@Override
public boolean tryCaptureView(Viewchild, int pointerId) {
return true;
}
這樣表示捕捉所有的子View,表示所有的子View都可以進行拖拽操作。
3.重寫onInterceptTouchView和onTouchEvent方法來攔截事件以及讓ViewDragHelper來進行處理攔截到得事件。因爲ViewDragHelper的內部是根據觸摸等相關事件來實現拖拽的。
/**
* 事件分發
* @param ev
* @return
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
returnmDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev){
mDragHelper.processTouchEvent(ev);
return true;
}
4.拖動行爲處理,例如我們現在需要處理橫向的拖拽的,那麼我們需要實現clampViewPositionHorizontal方法,並且返回一個適當的數值表示橫向拖拽的效果。 一般返回第二個參數即可,不過需要對邊界值做一下判斷這樣子View不要拖出屏幕。
【注】要實現橫向滑動這個方法必須要重寫,因爲ViewGragHelper內部該方法的時候直接返回了0,看下內部實現代碼:
public int clampViewPositionHorizontal(Viewchild, int left, int dx) {
return 0;
}
我們重寫的代碼如下:
/**
* 水平滑動 控制left
* @param child
* @param left
* @param dx
* @return
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
Log.d("DragLayout","clampViewPositionHorizontal " + left + "," + dx);
final int leftBound =getPaddingLeft();
final int rightBound = getWidth() -view_one.getWidth();
final int newLeft =Math.min(Math.max(left, leftBound), rightBound);
return newLeft;
}
同樣對於垂直方向拖拽的重寫clampViewPositionVertical()基本方法也差不多如下:
/**
* 垂直滑動,控制top
* @param child
* @param top
* @param dy
* @return
*/
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
Log.d("DragLayout","clampViewPositionVertical " + top + "," + dy);
final int topBound =getPaddingTop();
final int bottomBound = getHeight()- view_one.getHeight();
final int newTop =Math.min(Math.max(top, topBound), bottomBound);
return newTop;
}
5.基本功能實現核心代碼已經做完了,下面我們來實現一個佈局文件:自定義組件ViewGragOne內部放入了兩個TextView子View,到時候我們主要拖拽這兩個子View即可。
<?xmlversion="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"android:layout_width="match_parent"
android:layout_height="match_parent">
<includelayout="@layout/common_top_bar_layout"/>
<com.chinaztt.fda.test.ViewGragHelper.ViewGragOne
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:id="@+id/tv_one"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_margin="10dp"
android:text="TV_ONE"
android:gravity="center"
android:background="@color/color_1"/>
<TextView
android:id="@+id/tv_two"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_marginTop="200dp"
android:layout_marginLeft="50dp"
android:text="TV_TWO"
android:gravity="center"
android:background="@color/color_3"/>
</com.chinaztt.fda.test.ViewGragHelper.ViewGragOne>
</LinearLayout>
5.自定義組件ViewGragOnew繼承自LinerLayout,然後在內部採用ViewDragHelper做相關操作,具體包括以上核心操作的代碼如下:
public class ViewGragOne extends LinearLayout{
private View view_one,view_two;
private ViewDragHelper mDragHelper;
public ViewGragOne(Context context) {
this(context, null);
}
public ViewGragOne(Context context,AttributeSet attrs) {
this(context, attrs, 0);
}
public ViewGragOne(Context context,AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mDragHelper =ViewDragHelper.create(this, 1.0f, new DragHelperCallback());
}
class DragHelperCallback extends Callback {
/**
* 進行捕獲攔截,那些View可以進行drag操作
* @param child
* @param pointerId
* @return 直接返回true,攔截所有的VIEW
*/
@Override
public boolean tryCaptureView(Viewchild, int pointerId) {
return true;
}
/**
* 水平滑動 控制left
* @param child
* @param left
* @param dx
* @return
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
Log.d("DragLayout","clampViewPositionHorizontal " + left + "," + dx);
final int leftBound =getPaddingLeft();
final int rightBound = getWidth() -view_one.getWidth();
final int newLeft =Math.min(Math.max(left, leftBound), rightBound);
return newLeft;
}
/**
* 垂直滑動,控制top
* @param child
* @param top
* @param dy
* @return
*/
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
Log.d("DragLayout","clampViewPositionVertical " + top + "," + dy);
final int topBound =getPaddingTop();
final int bottomBound = getHeight()- view_one.getHeight();
final int newTop =Math.min(Math.max(top, topBound), bottomBound);
return newTop;
}
}
/**
* 事件分發
* @param ev
* @return
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
returnmDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev){
mDragHelper.processTouchEvent(ev);
return true;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
view_one=getChildAt(0);
view_two=getChildAt(1);
}
}
6.運行效果如下:
(四).ViewDragHelper進階講解:
1.tryCaptureView該方法可以進行選擇性攔截可以拖拽的子View,那麼我們這邊選擇只攔截第一個View,那麼第二個View就無法進行拖拽啦,具體實現方法如下:
@Override
public boolean tryCaptureView(Viewchild, int pointerId) {
return child==view_one;
}
實現效果如下:
2.滑動邊緣相關處理方法setEdgeTrackingEnabled(),onEdgeTouch()以及onEdgeDragStart()。
我們可以給ViewDragHelper加入滑動的邊緣設置,組件給我們提供瞭如下邊緣控制值:EDGE_LEFT,EDGE_RIGHT,EDGE_TOP,EDGE_BOTTOM和EDGE_ALL,分別控制上下左右以及全部邊緣控制。我們這邊設置左邊緣:
mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
那麼根據上面我們的方法介紹,我們可以重寫onEdgeTouched()方法來攔截觸摸到邊緣的動作。
@Override
public void onEdgeTouched(intedgeFlags, int pointerId) {
super.onEdgeTouched(edgeFlags,pointerId);
Toast.makeText(getContext(),"edgeTouched", Toast.LENGTH_SHORT).show();
}
如果要實現我們的手指在邊緣進行滑動的時候,同時根據滑動的距離來滑動另外一個View,我們可以重寫onEdgeDragStared()方法,在內部調用caturedChildView()方法實現即可。(這個效果我們就可以聯想到側滑界面效果,滑動時候可以打開主佈局界面),具體實現代碼如下:
/**
* 在邊界滑動的時候 同時滑動dragView2
* @param edgeFlags
* @param pointerId
*/
@Override
public void onEdgeDragStarted(intedgeFlags, int pointerId) {
mDragHelper.captureChildView(view_two, pointerId);
}
運行效果如下:3.除了以上的方法之外,我們還有一個手指觸摸釋放之後回調的方法,onViewReleased()。這個好比側滑組件中我們點擊一個位置,然後界面自動打開或者關閉的效果。
/**
* 當手指鬆開的時候回調方法
* @param releasedChild 滑動手指鬆開的View
* @param xvel
* @param yvel
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild,xvel, yvel);
Log.d("zttjiangqq","onViewReleased");
}
當我們拖拽一個子View,然後手指釋放之後運行效果如下:
4.ViewGragOne全部實例代碼如下:
package com.chinaztt.fda.test.ViewGragHelper;
import android.content.Context;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.widget.ViewDragHelper;
import android.support.v4.widget.ViewDragHelper.Callback;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.Toast;
import com.chinaztt.fda.utils.Log;
/**
* 當前類註釋:
* 項目名:FastDev4Android
* 包名:com.chinaztt.fda.test.ViewGragHelper
* 作者:江清清 on 15/11/24 20:29
* 郵箱:[email protected]
* QQ: 781931404
* 公司:江蘇中天科技軟件技術有限公司
*/
public class ViewGragOne extends LinearLayout{
private View view_one,view_two;
private ViewDragHelper mDragHelper;
public ViewGragOne(Context context) {
this(context, null);
}
public ViewGragOne(Context context,AttributeSet attrs) {
this(context, attrs, 0);
}
public ViewGragOne(Context context,AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mDragHelper =ViewDragHelper.create(this, 1.0f, new DragHelperCallback());
mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
}
class DragHelperCallback extends Callback {
/**
* 進行捕獲攔截,那些View可以進行drag操作
* @param child
* @param pointerId
* @return 直接返回true,攔截所有的VIEW
*/
@Override
public boolean tryCaptureView(Viewchild, int pointerId) {
return true;
}
/**
* 水平滑動 控制left
* @param child
* @param left
* @param dx
* @return
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
Log.d("DragLayout","clampViewPositionHorizontal " + left + "," + dx);
final int leftBound =getPaddingLeft();
final int rightBound = getWidth() -view_one.getWidth();
final int newLeft =Math.min(Math.max(left, leftBound), rightBound);
return newLeft;
}
/**
* 垂直滑動,控制top
* @param child
* @param top
* @param dy
* @return
*/
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
Log.d("DragLayout","clampViewPositionVertical " + top + "," + dy);
final int topBound =getPaddingTop();
final int bottomBound = getHeight()- view_one.getHeight();
final int newTop =Math.min(Math.max(top, topBound), bottomBound);
return newTop;
}
@Override
public void onEdgeTouched(int edgeFlags, int pointerId) {
super.onEdgeTouched(edgeFlags,pointerId);
Toast.makeText(getContext(),"edgeTouched", Toast.LENGTH_SHORT).show();
}
/**
* 在邊界滑動的時候 同時滑動dragView2
* @param edgeFlags
* @param pointerId
*/
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
mDragHelper.captureChildView(view_two, pointerId);
}
/**
* 當手指鬆開的時候回調方法
* @paramreleasedChild 滑動手指鬆開的View
* @param xvel
* @param yvel
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild,xvel, yvel);
Log.d("zttjiangqq","onViewReleased");
}
}
/**
* 事件分發
* @param ev
* @return
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev){
mDragHelper.processTouchEvent(ev);
return true;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
view_one=getChildAt(0);
view_two=getChildAt(1);
}
}
(五).最後總結
今天我們對於ViewDragHelper的基本使用方法和相關流程做了詳解,下一篇我們會通過ViewDragHelper來講解實現一個類似QQ5.x側滑效果的組件以及ViewDragHelper源代碼解析。
本次具體實例註釋過的全部代碼已經上傳到FastDev4Android項目中了。同時歡迎大家去Github站點進行clone或者下載瀏覽:
https://github.com/jiangqqlmj/FastDev4Android 同時歡迎大家star和fork整個開源快速開發框架項目~
尊重原創,轉載請註明:From Sky丶清(http://blog.csdn.net/developer_jiangqq) 侵權必究!
關注我的訂閱號,每天分享移動開發技術(Android/IOS),項目管理以及博客文章!
關注我的微博,可以獲得更多精彩內容