Android ViewDragHelper使用总结
来源:互联网 发布:mac 安装wget命令 编辑:程序博客网 时间:2024/04/30 10:50
Android ViewDragHelper使用总结
Android中的ViewDragHelper类(2013年出来的),使用的人算是比较少的把,但是它也是有一些特殊效果能实现,一般应用场合是拖动效果的设计。
要对它进行要对它的一些属性和方法进行了解(这是它的在线版英文API文档):
http://www.android-doc.com/reference/android/support/v4/widget/ViewDragHelper.html
本文程序设计的效果
只能水平方向移动效果1:
只能垂直方向移动效果2:
任意方向移动效果3:
复杂应用效果:
上面点击ListView中的某一个item后弹出一个可以拖拽的视图!
一.ViewDragHelper的基础知识
ViewDragHelper是一个类,它是在用在一个容器的自定义类中,比如六大布局的类中或者ViewGroup类中。
(一)ViewDragHelper的初始化
mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback());
上面的语句一般是在构造方法中执行,其中1.0f是敏感度参数参数越大越敏感。第一个参数为this,表示该类生成的对象,他是ViewDragHelper的拖动处理对象,必须为ViewGroup。
(二)拖动行为的处理
1.处理横向的拖动:
在DragHelperCallback中实现clampViewPositionHorizontal方法, 并且返回一个适当的数值就能实现横向拖动效果,lampViewPositionHorizontal的第二个参数是指当前拖动子view应该到达的x坐标。所以按照常理这个方法原封返回第二个参数就可以了,但为了让被拖动的view遇到边界之后就不在拖动,对返回的值做了更多的考虑。
代码如下:
@Override public int clampViewPositionHorizontal(View child, int left, int dx) { Log.e("DragLayout", "clampViewPositionHorizontal " + left + "," + dx); final int leftBound = getPaddingLeft(); final int rightBound = getWidth() - mDragView.getWidth(); final int newLeft = Math.min(Math.max(left, leftBound), rightBound); return newLeft; }
2.处理纵向的拖动
也是类似的,在DragHelperCallback中实现clampViewPositionVertical方法,
@Override public int clampViewPositionVertical(View child, int top, int dy) { final int topBound = getPaddingTop(); //padding顶部的距离 //父框体的宽度减去视图本身的宽度,得到可以移动的最大宽度 final int bottomBound = getHeight() - mDragView.getHeight(); //先取出padding顶部和距离顶部距离的最大值 //然后让这个最大值和这个视图可以移动的距离做比较,取出最小值,得到视图实际可以移动的距离 final int newTop = Math.min(Math.max(top, topBound), bottomBound); return newTop; }
这里横向处理和纵向处理,如果没有特殊要求格式基本是固定的,如果要横向和纵向都可以移动就两个方法都重写就可以了。
clampViewPositionHorizontal和clampViewPositionVertical,因为默认它返回的是0。如果不重写是不能拖动图像的。
3.tryCaotureView方法
通过DragHelperCallback的tryCaptureView方法的返回值可以决定一个parentview中哪个子view可以拖动,现在假设有两个子views (mDragView和mDragView1) ,如下实现tryCaptureView之后,则只有mDragView是可以拖动的。
//tryCaptureView方法的返回值可以决定一个parentview中哪个子view可以拖动 @Override public boolean tryCaptureView(View child, int pointerId) { return mDragView == child;//不返回true就不会被移动 //如果这里有多个View的话,返回值改变成 return child == mDragView1; //那么只有MDragView1可以被拖拽,其他View不能 }
还有很多方法,它的具体含义我也是不太懂,有兴趣的话自己看API!怪我英语不好,不能給你解释太多,但是我会基本的调用,已经很满足了!
下面是程序设计的具体代码,很多解释都在代码中自己看,理解!
二.第一个简单示例程序
这个程序,主要是针对上面的前面三个效果的设计,功能非常简单,但是一个演示了基本用法!
(一)自定义容器类的设计
package com.example.viewdraghelperdemo;import android.content.Context;import android.support.v4.view.MotionEventCompat;import android.support.v4.widget.ViewDragHelper;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.TextView;import android.widget.Toast;public class DragLayout extends LinearLayout { private ViewDragHelper mDragHelper; private View mDragView; private ImageView mImageView; private TextView mTextView; public DragLayout(Context context) { this(context,null); } public DragLayout(Context context, AttributeSet attrs) { this(context,null,0); } public DragLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); Log.e("TAG","init3"); initView(); } //这里一定要在渲染完成后才能实例化控件对象,否则返回的时空的值! @Override protected void onFinishInflate() { mImageView = (ImageView) findViewById(R.id.imageview); mTextView=(TextView) findViewById(R.id.text); mDragView=mImageView; //mDragView=mTextView; } //初始化数据 private void initView() { // setOrientation(LinearLayout.HORIZONTAL); //实例化DragHelper对象 mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback()); //设置手指点击到到左侧边缘时有onEdgeTouched方法回调 mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT); //设置手指点到到右侧边时有onEdgeTouched方法回调 // mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_RIGHT); //这里还很设置上边和底部,或EDGRE_ALL表示所有的边缘 //mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL); } //事件拦截处理,基本是固定的格式 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { final int action = MotionEventCompat.getActionMasked(ev); if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { mDragHelper.cancel(); return false; } return mDragHelper.shouldInterceptTouchEvent(ev); } //事件处理,格式也是固定的 @Override public boolean onTouchEvent(MotionEvent ev) { mDragHelper.processTouchEvent(ev); return true; } //创建DragHelper的Callback类 class DragHelperCallback extends ViewDragHelper.Callback{ //重写方法,格式也是固定, //tryCaptureView方法的返回值可以决定一个parentview中哪个子view可以拖动 @Override public boolean tryCaptureView(View child, int pointerId) { Log.e("TAG", "mDragView == child " + (mDragView == child)); return mDragView == child;//不返回true就不会被移动 //如果这里有多个View的话,返回值改变成 return child == mDragView1; //那么只有MDragView1可以被拖拽,其他View不能 } //如果要实现可以横向拖动,就要重写这个方法clampViewPositionHorizontal,否则默认返回的值为0 /*处理横向的拖动: 在DragHelperCallback中实现clampViewPositionHorizontal方法, 并且返回一个适当的数值就能实现横向拖动效果, clampViewPositionHorizontal的第二个参数是指当前拖动子view应该到达的x坐标。 所以按照常理这个方法原封返回第二个参数就可以了, 但为了让被拖动的view遇到边界之后就不在拖动,对返回的值做了更多的考虑。*/ @Override public int clampViewPositionHorizontal(View child, int left, int dx) { Log.e("DragLayout", "clampViewPositionHorizontal " + left + "," + dx); final int leftBound = getPaddingLeft(); final int rightBound = getWidth() - mDragView.getWidth(); final int newLeft = Math.min(Math.max(left, leftBound), rightBound); return newLeft; } //同样要实现纵向移动要实现这个方法 @Override public int clampViewPositionVertical(View child, int top, int dy) { final int topBound = getPaddingTop(); //padding顶部的距离 //父框体的宽度减去视图本身的宽度,得到可以移动的最大宽度 final int bottomBound = getHeight() - mDragView.getHeight(); //先取出padding顶部和距离顶部距离的最大值 //然后让这个最大值和这个视图可以移动的距离做比较,取出最小值,得到视图实际可以移动的距离 final int newTop = Math.min(Math.max(top, topBound), bottomBound); return newTop; } //重写处理视图滑动到边缘时的事件 //如果上面设置了mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_...);下面的方法才会得到回调 //没有得到回调的原因??? //这里滑动到边缘是指手指在容器的边缘滑动,而不是指把指View拖动到边缘时触发 @Override public void onEdgeTouched(int edgeFlags, int pointerId) { Log.e("TAG","滑动到左边或右边了"); super.onEdgeTouched(edgeFlags, pointerId); Toast.makeText(getContext(), "点击到左边了", Toast.LENGTH_SHORT).show(); } /* 如果你想在边缘滑动的时候根据滑动距离移动一个子view, 可以通过实现onEdgeDragStarted方法, 并在onEdgeDragStarted方法中手动指定要移动的子View*/ @Override //不太理解! public void onEdgeDragStarted(int edgeFlags, int pointerId) { Log.e("TAG","滑onEdgeDragStarted了"); //mDragHelper.captureChildView(mDragView, pointerId); } }}
(二)布局文件的设计
<RelativeLayout 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" > <com.example.viewdraghelperdemo.DragLayout android:layout_width="match_parent" android:layout_height="match_parent" > <ImageView android:id="@+id/imageview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/a" /> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="文志" android:textSize="30sp" /> </com.example.viewdraghelperdemo.DragLayout></RelativeLayout>
(三)主方法类
这里没用任何处理,只要显示出来就可以了。
package com.example.viewdraghelperdemo;import android.app.Activity;import android.os.Bundle;public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }}
上面是否在某个方向可以移动,决定于是否已经重写了
clampViewPositionHorizontal和clampViewPositionVertical!
程序运行后,单击边缘位置,可以看到:
三.复杂应用效果程序的设计
(一)自定义容器类的设计
package com.example.viewdraghelperdemo2;import android.content.Context;import android.support.v4.view.MotionEventCompat;import android.support.v4.view.ViewCompat;import android.support.v4.widget.ViewDragHelper;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;/** *定义的容器类,里面包含可拖拽的控件 * */public class MyViewDragHelper extends ViewGroup { private ViewDragHelper mDragHelper; //定义DragHelper对象 private View mHeaderView; //定义头部可拖拽的视图VIew private View mDescView; //其他的VIew private float mInitialMotionX; //初始时的移动X的距离 private float mInitialMotionY; //初始时的移动Y的距离 private int mDragRange; //可以拖拽的距离范围 private int mTop; //距离顶部的距离 private float mDragOffset; //当前拖拽的距离,这里指的是移动的一小段的距离 //三个构造方法,指向一个构造方法 public MyViewDragHelper(Context context) { this(context, null, 0); } public MyViewDragHelper(Context context, AttributeSet attrs) { this(context, attrs,0); } public MyViewDragHelper(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); //实例化ViewDragHelper对象 mDragHelper = ViewDragHelper.create(this, 1f, new DragHelperCallback()); } //容器内的控件的实例化是必须要在页面渲染完成后,否则是返回空值 @Override protected void onFinishInflate() { //实例化容器内的控件 mHeaderView = findViewById(R.id.viewHeader); mDescView = findViewById(R.id.viewDesc); } /* *判断是否完全显示页面 *如果传入0f,那么这个自定义View容器全屏显示 *如果 传入1f,那么这个自定义容器只显示可以拖拽部分View *这也是非常重要的一个封装方法,可以在外部调用,设置这个视图的显示 */ boolean smoothSlideTo(float slideOffset) { final int topBound = getPaddingTop(); int y = (int) (topBound + slideOffset * mDragRange); //smoothSlideViewTo()通过此方法可以把父布局中某一个子View移动到指定的左边, if (mDragHelper.smoothSlideViewTo(mHeaderView, mHeaderView.getLeft(), y)) { ViewCompat.postInvalidateOnAnimation(this); return true; } return false; } /** * *创建ViewDragHelper的callback类 */ private class DragHelperCallback extends ViewDragHelper.Callback { //设置可以拖动的视图对象 @Override public boolean tryCaptureView(View child, int pointerId) { return child == mHeaderView; } @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { mTop = top; mDragOffset = (float) top / mDragRange; mHeaderView.setPivotX(mHeaderView.getWidth()); mHeaderView.setPivotY(mHeaderView.getHeight()); mHeaderView.setScaleX(1 - mDragOffset / 2); mHeaderView.setScaleY(1 - mDragOffset / 2); mDescView.setAlpha(1 - mDragOffset); requestLayout(); } @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { int top = getPaddingTop(); if (yvel > 0 || (yvel == 0 && mDragOffset > 0.5f)) { top += mDragRange; } mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top); } @Override public int getViewVerticalDragRange(View child) { return mDragRange; } //设置子视图可以在垂直方向移动,返回的是可以移动的最大距离 @Override public int clampViewPositionVertical(View child, int top, int dy) { final int topBound = getPaddingTop(); final int bottomBound = getHeight() - mHeaderView.getHeight() - mHeaderView.getPaddingBottom(); final int newTop = Math.min(Math.max(top, topBound), bottomBound); return newTop; } } @Override public void computeScroll() { if (mDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } } //事件拦截方法 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { final int action = MotionEventCompat.getActionMasked(ev); if (( action != MotionEvent.ACTION_DOWN)) { mDragHelper.cancel(); return super.onInterceptTouchEvent(ev); } if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { mDragHelper.cancel(); return false; } final float x = ev.getX(); final float y = ev.getY(); boolean interceptTap = false; switch (action) { case MotionEvent.ACTION_DOWN: { mInitialMotionX = x; mInitialMotionY = y; interceptTap = mDragHelper.isViewUnder(mHeaderView, (int) x, (int) y); break; } case MotionEvent.ACTION_MOVE: { final float adx = Math.abs(x - mInitialMotionX); final float ady = Math.abs(y - mInitialMotionY); final int slop = mDragHelper.getTouchSlop(); if (ady > slop && adx > ady) { mDragHelper.cancel(); return false; } } } return mDragHelper.shouldInterceptTouchEvent(ev) || interceptTap; } //事件响应方法 @Override public boolean onTouchEvent(MotionEvent ev) { mDragHelper.processTouchEvent(ev); final int action = ev.getAction(); final float x = ev.getX(); final float y = ev.getY(); boolean isHeaderViewUnder = mDragHelper.isViewUnder(mHeaderView, (int) x, (int) y); switch (action & MotionEventCompat.ACTION_MASK) { case MotionEvent.ACTION_DOWN: { mInitialMotionX = x; mInitialMotionY = y; break; } case MotionEvent.ACTION_UP: { final float dx = x - mInitialMotionX; final float dy = y - mInitialMotionY; final int slop = mDragHelper.getTouchSlop(); if (dx * dx + dy * dy < slop * slop && isHeaderViewUnder) { if (mDragOffset == 0) { smoothSlideTo(1f); } else { smoothSlideTo(0f); } } break; } } return isHeaderViewUnder && isViewHit(mHeaderView, (int) x, (int) y) || isViewHit(mDescView, (int) x, (int) y); } //判断View是否隐藏 private boolean isViewHit(View view, int x, int y) { int[] viewLocation = new int[2]; view.getLocationOnScreen(viewLocation); int[] parentLocation = new int[2]; this.getLocationOnScreen(parentLocation); int screenX = parentLocation[0] + x; int screenY = parentLocation[1] + y; return screenX >= viewLocation[0] && screenX < viewLocation[0] + view.getWidth() && screenY >= viewLocation[1] && screenY < viewLocation[1] + view.getHeight(); } //测量布局 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureChildren(widthMeasureSpec, heightMeasureSpec); int maxWidth = MeasureSpec.getSize(widthMeasureSpec); int maxHeight = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0), resolveSizeAndState(maxHeight, heightMeasureSpec, 0)); } //设置容器里面控件的大小 @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { mDragRange = getHeight() - mHeaderView.getHeight(); mHeaderView.layout(0, mTop,r, mTop + mHeaderView.getMeasuredHeight()); mDescView.layout(0, mTop + mHeaderView.getMeasuredHeight(), r,mTop + b); } }
(二)布局文件的设计
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="match_parent" android:tag="list" /> <com.example.viewdraghelperdemo2.MyViewDragHelper android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/viewDragHelper" android:orientation="vertical" android:visibility="visible"> <TextView android:id="@+id/viewHeader" android:layout_width="match_parent" android:layout_height="128dp" android:fontFamily="sans-serif-thin" android:textSize="25sp" android:tag="text" android:gravity="center" android:text="欢迎你,可以拖拽这块区域" android:textColor="@android:color/white" android:background="#AD78CC"/> <TextView android:id="@+id/viewDesc" android:tag="desc" android:textSize="35sp" android:gravity="center" android:text="这里是显示具体内容区域,拖拽无效!" android:textColor="@android:color/white" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FF00FF"/> </com.example.viewdraghelperdemo2.MyViewDragHelper > </FrameLayout>
(三)主方法类的设计
package com.example.viewdraghelperdemo2;import android.app.Activity;import android.os.Bundle;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.ArrayAdapter;import android.widget.ListView;import android.widget.TextView;public class MainActivity extends Activity { //布局内的控件 ListView listView; TextView headerText; TextView descText; MyViewDragHelper myViewDragHelper; //创建ListView的数据源 String[] resource=new String[100]; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //实例化控件 listView=(ListView) findViewById(R.id.listView); headerText=(TextView) findViewById(R.id.viewHeader); descText=(TextView) findViewById(R.id.viewDesc); myViewDragHelper=(MyViewDragHelper) findViewById(R.id.viewDragHelper); //设置数据源 for (int i = 0; i < resource.length; i++) { resource[i]="这是第"+(i+1)+"章内容"; } //创建适配器,这里布局使用的是系统的TextView布局 ArrayAdapter adapter=new ArrayAdapter(this, android.R.layout.simple_list_item_1, resource); listView.setAdapter(adapter); //給ListView中的条目设置点击的监听对象 listView.setOnItemClickListener(listener); } //listVIew条目的监听对象 private OnItemClickListener listener=new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { //改变可拖拽文本的内容 headerText.setText(resource[arg2]+""); //改变第二个文本的内容 descText.setText(resource[arg2]+"的详细内容如下: → → → → → → → "); //完全显示自定义的View视图 myViewDragHelper.smoothSlideTo(0f); } };}
再展示一下程序效果:
到这里ViewDragHelper的介绍基本结束了,从这里可以看到两个程序,主要还是容器类的设计发挥作用,而ViewDragHelper的方法就是重重之重!
第二个程序的效果还是蛮酷的!
ViewDragHelper还有很多设计效果和场合需要大家慢慢探索!
- Android ViewDragHelper使用总结
- android-viewDragHelper总结
- android viewdraghelper使用
- Android ViewDragHelper简单使用
- Android ViewDragHelper 使用详解
- android关于ViewDragHelper的使用
- Android之ViewDragHelper的使用
- android ViewDragHelper介绍与使用
- Android ViewDragHelper及移动处理总结
- Android: ViewDragHelper
- Android ViewDragHelper
- Android 一步一步教你使用ViewDragHelper
- Android 一步一步教你使用ViewDragHelper
- 学习笔记:Android ViewDragHelper的使用
- ViewDragHelper 使用
- ViewDragHelper 使用
- ViewDragHelper使用
- ViewDragHelper 使用
- androidstudio模拟器错误emulator: ERROR: Could not initialize OpenglES emulation, use '-gpu off' to disabl
- Groovy简介和一点资源
- HDU3594-仙人掌图
- docker同步镜像报错Network timed out while trying to connect
- index_merge引发的死锁排查
- Android ViewDragHelper使用总结
- freeimage例子资料整理
- CC2640R2F之IAR下载、安装及注册
- string和char*转换
- 01
- 详解与重构hyman《Android SurfaceView实战 打造抽奖转盘》
- 02
- freeimage边缘检测
- CentOS下Mysql数据库的安装与配置