欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android應(yīng)用開發(fā)中自定義ViewGroup視圖容器的教程

 更新時(shí)間:2016年04月26日 15:12:34   作者:鴻洋_  
這篇文章主要介紹了Android應(yīng)用開發(fā)中自定義ViewGroup視圖容器的教程,重點(diǎn)在于View之間的參數(shù)傳遞,文中還講到了使用ViewDragHelper自定義ViewGroup的方法,需要的朋友可以參考下

一、概述
在寫代碼之前,我必須得問幾個(gè)問題:
1、ViewGroup的職責(zé)是啥?
ViewGroup相當(dāng)于一個(gè)放置View的容器,并且我們?cè)趯懖季謝ml的時(shí)候,會(huì)告訴容器(凡是以layout為開頭的屬性,都是為用于告訴容器的),我們的寬度(layout_width)、高度(layout_height)、對(duì)齊方式(layout_gravity)等;當(dāng)然還有margin等;于是乎,ViewGroup的職能為:給childView計(jì)算出建議的寬和高和測(cè)量模式 ;決定childView的位置;為什么只是建議的寬和高,而不是直接確定呢,別忘了childView寬和高可以設(shè)置為wrap_content,這樣只有childView才能計(jì)算出自己的寬和高。
2、View的職責(zé)是啥?
View的職責(zé),根據(jù)測(cè)量模式和ViewGroup給出的建議的寬和高,計(jì)算出自己的寬和高;同時(shí)還有個(gè)更重要的職責(zé)是:在ViewGroup為其指定的區(qū)域內(nèi)繪制自己的形態(tài)。
3、ViewGroup和LayoutParams之間的關(guān)系?
大家可以回憶一下,當(dāng)在LinearLayout中寫childView的時(shí)候,可以寫layout_gravity,layout_weight屬性;在RelativeLayout中的childView有l(wèi)ayout_centerInParent屬性,卻沒有l(wèi)ayout_gravity,layout_weight,這是為什么呢?這是因?yàn)槊總€(gè)ViewGroup需要指定一個(gè)LayoutParams,用于確定支持childView支持哪些屬性,比如LinearLayout指定LinearLayout.LayoutParams等。如果大家去看LinearLayout的源碼,會(huì)發(fā)現(xiàn)其內(nèi)部定義了LinearLayout.LayoutParams,在此類中,你可以發(fā)現(xiàn)weight和gravity的身影。

二、View的3種測(cè)量模式
上面提到了ViewGroup會(huì)為childView指定測(cè)量模式,下面簡(jiǎn)單介紹下三種測(cè)量模式:
EXACTLY:表示設(shè)置了精確的值,一般當(dāng)childView設(shè)置其寬、高為精確值、match_parent時(shí),ViewGroup會(huì)將其設(shè)置為EXACTLY;
AT_MOST:表示子布局被限制在一個(gè)最大值內(nèi),一般當(dāng)childView設(shè)置其寬、高為wrap_content時(shí),ViewGroup會(huì)將其設(shè)置為AT_MOST;
UNSPECIFIED:表示子布局想要多大就多大,一般出現(xiàn)在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此種模式比較少見。
注:上面的每一行都有一個(gè)一般,意思上述不是絕對(duì)的,對(duì)于childView的mode的設(shè)置還會(huì)和ViewGroup的測(cè)量mode有一定的關(guān)系;當(dāng)然了,這是第一篇自定義ViewGroup,而且絕大部分情況都是上面的規(guī)則,所以為了通俗易懂,暫不深入討論其他內(nèi)容。

三、從API角度進(jìn)行淺析
上面敘述了ViewGroup和View的職責(zé),下面從API角度進(jìn)行淺析。
View的根據(jù)ViewGroup傳人的測(cè)量值和模式,對(duì)自己寬高進(jìn)行確定(onMeasure中完成),然后在onDraw中完成對(duì)自己的繪制。
ViewGroup需要給View傳入view的測(cè)量值和模式(onMeasure中完成),而且對(duì)于此ViewGroup的父布局,自己也需要在onMeasure中完成對(duì)自己寬和高的確定。此外,需要在onLayout中完成對(duì)其childView的位置的指定。
四、完整的例子
需求:我們定義一個(gè)ViewGroup,內(nèi)部可以傳入0到4個(gè)childView,分別依次顯示在左上角,右上角,左下角,右下角。
1、決定該ViewGroup的LayoutParams
對(duì)于我們這個(gè)例子,我們只需要ViewGroup能夠支持margin即可,那么我們直接使用系統(tǒng)的MarginLayoutParams

@Override 
 public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) 
 { 
  return new MarginLayoutParams(getContext(), attrs); 
 } 

重寫父類的該方法,返回MarginLayoutParams的實(shí)例,這樣就為我們的ViewGroup指定了其LayoutParams為MarginLayoutParams。
2、onMeasure
在onMeasure中計(jì)算childView的測(cè)量值以及模式,以及設(shè)置自己的寬和高:

/** 
  * 計(jì)算所有ChildView的寬度和高度 然后根據(jù)ChildView的計(jì)算結(jié)果,設(shè)置自己的寬和高 
  */ 
 @Override 
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
 { 
  /** 
   * 獲得此ViewGroup上級(jí)容器為其推薦的寬和高,以及計(jì)算模式 
   */ 
  int widthMode = MeasureSpec.getMode(widthMeasureSpec); 
  int heightMode = MeasureSpec.getMode(heightMeasureSpec); 
  int sizeWidth = MeasureSpec.getSize(widthMeasureSpec); 
  int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); 
 
 
  // 計(jì)算出所有的childView的寬和高 
  measureChildren(widthMeasureSpec, heightMeasureSpec); 
  /** 
   * 記錄如果是wrap_content是設(shè)置的寬和高 
   */ 
  int width = 0; 
  int height = 0; 
 
  int cCount = getChildCount(); 
 
  int cWidth = 0; 
  int cHeight = 0; 
  MarginLayoutParams cParams = null; 
 
  // 用于計(jì)算左邊兩個(gè)childView的高度 
  int lHeight = 0; 
  // 用于計(jì)算右邊兩個(gè)childView的高度,最終高度取二者之間大值 
  int rHeight = 0; 
 
  // 用于計(jì)算上邊兩個(gè)childView的寬度 
  int tWidth = 0; 
  // 用于計(jì)算下面兩個(gè)childiew的寬度,最終寬度取二者之間大值 
  int bWidth = 0; 
 
  /** 
   * 根據(jù)childView計(jì)算的出的寬和高,以及設(shè)置的margin計(jì)算容器的寬和高,主要用于容器是warp_content時(shí) 
   */ 
  for (int i = 0; i < cCount; i++) 
  { 
   View childView = getChildAt(i); 
   cWidth = childView.getMeasuredWidth(); 
   cHeight = childView.getMeasuredHeight(); 
   cParams = (MarginLayoutParams) childView.getLayoutParams(); 
 
   // 上面兩個(gè)childView 
   if (i == 0 || i == 1) 
   { 
    tWidth += cWidth + cParams.leftMargin + cParams.rightMargin; 
   } 
 
   if (i == 2 || i == 3) 
   { 
    bWidth += cWidth + cParams.leftMargin + cParams.rightMargin; 
   } 
 
   if (i == 0 || i == 2) 
   { 
    lHeight += cHeight + cParams.topMargin + cParams.bottomMargin; 
   } 
 
   if (i == 1 || i == 3) 
   { 
    rHeight += cHeight + cParams.topMargin + cParams.bottomMargin; 
   } 
 
  } 
   
  width = Math.max(tWidth, bWidth); 
  height = Math.max(lHeight, rHeight); 
 
  /** 
   * 如果是wrap_content設(shè)置為我們計(jì)算的值 
   * 否則:直接設(shè)置為父容器計(jì)算的值 
   */ 
  setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth 
    : width, (heightMode == MeasureSpec.EXACTLY) ? sizeHeight 
    : height); 
 } 

10-14行,獲取該ViewGroup父容器為其設(shè)置的計(jì)算模式和尺寸,大多情況下,只要不是wrap_content,父容器都能正確的計(jì)算其尺寸。所以我們自己需要計(jì)算如果設(shè)置為wrap_content時(shí)的寬和高,如何計(jì)算呢?那就是通過其childView的寬和高來(lái)進(jìn)行計(jì)算。
17行,通過ViewGroup的measureChildren方法為其所有的孩子設(shè)置寬和高,此行執(zhí)行完成后,childView的寬和高都已經(jīng)正確的計(jì)算過了
43-71行,根據(jù)childView的寬和高,以及margin,計(jì)算ViewGroup在wrap_content時(shí)的寬和高。
80-82行,如果寬高屬性值為wrap_content,則設(shè)置為43-71行中計(jì)算的值,否則為其父容器傳入的寬和高。
3、onLayout對(duì)其所有childView進(jìn)行定位(設(shè)置childView的繪制區(qū)域)
 

// abstract method in viewgroup 
 @Override 
 protected void onLayout(boolean changed, int l, int t, int r, int b) 
 { 
  int cCount = getChildCount(); 
  int cWidth = 0; 
  int cHeight = 0; 
  MarginLayoutParams cParams = null; 
  /** 
   * 遍歷所有childView根據(jù)其寬和高,以及margin進(jìn)行布局 
   */ 
  for (int i = 0; i < cCount; i++) 
  { 
   View childView = getChildAt(i); 
   cWidth = childView.getMeasuredWidth(); 
   cHeight = childView.getMeasuredHeight(); 
   cParams = (MarginLayoutParams) childView.getLayoutParams(); 
 
   int cl = 0, ct = 0, cr = 0, cb = 0; 
 
   switch (i) 
   { 
   case 0: 
    cl = cParams.leftMargin; 
    ct = cParams.topMargin; 
    break; 
   case 1: 
    cl = getWidth() - cWidth - cParams.leftMargin 
      - cParams.rightMargin; 
    ct = cParams.topMargin; 
 
    break; 
   case 2: 
    cl = cParams.leftMargin; 
    ct = getHeight() - cHeight - cParams.bottomMargin; 
    break; 
   case 3: 
    cl = getWidth() - cWidth - cParams.leftMargin 
      - cParams.rightMargin; 
    ct = getHeight() - cHeight - cParams.bottomMargin; 
    break; 
 
   } 
   cr = cl + cWidth; 
   cb = cHeight + ct; 
   childView.layout(cl, ct, cr, cb); 
  } 
 
 } 

代碼比較容易懂:遍歷所有的childView,根據(jù)childView的寬和高以及margin,然后分別將0,1,2,3位置的childView依次設(shè)置到左上、右上、左下、右下的位置。
如果是第一個(gè)View(index=0) :則childView.layout(cl, ct, cr, cb); cl為childView的leftMargin , ct 為topMargin , cr 為cl+ cWidth , cb為 ct + cHeight
如果是第二個(gè)View(index=1) :則childView.layout(cl, ct, cr, cb);
cl為getWidth() - cWidth - cParams.leftMargin- cParams.rightMargin;
ct 為topMargin , cr 為cl+ cWidth , cb為 ct + cHeight
剩下兩個(gè)類似~
這樣就完成了,我們的ViewGroup代碼的編寫,下面我們進(jìn)行測(cè)試,分別設(shè)置寬高為固定值,wrap_content,match_parent
4、測(cè)試結(jié)果
布局1:

<com.example.zhy_custom_viewgroup.CustomImgContainer xmlns:android="http://schemas.android.com/apk/res/android" 
 xmlns:tools="http://schemas.android.com/tools" 
 android:layout_width="200dp" 
 android:layout_height="200dp" 
 android:background="#AA333333" > 
 
 <TextView 
  android:layout_width="50dp" 
  android:layout_height="50dp" 
  android:background="#FF4444" 
  android:gravity="center" 
  android:text="0" 
  android:textColor="#FFFFFF" 
  android:textSize="22sp" 
  android:textStyle="bold" /> 
 
 <TextView 
  android:layout_width="50dp" 
  android:layout_height="50dp" 
  android:background="#00ff00" 
  android:gravity="center" 
  android:text="1" 
  android:textColor="#FFFFFF" 
  android:textSize="22sp" 
  android:textStyle="bold" /> 
 
 <TextView 
  android:layout_width="50dp" 
  android:layout_height="50dp" 
  android:background="#ff0000" 
  android:gravity="center" 
  android:text="2" 
  android:textColor="#FFFFFF" 
  android:textSize="22sp" 
  android:textStyle="bold" /> 
 
 <TextView 
  android:layout_width="50dp" 
  android:layout_height="50dp" 
  android:background="#0000ff" 
  android:gravity="center" 
  android:text="3" 
  android:textColor="#FFFFFF" 
  android:textSize="22sp" 
  android:textStyle="bold" /> 
 
</com.example.zhy_custom_viewgroup.CustomImgContainer> 

ViewGroup寬和高設(shè)置為固定值
效果圖:

2016426150854570.png (367×312)

布局2:

<com.example.zhy_custom_viewgroup.CustomImgContainer xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:tools="http://schemas.android.com/tools" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:background="#AA333333" > 
 
  <TextView 
    android:layout_width="150dp" 
    android:layout_height="150dp" 
    android:background="#E5ED05" 
    android:gravity="center" 
    android:text="0" 
    android:textColor="#FFFFFF" 
    android:textSize="22sp" 
    android:textStyle="bold" /> 
 
  <TextView 
    android:layout_width="50dp" 
    android:layout_height="50dp" 
    android:background="#00ff00" 
    android:gravity="center" 
    android:text="1" 
    android:textColor="#FFFFFF" 
    android:textSize="22sp" 
    android:textStyle="bold" /> 
 
  <TextView 
    android:layout_width="50dp" 
    android:layout_height="50dp" 
    android:background="#ff0000" 
    android:gravity="center" 
    android:text="2" 
    android:textColor="#FFFFFF" 
    android:textSize="22sp" 
    android:textStyle="bold" /> 
 
  <TextView 
    android:layout_width="50dp" 
    android:layout_height="50dp" 
    android:background="#0000ff" 
    android:gravity="center" 
    android:text="3" 
    android:textColor="#FFFFFF" 
    android:textSize="22sp" 
    android:textStyle="bold" /> 
 
</com.example.zhy_custom_viewgroup.CustomImgContainer> 

 ViewGroup的寬和高設(shè)置為wrap_content
效果圖:

2016426160836216.png (363×311)

布局3:

<com.example.zhy_custom_viewgroup.CustomImgContainer xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:tools="http://schemas.android.com/tools" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:background="#AA333333" > 
 
  <TextView 
    android:layout_width="150dp" 
    android:layout_height="150dp" 
    android:background="#E5ED05" 
    android:gravity="center" 
    android:text="0" 
    android:textColor="#FFFFFF" 
    android:textSize="22sp" 
    android:textStyle="bold" /> 
 
  <TextView 
    android:layout_width="50dp" 
    android:layout_height="50dp" 
    android:background="#00ff00" 
    android:gravity="center" 
    android:text="1" 
    android:textColor="#FFFFFF" 
    android:textSize="22sp" 
    android:textStyle="bold" /> 
 
  <TextView 
    android:layout_width="50dp" 
    android:layout_height="50dp" 
    android:background="#ff0000" 
    android:gravity="center" 
    android:text="2" 
    android:textColor="#FFFFFF" 
    android:textSize="22sp" 
    android:textStyle="bold" /> 
 
  <TextView 
    android:layout_width="150dp" 
    android:layout_height="150dp" 
    android:background="#0000ff" 
    android:gravity="center" 
    android:text="3" 
    android:textColor="#FFFFFF" 
    android:textSize="22sp" 
    android:textStyle="bold" /> 
 
</com.example.zhy_custom_viewgroup.CustomImgContainer> 

ViewGroup的寬和高設(shè)置為match_parent

2016426160906486.png (363×622)

可以看到無(wú)論ViewGroup的寬和高的值如何定義,我們的需求都實(shí)現(xiàn)了預(yù)期的效果~~

四、使用ViewDragHelper自定義ViewGroup
1、概述

在自定義ViewGroup中,很多效果都包含用戶手指去拖動(dòng)其內(nèi)部的某個(gè)View(eg:側(cè)滑菜單等),針對(duì)具體的需要去寫好onInterceptTouchEvent和onTouchEvent這兩個(gè)方法是一件很不容易的事,需要自己去處理:多手指的處理、加速度檢測(cè)等等。
好在官方在v4的支持包中提供了ViewDragHelper這樣一個(gè)類來(lái)幫助我們方便的編寫自定義ViewGroup。簡(jiǎn)單看一下它的注釋:

ViewDragHelper is a utility class for writing custom ViewGroups. It offers a number
of useful operations and state tracking for allowing a user to drag and reposition
views within their parent ViewGroup.
下面將重點(diǎn)介紹ViewDragHelper的使用,并且最終去實(shí)現(xiàn)一個(gè)類似DrawerLayout的一個(gè)自定義的ViewGroup。(ps:官方的DrawerLayout就是用此類實(shí)現(xiàn))

2、入門小示例

首先我們通過一個(gè)簡(jiǎn)單的例子來(lái)看看其快捷的用法,分為以下幾個(gè)步驟:

A、創(chuàng)建實(shí)例
B、觸摸相關(guān)的方法的調(diào)用
C、ViewDragHelper.Callback實(shí)例的編寫
(1) 自定義ViewGroup

package com.zhy.learn.view;

import android.content.Context;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;

/**
 * Created by zhy on 15/6/3.
 */
public class VDHLayout extends LinearLayout
{
 private ViewDragHelper mDragger;

 public VDHLayout(Context context, AttributeSet attrs)
 {
  super(context, attrs);
  mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback()
  {
   @Override
   public boolean tryCaptureView(View child, int pointerId)
   {
    return true;
   }

   @Override
   public int clampViewPositionHorizontal(View child, int left, int dx)
   {
    return left;
   }

   @Override
   public int clampViewPositionVertical(View child, int top, int dy)
   {
    return top;
   }
  });
 }

 @Override
 public boolean onInterceptTouchEvent(MotionEvent event)
 {
  return mDragger.shouldInterceptTouchEvent(event);
 }

 @Override
 public boolean onTouchEvent(MotionEvent event)
 {
  mDragger.processTouchEvent(event);
  return true;
 }
}

可以看到,上面整個(gè)自定義ViewGroup的代碼非常簡(jiǎn)潔,遵循上述3個(gè)步驟:

A、創(chuàng)建實(shí)例

mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback()
  {
  });

創(chuàng)建實(shí)例需要3個(gè)參數(shù),第一個(gè)就是當(dāng)前的ViewGroup,第二個(gè)sensitivity,主要用于設(shè)置touchSlop:

helper.mTouchSlop = (int) (helper.mTouchSlop * (1 / sensitivity));

可見傳入越大,mTouchSlop的值就會(huì)越小。第三個(gè)參數(shù)就是Callback,在用戶的觸摸過程中會(huì)回調(diào)相關(guān)方法,后面會(huì)細(xì)說。

B、觸摸相關(guān)方法

 @Override
 public boolean onInterceptTouchEvent(MotionEvent event)
 {
  return mDragger.shouldInterceptTouchEvent(event);
 }

 @Override
 public boolean onTouchEvent(MotionEvent event)
 {
  mDragger.processTouchEvent(event);
  return true;
 }

onInterceptTouchEvent中通過使用mDragger.shouldInterceptTouchEvent(event)來(lái)決定我們是否應(yīng)該攔截當(dāng)前的事件。onTouchEvent中通過mDragger.processTouchEvent(event)處理事件。

C、實(shí)現(xiàn)ViewDragHelper.CallCack相關(guān)方法

new ViewDragHelper.Callback()
  {
   @Override
   public boolean tryCaptureView(View child, int pointerId)
   {
    return true;
   }

   @Override
   public int clampViewPositionHorizontal(View child, int left, int dx)
   {
    return left;
   }

   @Override
   public int clampViewPositionVertical(View child, int top, int dy)
   {
    return top;
   }
  }

ViewDragHelper中攔截和處理事件時(shí),需要會(huì)回調(diào)CallBack中的很多方法來(lái)決定一些事,比如:哪些子View可以移動(dòng)、對(duì)個(gè)移動(dòng)的View的邊界的控制等等。

上面復(fù)寫的3個(gè)方法:

tryCaptureView如何返回ture則表示可以捕獲該view,你可以根據(jù)傳入的第一個(gè)view參數(shù)決定哪些可以捕獲
clampViewPositionHorizontal,clampViewPositionVertical可以在該方法中對(duì)child移動(dòng)的邊界進(jìn)行控制,left , top 分別為即將移動(dòng)到的位置,比如橫向的情況下,我希望只在ViewGroup的內(nèi)部移動(dòng),即:最小>=paddingleft,最大<=ViewGroup.getWidth()-paddingright-child.getWidth。就可以按照如下代碼編寫:

 @Override
   public int clampViewPositionHorizontal(View child, int left, int dx)
   {
    final int leftBound = getPaddingLeft();
    final int rightBound = getWidth() - mDragView.getWidth() - leftBound;

    final int newLeft = Math.min(Math.max(left, leftBound), rightBound);

    return newLeft;
   }

經(jīng)過上述3個(gè)步驟,我們就完成了一個(gè)簡(jiǎn)單的自定義ViewGroup,可以自由的拖動(dòng)子View。

簡(jiǎn)單看一下布局文件

(2) 布局文件

<com.zhy.learn.view.VDHLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:orientation="vertical"
        android:layout_height="match_parent"
 >

 <TextView
  android:layout_margin="10dp"
  android:gravity="center"
  android:layout_gravity="center"
  android:background="#44ff0000"
  android:text="I can be dragged !"
  android:layout_width="100dp"
  android:layout_height="100dp"/>

 <TextView
  android:layout_margin="10dp"
  android:layout_gravity="center"
  android:gravity="center"
  android:background="#44ff0000"
  android:text="I can be dragged !"
  android:layout_width="100dp"
  android:layout_height="100dp"/>

 <TextView
  android:layout_margin="10dp"
  android:layout_gravity="center"
  android:gravity="center"
  android:background="#44ff0000"
  android:text="I can be dragged !"
  android:layout_width="100dp"
  android:layout_height="100dp"/>

</com.zhy.learn.view.VDHLayout>

我們的自定義ViewGroup中有三個(gè)TextView。

當(dāng)前效果:

2016426160532693.gif (436×684)

可以看到短短數(shù)行代碼就可以玩起來(lái)了~~~

有了直觀的認(rèn)識(shí)以后,我們還需要對(duì)ViewDragHelper.CallBack里面的方法做下深入的理解。首先我們需要考慮的是:我們的ViewDragHelper不僅僅說只能夠去讓子View去跟隨我們手指移動(dòng),我們繼續(xù)往下學(xué)習(xí)其他的功能。

3、功能展示

ViewDragHelper還能做以下的一些操作:

邊界檢測(cè)、加速度檢測(cè)(eg:DrawerLayout邊界觸發(fā)拉出)
回調(diào)Drag Release(eg:DrawerLayout部分,手指抬起,自動(dòng)展開/收縮)
移動(dòng)到某個(gè)指定的位置(eg:點(diǎn)擊Button,展開/關(guān)閉Drawerlayout)
那么我們接下來(lái)對(duì)我們最基本的例子進(jìn)行改造,包含上述的幾個(gè)操作。

首先看一下我們修改后的效果:

2016426160613495.gif (417×627)

簡(jiǎn)單的為每個(gè)子View添加了不同的操作:

第一個(gè)View,就是演示簡(jiǎn)單的移動(dòng)
第二個(gè)View,演示除了移動(dòng)后,松手自動(dòng)返回到原本的位置。(注意你拖動(dòng)的越快,返回的越快)
第三個(gè)View,邊界移動(dòng)時(shí)對(duì)View進(jìn)行捕獲。

好了,看完效果圖,來(lái)看下代碼的修改:

修改后的代碼

package com.zhy.learn.view;

import android.content.Context;
import android.graphics.Point;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;

/**
 * Created by zhy on 15/6/3.
 */
public class VDHLayout extends LinearLayout
{
 private ViewDragHelper mDragger;

 private View mDragView;
 private View mAutoBackView;
 private View mEdgeTrackerView;

 private Point mAutoBackOriginPos = new Point();

 public VDHLayout(Context context, AttributeSet attrs)
 {
  super(context, attrs);
  mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback()
  {
   @Override
   public boolean tryCaptureView(View child, int pointerId)
   {
    //mEdgeTrackerView禁止直接移動(dòng)
    return child == mDragView || child == mAutoBackView;
   }

   @Override
   public int clampViewPositionHorizontal(View child, int left, int dx)
   {
    return left;
   }

   @Override
   public int clampViewPositionVertical(View child, int top, int dy)
   {
    return top;
   }


   //手指釋放的時(shí)候回調(diào)
   @Override
   public void onViewReleased(View releasedChild, float xvel, float yvel)
   {
    //mAutoBackView手指釋放時(shí)可以自動(dòng)回去
    if (releasedChild == mAutoBackView)
    {
     mDragger.settleCapturedViewAt(mAutoBackOriginPos.x, mAutoBackOriginPos.y);
     invalidate();
    }
   }

   //在邊界拖動(dòng)時(shí)回調(diào)
   @Override
   public void onEdgeDragStarted(int edgeFlags, int pointerId)
   {
    mDragger.captureChildView(mEdgeTrackerView, pointerId);
   }
  });
  mDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
 }


 @Override
 public boolean onInterceptTouchEvent(MotionEvent event)
 {
  return mDragger.shouldInterceptTouchEvent(event);
 }

 @Override
 public boolean onTouchEvent(MotionEvent event)
 {
  mDragger.processTouchEvent(event);
  return true;
 }

 @Override
 public void computeScroll()
 {
  if(mDragger.continueSettling(true))
  {
   invalidate();
  }
 }

 @Override
 protected void onLayout(boolean changed, int l, int t, int r, int b)
 {
  super.onLayout(changed, l, t, r, b);

  mAutoBackOriginPos.x = mAutoBackView.getLeft();
  mAutoBackOriginPos.y = mAutoBackView.getTop();
 }

 @Override
 protected void onFinishInflate()
 {
  super.onFinishInflate();

  mDragView = getChildAt(0);
  mAutoBackView = getChildAt(1);
  mEdgeTrackerView = getChildAt(2);
 }
}

布局文件我們僅僅是換了下文本和背景色就不重復(fù)貼了。

第一個(gè)View基本沒做任何修改。

第二個(gè)View,我們?cè)趏nLayout之后保存了最開啟的位置信息,最主要還是重寫了Callback中的onViewReleased,我們?cè)趏nViewReleased中判斷如果是mAutoBackView則調(diào)用settleCapturedViewAt回到初始的位置。大家可以看到緊隨其后的代碼是invalidate();因?yàn)槠鋬?nèi)部使用的是mScroller.startScroll,所以別忘了需要invalidate()以及結(jié)合computeScroll方法一起。

第三個(gè)View,我們?cè)趏nEdgeDragStarted回調(diào)方法中,主動(dòng)通過captureChildView對(duì)其進(jìn)行捕獲,該方法可以繞過tryCaptureView,所以我們的tryCaptureView雖然并為返回true,但卻不影響。注意如果需要使用邊界檢測(cè)需要添加上mDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);。

到此,我們已經(jīng)介紹了Callback中常用的回調(diào)方法了,當(dāng)然還有一些方法沒有介紹,接下來(lái)我們修改下我們的布局文件,我們把我們的TextView全部加上clickable=true,意思就是子View可以消耗事件。再次運(yùn)行,你會(huì)發(fā)現(xiàn)本來(lái)可以拖動(dòng)的View不動(dòng)了,(如果有拿Button測(cè)試的兄弟應(yīng)該已經(jīng)發(fā)現(xiàn)這個(gè)問題了,我希望你看到這了,而不是已經(jīng)提問了,哈~)。

原因是什么呢?主要是因?yàn)?,如果子View不消耗事件,那么整個(gè)手勢(shì)(DOWN-MOVE*-UP)都是直接進(jìn)入onTouchEvent,在onTouchEvent的DOWN的時(shí)候就確定了captureView。如果消耗事件,那么就會(huì)先走onInterceptTouchEvent方法,判斷是否可以捕獲,而在判斷的過程中會(huì)去判斷另外兩個(gè)回調(diào)的方法:getViewHorizontalDragRange和getViewVerticalDragRange,只有這兩個(gè)方法返回大于0的值才能正常的捕獲。

所以,如果你用Button測(cè)試,或者給TextView添加了clickable = true ,都記得重寫下面這兩個(gè)方法:

@Override
public int getViewHorizontalDragRange(View child)
{
  return getMeasuredWidth()-child.getMeasuredWidth();
}

@Override
public int getViewVerticalDragRange(View child)
{
  return getMeasuredHeight()-child.getMeasuredHeight();
}

方法的返回值應(yīng)當(dāng)是該childView橫向或者縱向的移動(dòng)的范圍,當(dāng)前如果只需要一個(gè)方向移動(dòng),可以只復(fù)寫一個(gè)。

到此,我們列一下所有的Callback方法,看看還有哪些沒用過的:

onViewDragStateChanged

當(dāng)ViewDragHelper狀態(tài)發(fā)生變化時(shí)回調(diào)(IDLE,DRAGGING,SETTING[自動(dòng)滾動(dòng)時(shí)]):
onViewPositionChanged

當(dāng)captureview的位置發(fā)生改變時(shí)回調(diào):
onViewCaptured

當(dāng)captureview被捕獲時(shí)回調(diào):
onViewReleased 已用

onEdgeTouched

當(dāng)觸摸到邊界時(shí)回調(diào):
onEdgeLock

true的時(shí)候會(huì)鎖住當(dāng)前的邊界,false則unLock。
onEdgeDragStarted 已用

getOrderedChildIndex

改變同一個(gè)坐標(biāo)(x,y)去尋找captureView位置的方法。(具體在:findTopChildUnder方法中)
getViewHorizontalDragRange 已用

getViewVerticalDragRange 已用
tryCaptureView 已用
clampViewPositionHorizontal 已用
clampViewPositionVertical 已用
ok,至此所有的回調(diào)方法都有了一定的認(rèn)識(shí)。

總結(jié)下,方法的大致的回調(diào)順序:

shouldInterceptTouchEvent:

DOWN:
 getOrderedChildIndex(findTopChildUnder)
 ->onEdgeTouched

MOVE:
 getOrderedChildIndex(findTopChildUnder)
 ->getViewHorizontalDragRange & 
  getViewVerticalDragRange(checkTouchSlop)(MOVE中可能不止一次)
 ->clampViewPositionHorizontal&
  clampViewPositionVertical
 ->onEdgeDragStarted
 ->tryCaptureView
 ->onViewCaptured
 ->onViewDragStateChanged

processTouchEvent:

DOWN:
 getOrderedChildIndex(findTopChildUnder)
 ->tryCaptureView
 ->onViewCaptured
 ->onViewDragStateChanged
 ->onEdgeTouched
MOVE:
 ->STATE==DRAGGING:dragTo
 ->STATE!=DRAGGING:
  onEdgeDragStarted
  ->getOrderedChildIndex(findTopChildUnder)
  ->getViewHorizontalDragRange&
   getViewVerticalDragRange(checkTouchSlop)
  ->tryCaptureView
  ->onViewCaptured
  ->onViewDragStateChanged

ok,上述是正常情況下大致的流程,當(dāng)然整個(gè)過程可能會(huì)存在很多判斷不成立的情況。

從上面也可以解釋,我們?cè)谥癟extView(clickable=false)的情況下,沒有編寫getViewHorizontalDragRange方法時(shí),是可以移動(dòng)的。因?yàn)橹苯舆M(jìn)入processTouchEvent的DOWN,然后就onViewCaptured、onViewDragStateChanged(進(jìn)入DRAGGING狀態(tài)),接下來(lái)MOVE就直接dragTo了。

而當(dāng)子View消耗事件的時(shí)候,就需要走shouldInterceptTouchEvent,MOVE的時(shí)候經(jīng)過一系列的判斷(getViewHorizontalDragRange,clampViewPositionVertical等),才能夠去tryCaptureView。

ok,到此ViewDragHelper的入門用法我們就介紹結(jié)束了,下一篇,我們將使用ViewDragHelper去自己實(shí)現(xiàn)一個(gè)DrawerLayout。
有興趣的也可以根據(jù)本文,以及DrawerLayout的源碼去實(shí)現(xiàn)了~

相關(guān)文章

  • android二級(jí)listview列表實(shí)現(xiàn)代碼

    android二級(jí)listview列表實(shí)現(xiàn)代碼

    今天來(lái)實(shí)現(xiàn)以下大眾點(diǎn)評(píng)客戶端的橫向listview二級(jí)列表,感興趣的朋友可以研究下
    2013-01-01
  • Android RecyclerView實(shí)現(xiàn)下拉刷新和上拉加載

    Android RecyclerView實(shí)現(xiàn)下拉刷新和上拉加載

    這篇文章主要介紹了Android RecyclerView實(shí)現(xiàn)下拉刷新和上拉加載的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-05-05
  • Android編程實(shí)現(xiàn)保存圖片到系統(tǒng)圖庫(kù)的方法示例

    Android編程實(shí)現(xiàn)保存圖片到系統(tǒng)圖庫(kù)的方法示例

    這篇文章主要介紹了Android編程實(shí)現(xiàn)保存圖片到系統(tǒng)圖庫(kù)的方法,結(jié)合實(shí)例形式分析了Android保存圖片到系統(tǒng)圖庫(kù)的常見操作方法、注意事項(xiàng)與相關(guān)問題解決技巧,需要的朋友可以參考下
    2017-08-08
  • Android資源文件與層次式導(dǎo)航超詳細(xì)講解

    Android資源文件與層次式導(dǎo)航超詳細(xì)講解

    這篇文章主要介紹了Android資源文件與層次式導(dǎo)航,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2022-12-12
  • Android neon 優(yōu)化實(shí)踐示例

    Android neon 優(yōu)化實(shí)踐示例

    這篇文章主要為大家介紹了Android neon 優(yōu)化實(shí)踐示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • Android 通過jni返回Mat數(shù)據(jù)類型方法

    Android 通過jni返回Mat數(shù)據(jù)類型方法

    今天小編就為大家分享一篇Android 通過jni返回Mat數(shù)據(jù)類型方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧
    2018-08-08
  • 一篇文章弄懂Android自定義viewgroup的相關(guān)難點(diǎn)

    一篇文章弄懂Android自定義viewgroup的相關(guān)難點(diǎn)

    這篇文章主要給大家介紹了關(guān)于如何通過一篇文章弄懂Android中自定義viewgroup的一些相關(guān)難點(diǎn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-06-06
  • Android自定義viewGroup實(shí)現(xiàn)點(diǎn)擊動(dòng)畫效果

    Android自定義viewGroup實(shí)現(xiàn)點(diǎn)擊動(dòng)畫效果

    這篇文章主要介紹了Android自定義viewGroup實(shí)現(xiàn)點(diǎn)擊動(dòng)畫效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • Android常用命令集錦(圖文并茂適應(yīng)于初學(xué)者)

    Android常用命令集錦(圖文并茂適應(yīng)于初學(xué)者)

    大家好,今天我們要講的是android開發(fā)中,比較常用的名令集錦, 在我們開發(fā)中難免用到Android命令,有些確實(shí)命令確實(shí)很有用處,這也是我為什么總結(jié)這篇文章的原因了,希望對(duì)大家有所幫助
    2013-01-01
  • Android實(shí)現(xiàn)qq列表式的分類懸浮提示

    Android實(shí)現(xiàn)qq列表式的分類懸浮提示

    工作中遇到了一個(gè)需求,讓應(yīng)用中的一個(gè)列表按照分類顯示,并且能提示當(dāng)前是在哪個(gè)分類,度娘了一番,參考了前輩們的博客后實(shí)現(xiàn)了,現(xiàn)在分享給大家,有需要的可以參考借鑒。
    2016-09-09

最新評(píng)論