您的位置:首页 > 移动开发 > Android开发

Android知识点汇总--第二篇

2016-08-25 15:45 302 查看

View基础

View的位置参数:top,left,right,bottom。都是相对于View的父容器来说的。

Android3.0以后增加了:x,y,translationX,translationY。x,y为View左上角坐标,translationX,translationY是View左上角相对于父容器的偏移量。

在点击事件中:

getX/getY返回当前View左上角的x和y坐标。getRawX/getRawY返回手机屏幕左上角x/y坐标。

最小滑动距离:

ViewConfiguration.get(getContext()).getScaledTouchSlop()


VelocityTracker/GestureDetector/Scroller

VelocityTracker:

在View的onTouchEvent方法中追踪当前单击事件的速度:

VelocityTracker vt = VelocityTracker.obtain();
vt.addMovement(event);
......
vt.computeCurrentVelocity(1000);//计算1000ms之内移动的像素数
int xVt = (int)vt.getXVelocity();//x方向的速度
int yVt = (int)vt.getYVelocity();
......
//在不需要使用的时候,调用clear方法重置并回收内存
vt.clear();
vt.recycler();


GestureDetector:

可以用来检测用户的单击、滑动、长按、双击行为。可以实现OnGestureListener或者onDoubleTapListener来监听双击行为。如果只监听滑动事件,直接在onTouchEvent方法中处理就可以了,如果要监听双击行为,就需要使用GestureDetector。

创建GestureDetector对象以后,在View的onTouchEvent方法中添加:

boolean consume = mGestureDetector.onTouchEvent(event);
return consume;


Scroller:

Scroller本身无法让View弹性滑动,需要和View的computeScroll方法配合使用才能共同完成。通过scrollTo和scrollBy来完成,它不断让View重绘,而每一次重绘距滑动起始时间会有一个时间间隔,通过这个时间间隔Scroller就可以得出View当前的滑动距离,再通过scrollTo来完成滑动。

scrollBy内部调用scrollTo方法,scrollTo方法实现了基于所传递参数的绝对滑动。这两个方法只改变View内容的位置而不改变View在布局中的位置。

从左—–>右滑动:mScrollX为负值。

从上—–>下滑动:mScrollY为负值。

弹性滑动

Scroller/动画/延时策略

采用动画来实现滑动动画:

ValueAnimator animator = ValueAnimator.ofInt(0,1).setDuration(1000);
animator.addUpdateListener(new AnimatorUpdateListener(){
@Override
public void onAnimationUpdate(ValueAnimator animator){
float fraction = animator.getAnimatedFraction();
mView.scrollTo(startX+(int)(deltaX*fraction),0);
}
});


延时策略:

通过发送一系列延时消息从而达到滑动效果。可以使用Handler,postDelayed,sleep来完成。

View事件分发机制

涉及到三种主要方法:dispatchTouchEvent/onInterceptTouchEvent/onTouchEvent。

public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume = false;
if(onInterceptTouchEvent(ev)){ //根ViewGroup是否拦截
consume = onTouchEvent(ev);
}else{
consume = child.dispatchTouchEvent(ev);
}
return consume;
}


优先级:OnTouchListener>onTouchEvent>OnClickListener

点击事件传递过程:Activity->Window->View。

如果子View的onTouchEvent返回false,则会调用父容器的onTouchEvent方法。

ViewGroup默认不拦截任何事件。

View没有onInterceptTouchEvent方法,一旦有点击事件给它,那么它的onTouchEvent方法会被调用。

View的onTouchEvent默认都会消耗事件,除非它的clickable属性为false。

View的enable属性不影响onTouchEvent的默认返回值。

事件传递过程总是先传递给父元素,然后通过父元素分发给子View。在子元素中可以通过requestDisallowInterceptTouchEvent方法敢于父元素的分发过程,ACTION_DOWN事件除外。对于ACTION_DOWN事件,ViewGroup总是会调用自己的onInterceptTouchEvent方法来询问是否要拦截事件。

ViewGroup决定拦截事件后,后续的点击操作会默认交给它处理并且不再调用它的onInterceptTouchEvent方法。如果我们想提前处理所有的点击事件,要选择dispatchTouchEvent方法,只有这个方法会每次都会调用,前提是点击事件能够传递到当前的ViewGroup。

事件传递顺序:上级->下级。

事件处理顺序:下级->上级。

事件传递时候先调用dispatchTouchEvent方法,再执行onInterceptTouchEvent方法。返回值true,表示拦截不继续。

事件处理都是调用onTouchEvent方法,返回true,表示处理了,不用审核了。

初始情况下,返回值都是false。

滑动冲突解决方案

外部拦截法和内部拦截法

外部拦截法:

重写父容器的onInterceptTouchEvent方法。在ACTION_DOWN和ACTION_UP这两种情况下需要返回false,在ACTION_MOVE方法中根据条件判断父容器是否需要拦截。

内部拦截法:

父容器不拦截任何事件,所有事件传递给子元素。需要配合requestDisallowInterceptTouchEvent方法才能正常工作。在ACTION_DOWN的时候,子元素需要调用parent.requestDisallowInterceptTouchEvent(true)。

父元素也要默认拦截除了ACTION_DOWN以外的其他事件,这样当子元素调用parent.requestDisallowInterceptTouchEvent(false)方法时,父元素才能继续拦截所需事件。一旦父容器拦截了ACTION_DOWN事件,所有的事件都无法传递到子元素中。

ViewRoot和DecorView

ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的measure、layout、draw都是通过ViewRoot来完成的。在ActivityThread中,当Activity对象被创建完成后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,将ViewRootImpl和DecorView之间建立关联。

View的绘制

View工作流程指measure/layout/draw三个流程。

View的最终大小是在layout阶段确定的。

直接继承View的自定义控件需要重写onMeasure方法并设置wrap_content时的自身大小,否则在布局中使用wrap_content相当于使用match_parent。

解决View测量的四种方法:

1. onWindowFocusChanged方法

View已经初始化完毕了,当Activity窗口得到和失去焦点的时候都会调用一次。

2. view.post(runnable)

通过post也可以将一个runnalbe投递到消息队列的尾部,然后等待Looper调用此runnable的时候,View已经初始化好了。

protected void onStart(){
super.onStart();
view.post(new Runnable(){
@Ovriride
public void run(){
int width = view.getMeasureWidth();
int height = view.getMeasureHeight();
}
});
}


ViewTreeObserver

使用它的众多回调可以完成这个功能。可以通过OnGlobalLayoutListener这个接口。当View树的状态发生改变或者View树内部的View的可见性发生改变时,onGlobalLay
4000
out方法将会被回调。

protected void onStart(){
super.onStart();
ViewTreeObserver observer = view.getViewTreeObserver();
observer.addOnGloalLayoutListener(new OnGlobalLayoutListener(){
@Override
public void onGlobalLayout(){
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int width = view.getMeasureWidth();
int height = view.getMeasureHeight();
}
});
}


view.measure(int widthMeasureSpec,int heightMeasureSpec)

通过手动测量。

Layout的作用是ViewGroup用来确定子元素位置的,在layout方法中确定View本身的位置,在onLayout方法中确定所有子元素的位置。

View的绘制过程遵循以下几步:

绘制背景 background.draw(canvas)

绘制自己 onDraw

绘制children dispatchDraw,dispatchDraw会遍历所有子元素的draw方法。

绘制装饰 onDrawScrollBars

自定义View的方法

需要注意的一些问题:

1.让View支持wrap_content。

2.直接继承View的控件,如果不在draw方法中处理padding,那么padding属性不起作用;如果直接继承ViewGroup,需要在onMeasure和onLayout中考虑padding和子元素的margin对其造成的影响。

3.在View的内部提供了post系列的方法,可以替代Handler。

4.View中的动画和线程,在onDetachedFromWindow中停止比较好。

5.View中存在滑动嵌套,需要考虑滑动冲突问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android