Android View

1.View坐标

(1)View的坐标参数主要有哪些?分别有什么要注意的要点?

几个主要的坐标参数:

1)Left,Right,top,Bottom;他们表示的并非是距离屏幕左上方的绝对值,而是表示view和他的父控件的相对坐标值,并且代表View的初始坐标,在绘制完毕后就不会再改变。

2)X和Y表示的是View左上角相对于父控件的坐标值,即实时相对坐标。

3)TranslationX,translationY这两个值,默认都是0,表示的是相对于父控件的左上角的偏移量。

他们之间的换算关系是:

x=left+translationX;

y=top+translationY;

width=right-left;

height=bottom-top;

left=getLeft();

(2)View中的几个重要方法

1)omMeasure(widthMeasureSpec,heightMeasureSpec)

onMeasure过程决定了View的宽高,Measure完成之后可以通过getMeasureWidth和getMeasureHeight方法获取到View的测量后的宽高,在几乎所有的情况下都会等于最终View的宽高

onMeasure()方法接收两个参数,widthMeasureSpec和heightMeasureSpec,这两个值分别用于确定视图的宽度和高度的规格和大小。

2)onLayout(boolean changed,int left,int top,int right,int bottom)

layout过程决定了View的四个顶点的坐标和实际的View的宽高,完成以后可以通过getTop,getBottom,getLeft,getRight来获取View的四个顶点的位置,并通过getWidth和getHeight获取View的最终宽高

3)onDraw(Canvas canvas)

draw过程则决定了View的显示,完成draw后view会显示在屏幕上

  • 绘制背景(background.draw(Canvas))
  • 绘制自己
    • protected void onDraw(Canvas canvas)
    • onDraw绘制自己,新建一个paint在canvas上绘制自己的图形
  • 绘制children(dispatchDraw)
    • dispatchDraw会表里调用所有子元素的draw方法
  • 绘制装饰(onDrawScrollBars)

4)isEnabled() 当前视图是否可用。

可以调用setEnable()方法来改变视图的可用状态,传入true表示可用,传入false表示不可用。

它们之间最大的区别在于,不可用的视图是无法响应onTouch事件的。

5)isFocused() 当前视图是否获得焦点

通常情况下有两种方法可以让视图获得焦点,即通过键盘的上下左右键切换视图,以及调用requestFocus()方法。

而现在的Android手机几乎都没有键盘了,因此基本上只可以使用requestFocus()这个办法来让视图获得焦点了。

而requestFocus()方法也不能保证一定可以让视图获得焦点,它会有一个布尔值的返回值,如果返回true说明获得焦点成功,返回false说明获得焦点失败。一般只有视图在focusable和focusable in touch mode同时成立的情况下才能成功获取焦点,比如说EditText。

6)offsetTopAndBottom(int offset)及 offsetLeftAndRight(int offset)

offsetTopAndBottom直接改变的是top, bottom, 相当于在parent中上下平移View的位置;

offsetLeftAndRight直接改变的是left, right, 相当于在parent中左右平移View的位置;

View的边界直接发生了变化,又因为View和他的子View的相对位置没变,所以他的子View的边界也跟着变化了。

(3)当获取View的宽高为0时怎么处理?

如果我们要获取一个view的位置坐标,可以直接子findviewbyid之后获取,也可以在view的点击事件中获取,当然也可以在其他地方获取。如果我们直接在findviewbyid之后获取有时候就会获取失败,获取到的值是0;

分析原因可能有以下三种情况:

 1)view的宽高本身就是0;

2)View的visibility属性为gone;

3)视图还未绘制完成,当然未绘制完成也表现在不同的方面,比如,activity所代表的界面还没显示出来没有添加到WindowPhone的DecorView上;要获取的view没有被添加到DecorView上。

我们主要要说的是第三种情况,可以怎么解决呢?

1)在View的事件回调里获取;这时候该view已经被显示即被添加到DecorView上 如点击事件 触摸事件 焦点事件等

2)在activity被显示出来时即添加到了DecorView上时获取宽和高如 onWindowFocusChanged() 回调方法

3)在onResume方法中通过post(Runnable)

4)在onCreate()或onResume()等方法中需要获取宽高时使用getViewTreeObserver().addOnGlobalLayoutListener()

来为view添加回调在回调里获得宽度或者高度获取完后让view删除该回调

5)view.measure(int widthMeasureSpec,int heightMeasureSpec)

手动对view进行measure来得到view的宽高(该方式比较特殊,需要考虑好传入的spec)

2View滑动

1、让view滑动总共有几种方式,分别要注意什么?都适用于那些场景?

1)通过view本身提供的scrollTo和scrollBy来滑动;但是只能滑动view的内容 不可以滑动view本身。比如textview 调用这2个方法 滑动的就是显示出来的字的内容。

 

2)利用补间动画将view平移

补间动画平移view,是对view的影像的操作,它不能真正改变view的位置参数。

3)利用属性动画平移view

ObjectAnimator
        .ofFloat(view,"translationX",0,500)
        .setDuration(2000)
        .start();  
        
 ObjectAnimator
    .ofFloat(view,"translationY",0,500)
    .setDuration(2000)
    .start();

属性动画平移view需要兼容3.0以下版本

4)通过改变view的LayoutParams使得view重新布局从而实现滑动

ViewGroup.MarginLayoutParams params 
        = view.getLayoutParams();
params.leftMargin += 500;

(2)三种滑动方式的比较

  • scrollTo和scrollBy这种方式使用简单操作方便,但是它只能滑动view的内容,并不能直接滑动view本身

  • 补间动画滑动,是对view影像的操作,它同样不能真正改变view的位置参数

  • 属性动画滑动可以改变view的位置参数,但是需要兼容3.0以下版本

  • LayoutParams这种方式,使用起来稍有麻烦,使用于有交互的view

(3)使用动画(非属性动画)来实现view的滑动 有什么后果?

答:实际上view动画 是对view的表面ui 也就是给用户呈现出的视觉效果 来做的移动,动画本身并不能移动view的真正位置,属性动画除外。

动画播放结束以后,view最终还是会回到自己的位置的。当然了你可以设置FillAfter 属性 来让动画播放结束以后 view表象停留在变化以后的位置。

所以这会带来一个很严重的后果。比如你的button在屏幕的左边,你现在用个动画 并且设置了fillafter属性让他去了右边。

你会发现 点击右边的button 没有click事件触发,但是点击左边的 却可以触发,原因就是右边的button 只是view的表象,真正的button 还在左边没有动过。

你一定要这么做的话 可以提前在右边button移动后的位置放一个新的button,当你动画执行结束以后 把右边的visible,左边的让他gone就可以了。

(4)view的滑动渐变效果有哪些方法?

三种,
第一种是Scroller 也是使用最多的。
第二种就是动画,动画我就不多说了,不属于本文范畴。
第三种也是我们经常使用的就是用handler ,每隔一个时间间隔 来更新view的状态。

(5)onTouchEvent和GestureDetector 在什么时候用哪个比较好?

只有滑动需求的时候 就用前者,如果有双击等这种行为的时候 就用后者。

 

GestureDetector解析

当用户触摸屏幕的时候,会产生许多手势,例如down,up,scroll,filing等等。

一般情况下,我们知道View类有个View.OnTouchListener内部接口,通过重写他的onTouch(View v, MotionEvent event)方法,我们可以处理一些touch事件,但是这个方法太过简单,如果需要处理一些复杂的手势,用这个接口就会很麻烦(因为我们要自己根据用户触摸的轨迹去判断是什么手势)。

Android sdk给我们提供了GestureDetector(Gesture:手势Detector:识别)类,通过这个类我们可以识别很多的手势,主要是通过他的onTouchEvent(event)方法完成了不同手势的识别。虽然他能识别手势,但是不同的手势要怎么处理,应该是提供给程序员实现的。

GestureDetector这个类对外提供了两个接口:OnGestureListenerOnDoubleTapListener,还有一个内部类SimpleOnGestureListener

GestureDetector.OnDoubleTapListener接口:用来通知DoubleTap事件,类似于鼠标的双击事件。

1,onDoubleTap(MotionEvent e):在双击的第二下Touch down时触发 

2,onDoubleTapEvent(MotionEvent e):通知DoubleTap手势中的事件,包含downupmove事件(这里指的是在双击之间发生的事件,例如在同一个地方双击会产生DoubleTap手势,而在DoubleTap手势里面还会发生downup事件,这两个事件由该函数通知);双击的第二下Touch downup都会触发,可用e.getAction()区分。 

3onSingleTapConfirmed(MotionEvent e):用来判定该次点击是SingleTap而不是DoubleTap,如果连续点击两次就是DoubleTap手势,如果只点击一次,系统等待一段时间后没有收到第二次点击则判定该次点击为SingleTap而不是DoubleTap,然后触发SingleTapConfirmed事件。这个方法不同于onSingleTapUp,他是在GestureDetector确信用户在第一次触摸屏幕后,没有紧跟着第二次触摸屏幕,也就是不是“双击”的时候触发  

GestureDetector.OnGestureListener接口:用来通知普通的手势事件,该接口有如下六个回调函数:
     1.  
 onDown(MotionEvent e)down事件;
     2.  
 onSingleTapUp(MotionEvent e):一次点击up事件;在touch down后又没有滑动

onScroll),又没有长按(onLongPress),然后Touchup时触发。

 点击一下非常快的(不滑动)Touchup

onDown->onSingleTapUp->onSingleTapConfirmed 
          
点击一下稍微慢点的(不滑动)Touchup

onDown->onShowPress->onSingleTapUp->onSingleTapConfirmed
     3.  
 onShowPress(MotionEvent e)down事件发生而move或则up还没发生前触发该   

事件;Touch了还没有滑动时触发(与onDownonLongPress)比较onDown只要Touch down一定立刻触发。而Touchdown后过一会没有滑动先触发onShowPress再是onLongPress。所以Touchdown后一直不滑动

按照onDown->onShowPress->onLongPress这个顺序触发。 
     4.  
 onLongPress(MotionEvent e):长按事件;Touch了不移动一直Touch down时触发 
     5.  
 onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY):滑动手

势事件;Touch了滑动一点距离后,在ACTION_UP时才会触发       

参数:e1 第1个ACTION_DOWN MotionEvent 并且只有一个;e2 最后一个ACTION_MOVE MotionEvent ;velocityX X轴上的移动速度,像素/秒 ;velocityY Y轴上的移动速度,像素/秒.触发条件:X轴的坐标位移大于FLING_MIN_DISTANCE,且移动速度大于FLING_MIN_VELOCITY个像素/秒

6.   onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY):在屏幕上

拖动事件。无论是用手拖动view,或者是以抛的动作滚动,都会多次触发,这个方法在ACTION_MOVE动作发生时就会触发

        抛:手指触动屏幕后,稍微滑动后立即松开

onDown-----onScroll----onScroll----onScroll----………----->onFling

        拖动

onDown------》onScroll----》onScroll------》onFiling

SimpleOnGestureListenerGestureDetector提供给我们的一个更方便的响应不同手势的类,这个类实现了上述两个接口(但是所有的方法体都是空的),该类是static class,也就是说它实际上是一个外部类。程序员可以在外部继承这个类,重写里面的手势处理方法。

方法步骤

第一种示例:

1,通过GestureDetector的构造方法可以将SimpleOnGestureListener对象传递进去,这样GestureDetector能处理不同的手势了。

public GestureDetector

(Context context, GestureDetector.OnGestureListener listener)

2,在OnTouchListener的onTouch方法中

private OnTouchListener gestureTouchListener = new OnTouchListener() {
               public boolean onTouch(View v, MotionEvent event) {
             return gDetector.onTouchEvent(event);
        }
    };

第二种示例:

使用方法

private GestureDetector mGestureDetector;

mGestureListener = new BookOnGestureListener();

构造出来mGestureDetector = new GestureDetector(mGestureListener);

class BookOnGestureListener implements OnGestureListener {

同时要public boolean onTouchEvent(MotionEvent event) {
                        mGestureListener.onTouchEvent(event);
             }

 

第三种示例代码

代码:

01.private GestureDetector mGestureDetector;
02.@Override
03.public void onCreate(Bundle savedInstanceState) {
04.    super.onCreate(savedInstanceState);
05.    mGestureDetector = new GestureDetector(this, new LearnGestureListener());
06.}
07.@Override
08.public boolean onTouchEvent(MotionEvent event) {
09.    if (mGestureDetector.onTouchEvent(event))
10.        return true;
11.    else
12.        return false;
13.}
14.class LearnGestureListener extends GestureDetector.SimpleOnGestureListener{
15.    @Override
16.    public boolean onSingleTapUp(MotionEvent ev) {
17.        Log.d("onSingleTapUp",ev.toString());
18.        return true;
19.    }
20.    @Override
21.    public void onShowPress(MotionEvent ev) {
22.        Log.d("onShowPress",ev.toString());
23.    }
24.    @Override
25.    public void onLongPress(MotionEvent ev) {
26.        Log.d("onLongPress",ev.toString());
27.    }
28.    @Override
29.    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
30.        Log.d("onScroll",e1.toString());
31.        return true;
32.    }
33.    @Override
34.    public boolean onDown(MotionEvent ev) {
35.        Log.d("onDownd",ev.toString());
36.        return true;
37.    }
38.    @Override
39.    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
40.        Log.d("d",e1.toString());
41.        Log.d("e2",e2.toString());
42.        return true;
43.    }
44.}

1,在当前类中创建一个GestureDetector实例。

private GestureDetector mGestureDetector;

2,创建一个Listener来实时监听当前面板操作手势。
class LearnGestureListener extends GestureDetector.SimpleOnGestureListener                                                              

3,在初始化时,将Listener实例关联当前的GestureDetector实例。
mGestureDetector = new GestureDetector(this, new LearnGestureListener());

4,利用onTouchEvent方法作为入口检测,通过传递MotionEvent参数来监听操作手势。
1.mGestureDetector.onTouchEvent(event)

第四种示例代码

private GestureDetector mGestureDetector;

@Override

public void onCreate(Bundle savedInstanceState) {

  super.onCreate(savedInstanceState);

  mGestureDetector = new GestureDetector(this, new MyGestureListener());

}

@Override

public boolean onTouchEvent(MotionEvent event) {

 return mGestureDetector.onTouchEvent(event);

}

class MyGestureListener extends GestureDetector.SimpleOnGestureListener{

  @Override

  public boolean onSingleTapUp(MotionEvent ev) {

    Log.d("onSingleTapUp",ev.toString());

    return true;

  }

  @Override

  public void onShowPress(MotionEvent ev) {

    Log.d("onShowPress",ev.toString());

  }

  @Override

  public void onLongPress(MotionEvent ev) {

    Log.d("onLongPress",ev.toString());

  }

…

}

基本的内容就是创建一个GestureDetector的对象,传入listener对象,在自己接收到的onTouchEvent中将event传给GestureDetector进行分析,listener会回调给我们相应的动作。其中GestureDetector.SimpleOnGestureListener(Framework帮我们简化了)是实现了上面提到的OnGestureListener和OnDoubleTapListener两个接口的类,我们只需要继承它并重写其中我们关心的回调即可。

最后,再提一下双击和三击的识别过程:在第一次单击down时,给Hanlder发送了一个延时300ms的消息,如果300ms里,发生了第二次单击的down事件,那么,就认为是双击事件了,并移除之前发送的延时消息。如果300ms后仍没有第二次的down消息,那么就判定为SingleTapConfirmed事件(当然,此时用户的手指应已完成第一次点击的up过程)。三击的判定和双击的判定类似,只是多了一次发送延时消息的过程。

posted @ 2017-03-02 15:45  ChHM  阅读(168)  评论(0编辑  收藏  举报