参考书籍:《Android开发艺术探索》 任玉刚
如有错漏,请批评指出!
View基础知识
View类
View类是Android中所有控件的基类,包括ViewGroup(控件组);这也就意味着,View本身可以是单个控件,也可以是包含多个控件的一组控件。
View的位置参数
View的位置主要由四个顶点决定,分别对应于四个属性:top、left、right、bottom。从Android3.0开始,增加了几个额外参数:x、y、translationx、translationY。这些参数都是相对于父容器的。
x:View发生平移后的左上角横坐标
y:View发生平移后的左上角纵坐标
translationX:View的横向偏移量(初始为0)
translationY:View的纵向偏移量(初始为0)
MotionEvent
手指触摸屏幕后的典型事件类型有:
- ACTION_DOWN——手指接触屏幕瞬间;
- ACTION_MOVE——手指在屏幕上滑动;
- ACTION_UP——手指离开屏幕瞬间;
通过MotionEvent对象可以得到点击事件发生的x、y坐标:
- getX()——获取触摸点相对于当前View左上角的横坐标;
- getY()——获取触摸点相对于当前View左上角的纵坐标;
- getRawX()——获取触摸点相对于手机屏幕左上角的横坐标;
- getRawY()——获取触摸点相对于手机屏幕左上角的纵坐标;
TouchSlop
TouchSlop是系统所能识别出的被认为是滑动的最小距离。
- 这是一个常量,和设备有关,在不同设备上这个值可能不同;
- 当手指在屏幕上滑动的距离小于这个值,系统就不会认为这是滑动操作;
- 获取方法:
ViewConfiguration.get(getContext()).getScaledTouchSlop();
这个常量的意义在于,当我们做滑动处理的时候,可以用它来做一些过滤。这样可以改善用户的使用体验。
VelocityTracker
速度追踪,用于追踪手指在滑动过程中的速度,包括水平和竖直方向的速度。下面是使用示例:
@Override
public boolean onTouchEvent(MotionEvent event) {
//创建VelocityTracker对象,并将触摸界面的滑动事件
//加入到VelocityTracker当中。
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
//指定时间间隔为1s,计算速度
velocityTracker.computeCurrentVelocity(1000);
//获取水平速度和竖直速度(像素/s)
int xVelocity = (int)velocityTracker.getXVelocity();
int yVelocity = (int)velocityTracker.getYVelocity();
//销毁对象,回收内存
velocityTracker.clear();
velocityTracker.recycle();
return super.onTouchEvent(event);
}
速度 = (终点位置 - 起点位置)/时间段(可以为负值)
GestureDetector
手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为。下面是使用示例:
public class MainActivity extends AppCompatActivity implements
GestureDetector.OnGestureListener,GestureDetector.OnDoubleTapListener{
GestureDetector mGestureDetector;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//首先,创建GestureDetector对象并根据需求实现OnGestureListener接口
//和OnDoubleTapListener接口
mGestureDetector = new GestureDetector(this,this);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//将目标View中的触摸事件交给GestureDetector处理(这里是MainActivity中的触摸事件)
return mGestureDetector.onTouchEvent(event);
}
}
做完上面的事情之后,即可以选择性的实现OnGestureListener和OnDoubleTapListener中的方法了,这两个接口中的常用方法如下:
方法名 | 描述 | 所属接口 |
---|---|---|
onDown | 手指轻轻触摸屏幕瞬间,由一个ACTION_DOWN触发 | OnGestureListener |
onSingleTapUp | 手指(轻轻触摸屏幕后)松开,伴随一个ACTION_UP而触发,表示单击行为 | OnGestureListener |
onScroll | 手指按下屏幕并拖动,由一个ACTION_DOWN,多个ACTION_MOVE触发,表示拖动行为 | OnGestureListener |
onFling | 用户按下触摸屏,快速滑动后松开,由一个ACTION_DOWN、多个ACTION_MOVE和一个ACTION_UP触发,表示快速滑动行为 | OnGestureListener |
onLoongPress | 用户长久的按住屏幕不放,表示长按行为 | OnGestureListener |
onDoubleTap | 双击,由连续2次单击组成 | OnDoubleTapListener |
说明:
- 在实际开发中,可以不使用GestureDetector,完全可以自己在View中onTouchEvent方法中实现所需的监听。
- 建议:如果只是监听滑动相关,就在onTouchEvent中实现;如果要监听双击这种行为,就使用GestureDetector。
View的滑动
滑动在Android开发中具有很重要的作用,很多绚丽的滑动效果其实都是由基本的滑动操作和特效组成。书中讲了三种实现View滑动的方式:第一种是使用scrollTo / scrollBy 方法;第二种是通过动画给View施加平移效果来实现滑动;第三种是通过改变View的LayoutParams使得View重新布局从而实现滑动。这里我还要补充一种,使用 layout 方法改变View的位置参数从而实现滑动。
-
使用 scrollto / scrollBy 方法
先来看看这两个方法的源码:/** * Set the scrolled position of your view. This will cause a call to * {@link #onScrollChanged(int, int, int, int)} and the view will be * invalidated. * @param x the x position to scroll to * @param y the y position to scroll to */ public void scrollTo(int x, int y) { if (mScrollX != x || mScrollY != y) { int oldX = mScrollX; int oldY = mScrollY; mScrollX = x; mScrollY = y; invalidateParentCaches(); onScrollChanged(mScrollX, mScrollY, oldX, oldY); if (!awakenScrollBars()) { postInvalidateOnAnimation(); } } } /** * Move the scrolled position of your view. This will cause a call to * {@link #onScrollChanged(int, int, int, int)} and the view will be * invalidated. * @param x the amount of pixels to scroll by horizontally * @param y the amount of pixels to scroll by vertically */ public void scrollBy(int x, int y) { scrollTo(mScrollX + x, mScrollY + y); }
首先我们先了解源码中的 mScrollX 和 mScrollY 这两个值是什么:
从mScrollX 和 mScrollY的描述来看,这两个方法只能改变View内容的位置,并不能改变View的位置,也就是说,调用View的 scrollTo()和scroollBy() 方法不会改变View的位置参数(left、top、right、buttom、x、y、translationX、translationY),如果当前View还有子View时,子View也仅仅是内容发生平移,自身的位置参数不变。关于什么是View以及它的内容可以看下图:
mScrollX 是View左边缘和View内容左边缘在水平方向上的距离
mScrollY 是View上边缘和View内容上边缘在竖直方向上的距离
说明:mScrollX 和 mScrollY的单位为像素,当View左边缘在View内容右边时,mScrollX 为正值,反之为负值;当View上边缘在View内容下面时,mScrollY为正值,反之为负值。具体来说,当我们想要让View内容右移或下移时,我们传入scrollBy()或scrollTo()方法的参数应该为负值。
从源码来看,scrollBy() 方法实际上也是调用了scrollTo() 方法,因此,这两个方法都是改变 mScrollX 和 mScrollY 的值,不过scrollTo(int x, int y)方法是直接将传入的参数赋值给mScrollX 和 mScrollY,即将View的内容移动到指定的坐标位置;而scrollBy(int x, int y)方法则是将View的内容从当前位置向上或下平移x个像素,向左或右平移y个像素。下面看看实现过程和效果:
<LinearLayout android:id="@+id/ll_left" android:layout_width="match_parent" android:layout_height="match_parent"> <View android:id="@+id/view_left" android:layout_width="100dp" android:layout_height="100dp" android:background="@color/colorRed"/> </LinearLayout>
效果图如下:public void move(View v) { switch (v.getId()){ case R.id.but_show: ll_left.scrollBy(-100,-100); break; case R.id.but_hide: ll_left.scrollBy(100,100); break; default: break; } }
使用动画实现
关于动画的内容后面有单独的章节讲解,这里先略过。-
改变布局参数
改变布局参数,也就是改变LayoutParams。比如我们想把一个Buttom向右平移100px,我们只需要将这个Buttom的Layoutparams里的marginLeft参数的值增加100px即可。当然,这只是为了达到让View平移目的的一种实现方式,类似的还可以在这个Buttom左边放一个width为0的空View,当我们需要平移时,就改变空View的width,从而将Button挤到右边。具体如何实现呢?
看看效果:MarginLayoutParams params = (MarginLayoutParams)view_left.getLayoutParams(); //改变View的width params.width += 100; //改变View的 MarginLeft params.leftMargin += 100; view_left.setLayoutParams(params);
-
使用 layout(int l, int t, int r, int b) 方法实现
这种方法和第一种方式相比,它是实实在在改变了View的left、top、right、buttom这几个位置参数的。我们直接来看代码:@OnClick({R.id.but_show, R.id.but_hide}) public void move(View v) { int mLeft, mRight, mTop, mButtom; switch (v.getId()){ case R.id.but_show: mLeft = view_left.getLeft() + 100; mTop = view_left.getTop() + 100; mRight = view_left.getRight() + 100; mButtom = view_left.getBottom() + 100; view_left.layout(mLeft, mTop, mRight, mButtom); break; case R.id.but_hide: mLeft = view_left.getLeft() - 100; mTop = view_left.getTop() - 100; mRight = view_left.getRight() - 100; mButtom = view_left.getBottom() - 100; view_left.layout(mLeft, mTop, mRight, mButtom); break; default: break; } }
效果和第一种看起来相同,只不过第一种是View的内容发生平移,这里是View自身发生平移。
滑动方式对比
- ScrollTo / ScrollBy:这两个方法是View提供的原生方法,它改变的是View内容的位置,不会改变View自身的位置参数,因此,不会影响View以及其内部控件的点击事件位置。
- 动画:使用动画可以实现很多炫酷的动画效果,但是它不适用于有交互的View。
- 改变布局参数:这种方式操作略微复杂,比较适合有交互的View。
- layout:这种方式实际上通过改变位置参数,重绘View,会改变View的位置参数。
上一篇:Android学习笔记(二) | Activity的启动模式
下一篇:Android学习笔记(四)| Android 控件架构与自定义控件