android 自定义控件学习之三 控件布局常用知识总结

1、View是什么

View是Android所有控件的基类,简单到TextView、Button,复杂到RelativeLayout,LinearLayout,其共同基类都是View。
所以,View可以理解为控件的抽象,也是一个控件。
除此之外,还有ViewGroup,字面意义上,它表示控件组,内部可以包含许多个控件。
ViewGroup也继承自View,这意味着,一个View的可以是单个控件,也可以是多个控件组成的一组控件,这就形成了View树。
下面这个图很好地体现了View的继承关系

2、View的相关参数

View的位置决定于它的四个顶点,对应View的四个属性:
Top:左上角纵坐标,通过getTop ()获得
Left:左上角横坐标,通过getLeft()获得
Right: 右下角横坐标,通过getRight ()获得
Bottom: 右下角纵坐标,通过getBottom ()获得

这些坐标都是相对于View的父容器所说的,是一种相对坐标。
下面这张图表示的是View中涉及位置参数的各个方法对应的具体含义。
最外层是手机屏幕,中间是一个ViewGroup嵌套一个View。
涉及到的其他方法请继续往下看。


此外,参数x,y表示View左上角的横纵坐标,
translationX和translationY表示View的左上角相对于父容器的偏移量。
他们都有相应的Get/Set方法
这几个参数也是相对于父容器的坐标
可以知道,这几个参数换算关系如下
x = left + translationX
y = top + translationY

利用这些参数,我们来自定义一个能随手指滑动而改变位置的View
实现如下效果:
初始位置:

手指滑动后,自定义View走到了图示位置:


代码如下:
自定义View
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class DragView extends View{  
  2.   
  3.     int lastX;  
  4.     int lastY;  
  5.   
  6.     public DragView(Context context) {  
  7.         super(context);  
  8.     }  
  9.   
  10.     public DragView(Context context, AttributeSet attrs) {  
  11.         super(context, attrs);  
  12.     }  
  13.   
  14.     @Override  
  15.     protected void onDraw(Canvas canvas) {  
  16.         super.onDraw(canvas);  
  17.     }  
  18.   
  19.     @Override  
  20.     public boolean onTouchEvent(MotionEvent event) {  
  21.   
  22.         int x = (int) event.getX();  
  23.         int y = (int) event.getY();  
  24.         Log.e("触发onTouchEvent",x+"::::::"+y);  
  25.   
  26.         switch (event.getAction()){  
  27.             case MotionEvent.ACTION_DOWN:{  
  28.                 lastX = x;  
  29.                 lastY = y;  
  30.             }  
  31.             break;  
  32.             case MotionEvent.ACTION_MOVE:{  
  33.                 int offsetX = x - lastX;  
  34.                 int offsetY = y - lastY;  
  35.                 Log.e("触发ACTION_MOVE",offsetX+"::::::"+offsetY);  
  36.                 layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);  
  37.                 Log.d("DragView",getLeft()+"______"+getTop()+"-------"+getBottom()+"-------"+getRight());  
  38.             }  
  39.             break;  
  40.         }  
  41.   
  42.         return true;  
  43.     }  
  44. }  

布局:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <RelativeLayout  
  2.     xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:tools="http://schemas.android.com/tools"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:paddingBottom="@dimen/activity_vertical_margin"  
  7.     android:paddingLeft="@dimen/activity_horizontal_margin"  
  8.     android:paddingRight="@dimen/activity_horizontal_margin"  
  9.     android:paddingTop="@dimen/activity_vertical_margin"  
  10.     tools:context="com.lian.scrolltest.MainActivity">  
  11.   
  12.     <com.lian.scrolltest.DragView  
  13.         android:layout_width="100dp"  
  14.         android:layout_height="100dp"  
  15.         android:background="#000000"  
  16.   
  17.         />  
  18. </RelativeLayout>  

MainActivity直接显示布局即可

很好地实现了如上效果


3、MotionEvent和TouchSlop

(1)MotionEvent
我们在自定义View的时候,常常需要在onTouchEvent()中定义触摸行为
典型的触摸事件类型包括:
ACTION_DOWN:手指刚刚接触屏幕
ACTION_MOVE:手指在屏幕上移动
ACTION_UP:手指从屏幕离开

通过MotionEvent对象,我们可以得到点击事件的一系列位置参数
getX(),getY():触摸事件发生的位置相对于View的坐标
getRawX(),getRawY()返回返回相对于屏幕左上角的x 和 y 坐标

(2)TouchSlop
表示系统能辨识出的认为是滑动的最小距离。
若两次滑动小于此常量,判定为不属于滑动操作
这个常量的大小和手机有关。

通过如下方式获得:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. ViewConfiguration.get(getContext()).getScaledTouchSlop();  

我们处理滑动事件时,可以用这个参数进行过滤。

4、VelocityTracker、GestureDetector、Scroller
(1)VelocityTracker
用于追踪滑动过程中的速度,包括水平和竖直方向的速度。

使用方法:
在View的onTouchEvent()中将Event托管给VelocityTracker,
采用相应API获取参数
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //获取对象  
  2. VelocityTracker velocityTracker = VelocityTracker.obtain();  
  3. //托管event  
  4. velocityTracker.addMovement(event);  
  5. //设置时间间隔,结果会表示为每1000毫秒经过多少像素,若设置为100,结果表示为没100毫秒经过多少像素  
  6. velocityTracker.computeCurrentVelocity(1000);  
  7. //获取X和Y方向上的速度  
  8. int xVelicity = (int) velocityTracker.getXVelocity();  
  9. int yVelicity = (int) velocityTracker.getYVelocity();  
比如1秒内X方向滑动了100像素,那么参数设置为1000时,结果就为100,表示1000毫秒划过100像素
参数设置为100时,结果就为10(表示每100毫秒划过10像素)

不需要使用时,对其进行回收
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. velocityTracker.clear();  
  2. velocityTracker.recycle();  

(2)GestureDetector
GestureDetector中将封装了一系列触摸行为,包括单击、滑动、长按,双击等。
使用:

在自定义View中实现onGestureDetector接口,在其中重写onSingleTapTop()-单击事件、onFiling()-快速滑动、omLongPress()-长按、onDoubleTap()-双击
等方法,定义自己的事件处理逻辑
还有,在onTouchEvent()中:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //获取对象  
  2. GestureDetector gestureDetector = new GestureDetector(this);  
  3. //解决长按屏幕后无法拖动的问题  
  4. gestureDetector.setIsLongpressEnabled(false);  
  5. //托管event  
  6. boolean consume = gestureDetector.onTouchEvent(event);  
  7. return consume;  

(3)Scroller
用于实现View的弹性滑动。
为了让View实现滑动,我们常常使用scrollTo和ScrollBy,但其过程是瞬间完成的,没有过度效果,用户体验并不好。
使用Scroller和View的computeScroll配合,可以实现有过渡效果的滑动



三、View的滑动

滑动是自定义View使用最多的效果之一,
有三种实现方式:
a、View的scrollTo/ScrollBy方法

b、使用动画为View施加平移效果

c、改变View的LayoutParams是的View重新布局实现滑动

1、使用scrollTo/ScrollBy

源码:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Set the scrolled position of your view. This will cause a call to 
  3.  * {@link #onScrollChanged(int, int, int, int)} and the view will be 
  4.  * invalidated. 
  5.  * @param x the x position to scroll to 
  6.  * @param y the y position to scroll to 
  7.  */  
  8. public void scrollTo(int x, int y) {  
  9.     if (mScrollX != x || mScrollY != y) {  
  10.         int oldX = mScrollX;  
  11.         int oldY = mScrollY;  
  12.         mScrollX = x;  
  13.         mScrollY = y;  
  14.         invalidateParentCaches();  
  15.         onScrollChanged(mScrollX, mScrollY, oldX, oldY);  
  16.         if (!awakenScrollBars()) {  
  17.             postInvalidateOnAnimation();  
  18.         }  
  19.     }  
  20. }  
  21.   
  22. /** 
  23.  * Move the scrolled position of your view. This will cause a call to 
  24.  * {@link #onScrollChanged(int, int, int, int)} and the view will be 
  25.  * invalidated. 
  26.  * @param x the amount of pixels to scroll by horizontally 
  27.  * @param y the amount of pixels to scroll by vertically 
  28.  */  
  29. public void scrollBy(int x, int y) {  
  30.     scrollTo(mScrollX + x, mScrollY + y);  
  31. }  

scrollBy实际上也调用scrollTo方法,实现基于当前位置的滑动,scrollTo实现基于所传递参数的绝对滑动
mScrollX和mScrollY可以动过get方法得到。
mScrollX的值总是等于View的左边缘到View的内容左边缘水平方向的距离,
mScrollY的值总是等于View的上边缘和View的内容上边缘竖直方向的距离。

需要注意的是,scrollBy和scrollTo 只能改变View的内容的位置而不能改变View在布局中的位置
在View的内容位置改变是,mScrollX和mScrollY值可正可负

2、使用动画
通过动画可以让一个View进行平移,主要是操作View的translationX和translationY属性,可以用View动画,也可以用属性动画。
以属性动画为例,
我们在上面例子的基础上添加一个按钮

点击按钮,View在1秒钟的时间内向右平移200像素


通过如下代码:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. Button button = (Button) findViewById(R.id.btn);  
  2. button.setOnClickListener(new View.OnClickListener() {  
  3.     @Override  
  4.     public void onClick(View v) {  
  5.         dragView.animate().translationX(200).setDuration(1000).start();  
  6.     }  
  7. });  

3.改变布局参数
即改变LayoutParams,
比如我们让以上自定义View向右平移100像素
只要将此View的marginLeft参数值增加100px

同样以上为例,将自定义View的宽度增加100px,向右平移100px
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. button.setOnClickListener(new View.OnClickListener() {  
  2.     @Override  
  3.     public void onClick(View v) {  
  4.         RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) dragView.getLayoutParams();  
  5.         layoutParams.width+=100;  
  6.         layoutParams.leftMargin+=100;  
  7.         dragView.requestLayout();  
  8.         //或者dragView.setLayoutParams(layoutParams);  
  9.     }  
  10. });  


点击按钮发现View向右滑动而且变胖了,但是瞬间滑动过去的,没有动画效果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值