收集整理 View必须要了解的Android坐标知识
2014-12-01 15:43
417 查看
说来说去都不如 画图示意 简单易懂啊!!!真是的! 来吧~~先上张图~~!
(一)首先明确一下 android 中的坐标系统 :
屏幕的左上角是坐标系统原点(0,0)
原点向右延伸是X轴正方向,原点向下延伸是Y轴正方向
(二)关于Scroll: 屏幕显示的内容很多时,会有超出一屏的情况,于是就产生了Scroll的概念。
在View类中有个方法:
getScrollY() 英文原文描述是:
Return the scrolled top position of this view. This is the top edge of the displayed part of your view....
其实理解起来仍然就是:就是这个view相对于“坐标系统原点”(见上图)在Y轴上的偏移量.(getScrollX同理)
【哇哈,有了图就是好说明啊~ 省了多少语言描述啊,而且还不一定能说清楚~】
getScrollY()就是当前视图相对于屏幕原点在Y轴上的偏移量.
(三)MotionEvent类中 getRowX()和 getX()的区别:
event.getRowX():触摸点相对于屏幕原点的x坐标
event.getX(): 触摸点相对于其所在组件原点的x坐标
于是乎: view.getScrollY() + event.getY() 就得到了view中的触摸点在Y轴上的偏移量
结论:
当你触到按钮时,x,y是相对于该按钮左上点(控件本身)的相对位置。
而rawx,rawy始终是相对于屏幕的位置。
可知:
getRowX:触摸点相对于屏幕的坐标
getX: 触摸点相对于按钮的坐标
getTop: 按钮左上角相对于父view(LinerLayout)的y坐标
getLeft: 按钮左上角相对于父view(LinerLayout)的x坐标
可以想象 getRight()等同于下面的计算:getLeft()+getWidth()。
getX()是表示Widget相对于自身左上角的x坐标,而getRawX()是表示相对于屏幕左上角的x坐标值(注意:这个屏幕左上角是手机屏幕左上角,不管activity是否有titleBar或是否全屏幕),getY(),getRawY()一样的道理
(四)TextView类中
有个 getLayout()方法:the Layout that is currently being used to display the text. This
can be null if the text or width
has recently changes.
其返回类型是Layout ,也就是返回textView的布局。
然后重要的是通过这个 layout调用一个方法:
getLineForVertical(int verticalPointPosition) //得到某点在垂直方向上的行数值
于是综上所述,在实际的触摸事件中可以这样使用:
Layout layout=textView.getLayout();
int line = layout.getLineForVertical(textView.getScrollY() + (int) event.getY());
//得到触摸点在textView中垂直方向上的行数值。参数是触摸点在Y轴上的偏移量
接下来继续介绍一个方法,要用到上边的 layout 和 line:
layout.getOffsetForHorizontal( line , (int) event.getX() );
//得到触摸点在某一行水平方向上的偏移量。
参数分别是: 该行行数值 和 触摸点在该行X轴上的偏移量。
此方法得到的该值会根据该行上的文字的多少而变化,并不是横向上的像素大小;
整个坐标系是以手机屏幕左上角为原点(0,0),如果在屏幕没有滑动之前,这一理解肯定是ok的,但在滑屏之后,就会产生很多歧义和混淆,原因在于使用过程当中,很多方法的参数并非是参照屏幕,而是相对于父视图,对这整个过程和后面自定义控件的坐标变化带来的各个参数变化来说理解起来就并不那么适合了,最开始给人的感觉是越来越不清楚这个坐标该怎么设置,好像坐标系总在发生变化,后来才慢慢在思维当中构建起视图与视图容器以及屏幕之间的关系。
android.view.View.layout(int l, int t, int r, int b) layout的过程就是确定View在屏幕上显示的具体位置,在代码中就是设置其成员变量mLeft,mTop,mRight,mBottom的值,这几个值构成的矩形区域就是该View显示的位置,不过这里的具体位置都是相对与父视图的位置。mLeft代表当前view.layout的这个view的左边缘离它的父视图左边缘的距离,拿上面“子视图2.layout(int
l, int t, int r, int b) ”来说,它的父视图便是子视图1,2,3合起来形成的整个大矩形,那么这里将父视图的左上角定为(0,0),那么可以确定mLeft为一个子视图宽度320,以此类推,mTop指当前view的上边缘离父视图上边缘的距离。而以此为界,mRight所指的是当前view的右边缘离父视图左边缘的距离,一眼可以看出值为640(mLeft+自己的宽度),mBottom也是指当前view的下边缘离父视图的上边缘的距离。至于为何如此,大概是因为坐标系的缘故,坐标中的任何点都必须以(0,0)为起点,XY轴为衡量。
视图左侧位置 view.getLeft()
视图右侧位置 view.getRight()
视图顶部位置 view.getTop();
视图底部位置 view.getBottom();
这四个方法所获取到的各个左上右下的值与layout的四个参数代表的是一样的,都是相对父视图的左边缘与上边缘。
视图宽度 view.getWidth();
视图高度 view.getHeight() ;
这两个方法获取的是该view的高和宽,仅仅在滑动的情况下,或者说该view的大小如果不发生变化,它的值是不会变的。
getMeasuredWidth();
getMeasuredHeight();
说到这里就不得不提getWidth()、getHeight()和getMeasuredWidth()、getMeasuredHeight()这两对函数之间的区别,getMeasuredWidth()、getMeasuredHeight()返回的是measure过程得到的mMeasuredWidth和mMeasuredHeight的值,而getWidth()和getHeight()返回的是mRight
- mLeft和mBottom - mTop的值。一般情况下layout过程会参考measure过程中计算得到的mMeasuredWidth和mMeasuredHeight来安排子视图在父视图中显示的位置,但这不是必须的,measure过程得到的结果可能完全没有实际用处,特别是对于一些自定义的ViewGroup,其子视图的个数、位置和大小都是固定的,这时候我们可以忽略整个measure过程,只在layout函数中传入的4个参数来安排每个子视图的具体位置。
view.getX();
view.getY();
getX和getY获取到的值为相对于父视图而言的两个左边缘和上边缘的距离。
view.getLocationOnScreen(location); 该方法可以获取到当前view与屏幕的关系,location(0)代表X值,表示该view的左边缘与屏幕的左边缘之间的距离。可以想象,当滑屏产生,view开始移动该值肯定会改变的。location(1)代表Y值,表示该view的上边缘与屏幕的上边缘之间的距离,该距离肯定是包含标题栏的高度的。
getLocationInWindow();
ps:View.getLocationInWindow()和 View.getLocationOnScreen()在window占据全部screen时,返回值相同,不同的典型情况是在Dialog中时。当Dialog出现在屏幕中间时,View.getLocationOnScreen()取得的值要比View.getLocationInWindow()取得的值要大。
VelocityTracker.getXVelocity() 指滑动速度包括速率和方向两个方面,往左滑动小于0,值为负;往右滑动大于0,值为正。
view.getLocationInWindow(location); //获取在当前窗口内的绝对坐标
view.getLocationOnScreen(location);//获取在整个屏幕内的绝对坐标
location [0]--->x坐标,location [1]--->y坐标
getLocationOnScreen
,计算该视图在全局坐标系中的x,y值,(注意这个值是要从屏幕顶端算起,也就是索包括了通知栏的高度)//获取在当前屏幕内的绝对坐标
getLocationInWindow ,计算该视图在它所在的widnow的坐标x,y值,//获取在整个窗口内的绝对坐标
getLeft , getTop, getBottom,getRight, 这一组是获取相对在它父亲里的坐标
view.scrollTo(x,y) 将整个父视图的左上角定为(0,0),再移动这个屏幕的左上角到父视图的点(x,y)处,注意此处的x和y是根据父视图的坐标系来定的。
view.scrollBy(x,y) x代表横向移动的距离,y代表纵向移动的距离
view.getScrollX
view.getScrollY
将整个父视图的左上角定为(0,0),那么子view.getScrollX会获取到屏幕左边缘减去父视图的左边缘为0的距离,特别当滑屏时,父视图会被迫隐藏一部分,因为屏幕的大小是固定的。getScrollY以此类推。
event.getX()
event.getY()
该方法是不受视图影响的,X和Y的值仅仅代表手指在以左上角(0,0)为原点的屏幕触摸点的坐标值。
Scroller.getCurrY()
Scroller.getCurrX()
该方法拿横轴来说,代表屏幕的左边缘离父视图的左边缘的距离。
Scroller.startScroll(int startX, int startY, int dx, int dy)
四个参数分别表示起点的坐标和滑动的向量,即从(startX,startY)开始滑动,横向滑动dx的距离,纵向滑动dy的距离(正值向左滑,负值向右滑),而这里的startX,startY又是参照的父视图左上角为原点坐标的坐标系,滑屏时经常使用getScrollX()和getScrollY()来代表屏幕左边缘和上边缘处于父视图坐标系的具体位置
TranslateAnimation()
参数参照:http://blog.sina.com.cn/s/blog_90b91bf10101ai3e.html
以上是在做滑屏控件经常用到的方法,一方面需要了解layout和measure的基本流程,更重要一方面,当你想要实现某一个效果的时候,比如slidingmenu那样的控件,查看源码我们可以知道它是继承的ViewGroup,该怎样入手去做呢。
首先,需要了解它的父视图是什么,slidingmenu为例,打开程序,第一眼,是一个很普通的视图页面,当向右滑动手指,这个视图页面开始向右边移动,而从左边会慢慢移出来另一部分视图,看上去像是抽出来的或者是隐藏的,事实上抛开阴影效果来讲,想象手机屏幕的左边有一部分我们看不到的视图,它就是这个被抽出来的menu视图了。概括来说,一个主view,一个menu其实是并排于一个大视图上面的。
找到了父视图,接下来就好办了,认定这个父视图的宽度就是主view的宽度和menu的宽度之和(暂不考虑padding之类),高度就是屏幕的高度,那么在思维当中这个二维平面就产生了,将它想成一张纸,然后对准主view将这张纸贴到手机屏幕上,左右滑动,会看到其实slidingmenu也就是这么个效果。
然后,实现的思路会清晰很多。定义这个父视图为myview继承viewgroup,原因在于尽管主view和menu并排在一个大view下,但毕竟两者的内容不同,后面需要放进不同的控件处理不同的事件,这个父视图内包含着两个view,到时候处理起来会方便很多,setcontentview为这个父视图,那么打开程序的第一眼就会看到它。再定义这两个view设置好两个内容布局,并将它们addview添加到myview当中。外部工作基本就完成了,可以呈现父视图,并且父视图内有两个子view。
接下来,需要去完善一些细节,父视图内的子view该如何放置,这是关乎成败的一环,也就是如何将这张纸贴到我们希望的位置,这时就是onlayout的处理了,处理好屏幕,父视图子view之间的位置关系,通过各自的layout参数设置来摆放妥当各个view,比如开始的时候menu是隐藏的,这个就是通过位置的摆放设置的,然后它是从左边滑出来的,说明它处于父视图的左边位置,而主view处于相对右边的位置,而屏幕刚好也处于父视图右边的位置,恰好能看到主view的全貌,在脑海里如果能有清晰的画面出现,实现起来就会轻松很多。当实现了这个摆放,就可以理解menudrawer里面上下左右都可以滑出menu的结构了。
最后,便是滑动效果,请相信这样的控件里面,任何处理肯定都会和view位置的摆放扯上关系,滑动方向,滑动距离等等都涉及到坐标的处理。这也是为何上面列出那些常用的获取view坐标的方法。
总结下来,构建类似这样的控件,也就这三点,明确父子视图和屏幕的关系,通过坐标和位置参数设置它们的关系,处理这些关系发生变化的情况。
当然,事实上slidingmenu远远没这么简单,其中为了方便后续开发,它内置了很多接口和处理,大多数都是位置坐标和事件监听相关联,而万变不离其宗的是,它也肯定有这三个方面的构建,理解了这些基本的东西,尝试做一些自己想象的效果,对自定义的理解来说,进步会非常大。
资料来自互联网
Android~获取view在屏幕中的位置
getLocalVisibleRect , 返回一个填充的Rect对象, 感觉是这个View的Rect大小,left,top取到的都是0
getGlobalVisibleRect , 获取全局坐标系的一个视图区域, 返回一个填充的Rect对象;该Rect是基于总整个屏幕的
getLocationOnScreen ,计算该视图在全局坐标系中的x,y值,(注意这个值是要从屏幕顶端算起,也就是索包括了通知栏的高度)//获取在当前屏幕内的绝对坐标
getLocationInWindow ,计算该视图在它所在的widnow的坐标x,y值,//获取在整个窗口内的绝对坐标 (不是很理解= =、)
getLeft , getTop, getBottom, getRight, 这一组是获取相对在它父亲里的坐标
**注**:如果在Activity的OnCreate()事件输出那些参数,是全为0,要等UI控件都加载完了才能获取到这些
example:
int[] location = new int[2];
v.getLocationOnScreen(location);
int x = location[0];
int y = location[1];
Rect 这个类到底是干嘛用的,看源码介绍
得到宽度,因为没有做 左点<=右点 所以结果可能为负值
得到高度,同上
得到整数的中心点x,y坐标
得到精确的值float 中心点x,y
RectF 看源码其实就是提高了精度的,其他区别不大
常用的计算方法,比如获得TextView文本内容的矩形大小,其实TextView就是个矩形,里面填充了文字。我们有个逻辑是给这个textview外面套一个单线条红色圈,就需要知道它的Rect值
mTextPaint = new Paint();
mTextPaint.getTextBounds(mText, 0, mText.length(), mTextBounds);
这样mTextBounds就是Rect类的Object
MeasureSpec学习
在自定义View和ViewGroup的时候,我们经常会遇到int型的MeasureSpec来表示一个组件的大小,这个变量里面不仅有组件的尺寸大小,还有大小的模式。
这个大小的模式,有点难以理解。在系统中组件的大小模式有三种:
1.精确模式(MeasureSpec.EXACTLY)
在这种模式下,尺寸的值是多少,那么这个组件的长或宽就是多少。
2.最大模式(MeasureSpec.AT_MOST)
这个也就是父组件,能够给出的最大的空间,当前组件的长或宽最大只能为这么大,当然也可以比这个小。
3.未指定模式(MeasureSpec.UNSPECIFIED)
这个就是说,当前组件,可以随便用空间,不受限制。
可能有很多人想不通,一个int型整数怎么可以表示两个东西(大小模式和大小的值),一个int类型我们知道有32位。而模式有三种,要表示三种状
态,至少得2位二进制位。于是系统采用了最高的2位表示模式。如图:
最高两位是00的时候表示"未指定模式"。即MeasureSpec.UNSPECIFIED
最高两位是01的时候表示"'精确模式"。即MeasureSpec.EXACTLY
最高两位是11的时候表示"最大模式"。即MeasureSpec.AT_MOST
很多人一遇到位操作头就大了,为了操作简便,于是系统给我提供了一个MeasureSpec工具类。
这个工具类有四个方法和三个常量(上面所示)供我们使用:
//这个是由我们给出的尺寸大小和模式生成一个包含这两个信息的int变量,这里这个模式这个参数,传三个常量中的一个。
public static int makeMeasureSpec(int size, int mode)
//这个是得到这个变量中表示的模式信息,将得到的值与三个常量进行比较。
public static int getMode(int measureSpec)
//这个是得到这个变量中表示的尺寸大小的值。
public static int getSize(int measureSpec)
//把这个变量里面的模式和大小组成字符串返回来,方便打日志
public static String toString(int measureSpec)
MeasureSpec.EXACTLY:当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸,注意FILL_PARENT是屏幕的尺寸,所以是精确值。
MeasureSpec.AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。
MeasureSpec.UNSPECIFIED是未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。
因此,在重写onMeasure方法时要根据模式不同进行尺寸计算。下面代码就是一种比较典型的方式:
[java]
view plaincopy
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getMeasuredLength(widthMeasureSpec, true), getMeasuredLength(heightMeasureSpec, false));
}
private int getMeasuredLength(int length, boolean isWidth) {
int specMode = MeasureSpec.getMode(length);
int specSize = MeasureSpec.getSize(length);
int size;
int padding = isWidth ? getPaddingLeft() + getPaddingRight()
: getPaddingTop() + getPaddingBottom();
if (specMode == MeasureSpec.EXACTLY) {
size = specSize;
} else {
size = isWidth ? padding + mWave.length / 4 : DEFAULT_HEIGHT
+ padding;
if (specMode == MeasureSpec.AT_MOST) {
size = Math.min(size, specSize);
}
}
return size;
}
解决ScrollView嵌套ListView和GridView冲突的方法
[html]
view plaincopy
public class MyListView extends ListView {
public MyListView(Context context) {
super(context);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
public class MyGridView extends GridView {
private boolean haveScrollbar = true;
public MyGridView(Context context) {
super(context);
}
public MyGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* 设置是否有ScrollBar,当要在ScollView中显示时,应当设置为false。 默认为 true
*
* @param haveScrollbars
*/
public void setHaveScrollbar(boolean haveScrollbar) {
this.haveScrollbar = haveScrollbar;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (haveScrollbars == false) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
在自定义View和ViewGroup的时候,我们经常会遇到int型的MeasureSpec来表示一个组件的大小,这个变量里面不仅有组件的尺寸大小,还有大小的模式。
这个大小的模式,有点难以理解。在系统中组件的大小模式有三种:
1.精确模式(MeasureSpec.EXACTLY)
在这种模式下,尺寸的值是多少,那么这个组件的长或宽就是多少。
2.最大模式(MeasureSpec.AT_MOST)
这个也就是父组件,能够给出的最大的空间,当前组件的长或宽最大只能为这么大,当然也可以比这个小。
3.未指定模式(MeasureSpec.UNSPECIFIED)
这个就是说,当前组件,可以随便用空间,不受限制。
可能有很多人想不通,一个int型整数怎么可以表示两个东西(大小模式和大小的值),一个int类型我们知道有32位。而模式有三种,要表示三种状
态,至少得2位二进制位。于是系统采用了最高的2位表示模式。如图:
最高两位是00的时候表示"未指定模式"。即MeasureSpec.UNSPECIFIED
最高两位是01的时候表示"'精确模式"。即MeasureSpec.EXACTLY
最高两位是11的时候表示"最大模式"。即MeasureSpec.AT_MOST
很多人一遇到位操作头就大了,为了操作简便,于是系统给我提供了一个MeasureSpec工具类。
这个工具类有四个方法和三个常量(上面所示)供我们使用:
//这个是由我们给出的尺寸大小和模式生成一个包含这两个信息的int变量,这里这个模式这个参数,传三个常量中的一个。
public static int makeMeasureSpec(int size, int mode)
//这个是得到这个变量中表示的模式信息,将得到的值与三个常量进行比较。
public static int getMode(int measureSpec)
//这个是得到这个变量中表示的尺寸大小的值。
public static int getSize(int measureSpec)
//把这个变量里面的模式和大小组成字符串返回来,方便打日志
public static String toString(int measureSpec)
MeasureSpec.EXACTLY:当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。
MeasureSpec.AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。
MeasureSpec.UNSPECIFIED是未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。
因此,在重写onMeasure方法时要根据模式不同进行尺寸计算。下面代码就是一种比较典型的方式:
[java]
view plaincopy
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getMeasuredLength(widthMeasureSpec, true), getMeasuredLength(heightMeasureSpec, false));
}
private int getMeasuredLength(int length, boolean isWidth) {
int specMode = MeasureSpec.getMode(length);
int specSize = MeasureSpec.getSize(length);
int size;
int padding = isWidth ? getPaddingLeft() + getPaddingRight()
: getPaddingTop() + getPaddingBottom();
if (specMode == MeasureSpec.EXACTLY) {
size = specSize;
} else {
size = isWidth ? padding + mWave.length / 4 : DEFAULT_HEIGHT
+ padding;
if (specMode == MeasureSpec.AT_MOST) {
size = Math.min(size, specSize);
}
}
return size;
}
解决ScrollView嵌套ListView和GridView冲突的方法
[html]
view plaincopy
public class MyListView extends ListView {
public MyListView(Context context) {
super(context);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
public class MyGridView extends GridView {
private boolean haveScrollbar = true;
public MyGridView(Context context) {
super(context);
}
public MyGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* 设置是否有ScrollBar,当要在ScollView中显示时,应当设置为false。 默认为 true
*
* @param haveScrollbars
*/
public void setHaveScrollbar(boolean haveScrollbar) {
this.haveScrollbar = haveScrollbar;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (haveScrollbars == false) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
* Paint即画笔,在绘图过程中起到了极其重要的作用,画笔主要保存了颜色,
* 样式等绘制信息,指定了如何绘制文本和图形,画笔对象有很多设置方法,
* 大体上可以分为两类,一类与图形绘制相关,一类与文本绘制相关。
*
* <strong><span style="color: #800000; font-size: small;">1.图形绘制 </span></strong>
* setARGB(int a,int r,int g,int b);
* 设置绘制的颜色,a代表透明度,r,g,b代表颜色值。
*
* setAlpha(int a);
* 设置绘制图形的透明度。
*
* setColor(int color);
* 设置绘制的颜色,使用颜色值来表示,该颜色值包括透明度和RGB颜色。
*
* setAntiAlias(boolean aa);
* 设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
*
* setDither(boolean dither);
* 设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰
*
* setFilterBitmap(boolean filter);
* 如果该项设置为true,则图像在动画进行中会滤掉对Bitmap图像的优化操作,加快显示
* 速度,本设置项依赖于dither和xfermode的设置
*
* setMaskFilter(MaskFilter maskfilter);
* 设置MaskFilter,可以用不同的MaskFilter实现滤镜的效果,如滤化,立体等 *
* setColorFilter(ColorFilter colorfilter);
* 设置颜色过滤器,可以在绘制颜色时实现不用颜色的变换效果
*
* setPathEffect(PathEffect effect);
* 设置绘制路径的效果,如点画线等
*
* setShader(Shader shader);
* 设置图像效果,使用Shader可以绘制出各种渐变效果
*
* setShadowLayer(float radius ,float dx,float dy,int color);
* 在图形下面设置阴影层,产生阴影效果,radius为阴影的角度,dx和dy为阴影在x轴和y轴上的距离,color为阴影的颜色
*
* setStyle(Paint.Style style);
* 设置画笔的样式,为FILL,FILL_OR_STROKE,或STROKE
*
* setStrokeCap(Paint.Cap cap);
* 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式,如圆形样式
* Cap.ROUND,或方形样式Cap.SQUARE
*
* setSrokeJoin(Paint.Join join);
* 设置绘制时各图形的结合方式,如平滑效果等
*
* setStrokeWidth(float width);
* 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细度
*
* setXfermode(Xfermode xfermode);
* 设置图形重叠时的处理方式,如合并,取交集或并集,经常用来制作橡皮的擦除效果
*
*<span style="color: #800000; font-size: small;"><strong> 2.文本绘制 </strong></span>
* setFakeBoldText(boolean fakeBoldText);
* 模拟实现粗体文字,设置在小字体上效果会非常差
*
* setSubpixelText(boolean subpixelText);
* 设置该项为true,将有助于文本在LCD屏幕上的显示效果
*
* setTextAlign(Paint.Align align);
* 设置绘制文字的对齐方向
*
* setTextScaleX(float scaleX);
* 设置绘制文字x轴的缩放比例,可以实现文字的拉伸的效果
*
* setTextSize(float textSize);
* 设置绘制文字的字号大小
*
* setTextSkewX(float skewX);
* 设置斜体文字,skewX为倾斜弧度
*
* setTypeface(Typeface typeface);
* 设置Typeface对象,即字体风格,包括粗体,斜体以及衬线体,非衬线体等
*
* setUnderlineText(boolean underlineText);
* 设置带有下划线的文字效果
*
* setStrikeThruText(boolean strikeThruText);
* 设置带有删除线的效果
*
**/
Paint.setStrokeJoin(Join join)设置结合处的样子,Miter:结合处为锐角, Round:结合处为圆弧:BEVEL:结合处为直线。
setStrokeMiter(float miter )是设置笔画的倾斜度,如:小时候用的铅笔,削的时候斜与垂直削出来的笔尖效果是
1.下面的Xfermode子类可以改变这种行为:
AvoidXfermode 指定了一个颜色和容差,强制Paint避免在它上面绘图(或者只在它上面绘图)。
PixelXorXfermode 当覆盖已有的颜色时,应用一个简单的像素XOR操作。
PorterDuffXfermode 这是一个非常强大的转换模式,使用它,可以使用图像合成的16条Porter-Duff规则的任意一条来控制Paint如何与已有的Canvas图像进行交互。
要应用转换模式,可以使用setXferMode方法,如下所示:
AvoidXfermode avoid = new AvoidXfermode(Color.BLUE, 10, AvoidXfermode.Mode. AVOID); borderPen.setXfermode(avoid);
2.Porter-Duff 效果图:
1.PorterDuff.Mode.CLEAR
所绘制不会提交到画布上。
2.PorterDuff.Mode.SRC
显示上层绘制图片
3.PorterDuff.Mode.DST
显示下层绘制图片
4.PorterDuff.Mode.SRC_OVER
正常绘制显示,上下层绘制叠盖。
5.PorterDuff.Mode.DST_OVER
上下层都显示。下层居上显示。
6.PorterDuff.Mode.SRC_IN
取两层绘制交集。显示上层。
7.PorterDuff.Mode.DST_IN
取两层绘制交集。显示下层。
8.PorterDuff.Mode.SRC_OUT
取上层绘制非交集部分。
9.PorterDuff.Mode.DST_OUT
取下层绘制非交集部分。
10.PorterDuff.Mode.SRC_ATOP
取下层非交集部分与上层交集部分
11.PorterDuff.Mode.DST_ATOP
取上层非交集部分与下层交集部分
12.PorterDuff.Mode.XOR
13.PorterDuff.Mode.DARKEN
14.PorterDuff.Mode.LIGHTEN
15.PorterDuff.Mode.MULTIPLY
16.PorterDuff.Mode.SCREEN
(一)首先明确一下 android 中的坐标系统 :
屏幕的左上角是坐标系统原点(0,0)
原点向右延伸是X轴正方向,原点向下延伸是Y轴正方向
(二)关于Scroll: 屏幕显示的内容很多时,会有超出一屏的情况,于是就产生了Scroll的概念。
在View类中有个方法:
getScrollY() 英文原文描述是:
Return the scrolled top position of this view. This is the top edge of the displayed part of your view....
其实理解起来仍然就是:就是这个view相对于“坐标系统原点”(见上图)在Y轴上的偏移量.(getScrollX同理)
【哇哈,有了图就是好说明啊~ 省了多少语言描述啊,而且还不一定能说清楚~】
getScrollY()就是当前视图相对于屏幕原点在Y轴上的偏移量.
(三)MotionEvent类中 getRowX()和 getX()的区别:
event.getRowX():触摸点相对于屏幕原点的x坐标
event.getX(): 触摸点相对于其所在组件原点的x坐标
于是乎: view.getScrollY() + event.getY() 就得到了view中的触摸点在Y轴上的偏移量
结论:
当你触到按钮时,x,y是相对于该按钮左上点(控件本身)的相对位置。
而rawx,rawy始终是相对于屏幕的位置。
可知:
getRowX:触摸点相对于屏幕的坐标
getX: 触摸点相对于按钮的坐标
getTop: 按钮左上角相对于父view(LinerLayout)的y坐标
getLeft: 按钮左上角相对于父view(LinerLayout)的x坐标
可以想象 getRight()等同于下面的计算:getLeft()+getWidth()。
getX()是表示Widget相对于自身左上角的x坐标,而getRawX()是表示相对于屏幕左上角的x坐标值(注意:这个屏幕左上角是手机屏幕左上角,不管activity是否有titleBar或是否全屏幕),getY(),getRawY()一样的道理
(四)TextView类中
有个 getLayout()方法:the Layout that is currently being used to display the text. This
can be null if the text or width
has recently changes.
其返回类型是Layout ,也就是返回textView的布局。
然后重要的是通过这个 layout调用一个方法:
getLineForVertical(int verticalPointPosition) //得到某点在垂直方向上的行数值
于是综上所述,在实际的触摸事件中可以这样使用:
Layout layout=textView.getLayout();
int line = layout.getLineForVertical(textView.getScrollY() + (int) event.getY());
//得到触摸点在textView中垂直方向上的行数值。参数是触摸点在Y轴上的偏移量
接下来继续介绍一个方法,要用到上边的 layout 和 line:
layout.getOffsetForHorizontal( line , (int) event.getX() );
//得到触摸点在某一行水平方向上的偏移量。
参数分别是: 该行行数值 和 触摸点在该行X轴上的偏移量。
此方法得到的该值会根据该行上的文字的多少而变化,并不是横向上的像素大小;
整个坐标系是以手机屏幕左上角为原点(0,0),如果在屏幕没有滑动之前,这一理解肯定是ok的,但在滑屏之后,就会产生很多歧义和混淆,原因在于使用过程当中,很多方法的参数并非是参照屏幕,而是相对于父视图,对这整个过程和后面自定义控件的坐标变化带来的各个参数变化来说理解起来就并不那么适合了,最开始给人的感觉是越来越不清楚这个坐标该怎么设置,好像坐标系总在发生变化,后来才慢慢在思维当中构建起视图与视图容器以及屏幕之间的关系。
android.view.View.layout(int l, int t, int r, int b) layout的过程就是确定View在屏幕上显示的具体位置,在代码中就是设置其成员变量mLeft,mTop,mRight,mBottom的值,这几个值构成的矩形区域就是该View显示的位置,不过这里的具体位置都是相对与父视图的位置。mLeft代表当前view.layout的这个view的左边缘离它的父视图左边缘的距离,拿上面“子视图2.layout(int
l, int t, int r, int b) ”来说,它的父视图便是子视图1,2,3合起来形成的整个大矩形,那么这里将父视图的左上角定为(0,0),那么可以确定mLeft为一个子视图宽度320,以此类推,mTop指当前view的上边缘离父视图上边缘的距离。而以此为界,mRight所指的是当前view的右边缘离父视图左边缘的距离,一眼可以看出值为640(mLeft+自己的宽度),mBottom也是指当前view的下边缘离父视图的上边缘的距离。至于为何如此,大概是因为坐标系的缘故,坐标中的任何点都必须以(0,0)为起点,XY轴为衡量。
视图左侧位置 view.getLeft()
视图右侧位置 view.getRight()
视图顶部位置 view.getTop();
视图底部位置 view.getBottom();
这四个方法所获取到的各个左上右下的值与layout的四个参数代表的是一样的,都是相对父视图的左边缘与上边缘。
视图宽度 view.getWidth();
视图高度 view.getHeight() ;
这两个方法获取的是该view的高和宽,仅仅在滑动的情况下,或者说该view的大小如果不发生变化,它的值是不会变的。
getMeasuredWidth();
getMeasuredHeight();
说到这里就不得不提getWidth()、getHeight()和getMeasuredWidth()、getMeasuredHeight()这两对函数之间的区别,getMeasuredWidth()、getMeasuredHeight()返回的是measure过程得到的mMeasuredWidth和mMeasuredHeight的值,而getWidth()和getHeight()返回的是mRight
- mLeft和mBottom - mTop的值。一般情况下layout过程会参考measure过程中计算得到的mMeasuredWidth和mMeasuredHeight来安排子视图在父视图中显示的位置,但这不是必须的,measure过程得到的结果可能完全没有实际用处,特别是对于一些自定义的ViewGroup,其子视图的个数、位置和大小都是固定的,这时候我们可以忽略整个measure过程,只在layout函数中传入的4个参数来安排每个子视图的具体位置。
view.getX();
view.getY();
getX和getY获取到的值为相对于父视图而言的两个左边缘和上边缘的距离。
view.getLocationOnScreen(location); 该方法可以获取到当前view与屏幕的关系,location(0)代表X值,表示该view的左边缘与屏幕的左边缘之间的距离。可以想象,当滑屏产生,view开始移动该值肯定会改变的。location(1)代表Y值,表示该view的上边缘与屏幕的上边缘之间的距离,该距离肯定是包含标题栏的高度的。
getLocationInWindow();
ps:View.getLocationInWindow()和 View.getLocationOnScreen()在window占据全部screen时,返回值相同,不同的典型情况是在Dialog中时。当Dialog出现在屏幕中间时,View.getLocationOnScreen()取得的值要比View.getLocationInWindow()取得的值要大。
VelocityTracker.getXVelocity() 指滑动速度包括速率和方向两个方面,往左滑动小于0,值为负;往右滑动大于0,值为正。
getLocationInWindow 和 getLocationOnScreen
int[] location = new int[2] ;view.getLocationInWindow(location); //获取在当前窗口内的绝对坐标
view.getLocationOnScreen(location);//获取在整个屏幕内的绝对坐标
location [0]--->x坐标,location [1]--->y坐标
getLocationOnScreen
,计算该视图在全局坐标系中的x,y值,(注意这个值是要从屏幕顶端算起,也就是索包括了通知栏的高度)//获取在当前屏幕内的绝对坐标
getLocationInWindow ,计算该视图在它所在的widnow的坐标x,y值,//获取在整个窗口内的绝对坐标
getLeft , getTop, getBottom,getRight, 这一组是获取相对在它父亲里的坐标
view.scrollTo(x,y) 将整个父视图的左上角定为(0,0),再移动这个屏幕的左上角到父视图的点(x,y)处,注意此处的x和y是根据父视图的坐标系来定的。
view.scrollBy(x,y) x代表横向移动的距离,y代表纵向移动的距离
view.getScrollX
view.getScrollY
将整个父视图的左上角定为(0,0),那么子view.getScrollX会获取到屏幕左边缘减去父视图的左边缘为0的距离,特别当滑屏时,父视图会被迫隐藏一部分,因为屏幕的大小是固定的。getScrollY以此类推。
event.getX()
event.getY()
该方法是不受视图影响的,X和Y的值仅仅代表手指在以左上角(0,0)为原点的屏幕触摸点的坐标值。
Scroller.getCurrY()
Scroller.getCurrX()
该方法拿横轴来说,代表屏幕的左边缘离父视图的左边缘的距离。
Scroller.startScroll(int startX, int startY, int dx, int dy)
四个参数分别表示起点的坐标和滑动的向量,即从(startX,startY)开始滑动,横向滑动dx的距离,纵向滑动dy的距离(正值向左滑,负值向右滑),而这里的startX,startY又是参照的父视图左上角为原点坐标的坐标系,滑屏时经常使用getScrollX()和getScrollY()来代表屏幕左边缘和上边缘处于父视图坐标系的具体位置
TranslateAnimation()
参数参照:http://blog.sina.com.cn/s/blog_90b91bf10101ai3e.html
以上是在做滑屏控件经常用到的方法,一方面需要了解layout和measure的基本流程,更重要一方面,当你想要实现某一个效果的时候,比如slidingmenu那样的控件,查看源码我们可以知道它是继承的ViewGroup,该怎样入手去做呢。
首先,需要了解它的父视图是什么,slidingmenu为例,打开程序,第一眼,是一个很普通的视图页面,当向右滑动手指,这个视图页面开始向右边移动,而从左边会慢慢移出来另一部分视图,看上去像是抽出来的或者是隐藏的,事实上抛开阴影效果来讲,想象手机屏幕的左边有一部分我们看不到的视图,它就是这个被抽出来的menu视图了。概括来说,一个主view,一个menu其实是并排于一个大视图上面的。
找到了父视图,接下来就好办了,认定这个父视图的宽度就是主view的宽度和menu的宽度之和(暂不考虑padding之类),高度就是屏幕的高度,那么在思维当中这个二维平面就产生了,将它想成一张纸,然后对准主view将这张纸贴到手机屏幕上,左右滑动,会看到其实slidingmenu也就是这么个效果。
然后,实现的思路会清晰很多。定义这个父视图为myview继承viewgroup,原因在于尽管主view和menu并排在一个大view下,但毕竟两者的内容不同,后面需要放进不同的控件处理不同的事件,这个父视图内包含着两个view,到时候处理起来会方便很多,setcontentview为这个父视图,那么打开程序的第一眼就会看到它。再定义这两个view设置好两个内容布局,并将它们addview添加到myview当中。外部工作基本就完成了,可以呈现父视图,并且父视图内有两个子view。
接下来,需要去完善一些细节,父视图内的子view该如何放置,这是关乎成败的一环,也就是如何将这张纸贴到我们希望的位置,这时就是onlayout的处理了,处理好屏幕,父视图子view之间的位置关系,通过各自的layout参数设置来摆放妥当各个view,比如开始的时候menu是隐藏的,这个就是通过位置的摆放设置的,然后它是从左边滑出来的,说明它处于父视图的左边位置,而主view处于相对右边的位置,而屏幕刚好也处于父视图右边的位置,恰好能看到主view的全貌,在脑海里如果能有清晰的画面出现,实现起来就会轻松很多。当实现了这个摆放,就可以理解menudrawer里面上下左右都可以滑出menu的结构了。
最后,便是滑动效果,请相信这样的控件里面,任何处理肯定都会和view位置的摆放扯上关系,滑动方向,滑动距离等等都涉及到坐标的处理。这也是为何上面列出那些常用的获取view坐标的方法。
总结下来,构建类似这样的控件,也就这三点,明确父子视图和屏幕的关系,通过坐标和位置参数设置它们的关系,处理这些关系发生变化的情况。
当然,事实上slidingmenu远远没这么简单,其中为了方便后续开发,它内置了很多接口和处理,大多数都是位置坐标和事件监听相关联,而万变不离其宗的是,它也肯定有这三个方面的构建,理解了这些基本的东西,尝试做一些自己想象的效果,对自定义的理解来说,进步会非常大。
资料来自互联网
Android~获取view在屏幕中的位置
getLocalVisibleRect , 返回一个填充的Rect对象, 感觉是这个View的Rect大小,left,top取到的都是0
getGlobalVisibleRect , 获取全局坐标系的一个视图区域, 返回一个填充的Rect对象;该Rect是基于总整个屏幕的
getLocationOnScreen ,计算该视图在全局坐标系中的x,y值,(注意这个值是要从屏幕顶端算起,也就是索包括了通知栏的高度)//获取在当前屏幕内的绝对坐标
getLocationInWindow ,计算该视图在它所在的widnow的坐标x,y值,//获取在整个窗口内的绝对坐标 (不是很理解= =、)
getLeft , getTop, getBottom, getRight, 这一组是获取相对在它父亲里的坐标
**注**:如果在Activity的OnCreate()事件输出那些参数,是全为0,要等UI控件都加载完了才能获取到这些
example:
int[] location = new int[2];
v.getLocationOnScreen(location);
int x = location[0];
int y = location[1];
Rect 与 RectF
Rect 这个类到底是干嘛用的,看源码介绍/** * Rect holds four integer coordinates for a rectangle. The rectangle is * represented by the coordinates of its 4 edges (left, top, right bottom). * These fields can be accessed directly. Use width() and height() to retrieve * the rectangle's width and height. Note: most methods do not check to see that * the coordinates are sorted correctly (i.e. left <= right and top <= bottom). */ public final class Rect implements Parcelable { public int left; public int top; public int right; public int bottom; private static final Pattern FLATTENED_PATTERN = Pattern.compile( "(-?\\d+) (-?\\d+) (-?\\d+) (-?\\d+)"); /** * Create a new empty Rect. All coordinates are initialized to 0. */ public Rect() {} /** * Create a new rectangle with the specified coordinates. Note: no range * checking is performed, so the caller must ensure that left <= right and * top <= bottom. * * @param left The X coordinate of the left side of the rectagle * @param top The Y coordinate of the top of the rectangle * @param right The X coordinate of the right side of the rectagle * @param bottom The Y coordinate of the bottom of the rectangle */ public Rect(int left, int top, int right, int bottom) { this.left = left; this.top = top; this.right = right; this.bottom = bottom; } /** * @return the rectangle's width. This does not check for a valid rectangle * (i.e. left <= right) so the result may be negative. */ public final int width() { return right - left; } /** * @return the rectangle's height. This does not check for a valid rectangle * (i.e. top <= bottom) so the result may be negative. */ public final int height() { return bottom - top; } /** * @return the horizontal center of the rectangle. If the computed value * is fractional, this method returns the largest integer that is * less than the computed value. */ public final int centerX() { return (left + right) >> 1; } /** * @return the vertical center of the rectangle. If the computed value * is fractional, this method returns the largest integer that is * less than the computed value. */ public final int centerY() { return (top + bottom) >> 1; } /** * @return the exact horizontal center of the rectangle as a float. */ public final float exactCenterX() { return (left + right) * 0.5f; } /** * @return the exact vertical center of the rectangle as a float. */ public final float exactCenterY() { return (top + bottom) * 0.5f; }看到了,这个类其实就是一个矩形,它里面记录了左上右下4个点的坐标位置。几个比较常用的方法,其实自己理解了也可以写,但是API既然已经提供了,就不要再重复制造轮子了,但是计算中心位置的方法效率很高,可以借鉴。
得到宽度,因为没有做 左点<=右点 所以结果可能为负值
/** * @return the rectangle's width. This does not check for a valid rectangle * (i.e. left <= right) so the result may be negative. */ public final int width() { return right - left; }
得到高度,同上
/** * @return the rectangle's height. This does not check for a valid rectangle * (i.e. top <= bottom) so the result may be negative. */ public final int height() { return bottom - top; }
得到整数的中心点x,y坐标
/** * @return the horizontal center of the rectangle. If the computed value * is fractional, this method returns the largest integer that is * less than the computed value. */ public final int centerX() { return (left + right) >> 1; } /** * @return the vertical center of the rectangle. If the computed value * is fractional, this method returns the largest integer that is * less than the computed value. */ public final int centerY() { return (top + bottom) >> 1; }
得到精确的值float 中心点x,y
/** * @return the exact horizontal center of the rectangle as a float. */ public final float exactCenterX() { return (left + right) * 0.5f; } /** * @return the exact vertical center of the rectangle as a float. */ public final float exactCenterY() { return (top + bottom) * 0.5f; }
RectF 看源码其实就是提高了精度的,其他区别不大
常用的计算方法,比如获得TextView文本内容的矩形大小,其实TextView就是个矩形,里面填充了文字。我们有个逻辑是给这个textview外面套一个单线条红色圈,就需要知道它的Rect值
mTextPaint = new Paint();
mTextPaint.getTextBounds(mText, 0, mText.length(), mTextBounds);
这样mTextBounds就是Rect类的Object
MeasureSpec学习
在自定义View和ViewGroup的时候,我们经常会遇到int型的MeasureSpec来表示一个组件的大小,这个变量里面不仅有组件的尺寸大小,还有大小的模式。这个大小的模式,有点难以理解。在系统中组件的大小模式有三种:
1.精确模式(MeasureSpec.EXACTLY)
在这种模式下,尺寸的值是多少,那么这个组件的长或宽就是多少。
2.最大模式(MeasureSpec.AT_MOST)
这个也就是父组件,能够给出的最大的空间,当前组件的长或宽最大只能为这么大,当然也可以比这个小。
3.未指定模式(MeasureSpec.UNSPECIFIED)
这个就是说,当前组件,可以随便用空间,不受限制。
可能有很多人想不通,一个int型整数怎么可以表示两个东西(大小模式和大小的值),一个int类型我们知道有32位。而模式有三种,要表示三种状
态,至少得2位二进制位。于是系统采用了最高的2位表示模式。如图:
最高两位是00的时候表示"未指定模式"。即MeasureSpec.UNSPECIFIED
最高两位是01的时候表示"'精确模式"。即MeasureSpec.EXACTLY
最高两位是11的时候表示"最大模式"。即MeasureSpec.AT_MOST
很多人一遇到位操作头就大了,为了操作简便,于是系统给我提供了一个MeasureSpec工具类。
这个工具类有四个方法和三个常量(上面所示)供我们使用:
//这个是由我们给出的尺寸大小和模式生成一个包含这两个信息的int变量,这里这个模式这个参数,传三个常量中的一个。
public static int makeMeasureSpec(int size, int mode)
//这个是得到这个变量中表示的模式信息,将得到的值与三个常量进行比较。
public static int getMode(int measureSpec)
//这个是得到这个变量中表示的尺寸大小的值。
public static int getSize(int measureSpec)
//把这个变量里面的模式和大小组成字符串返回来,方便打日志
public static String toString(int measureSpec)
MeasureSpec.EXACTLY:当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸,注意FILL_PARENT是屏幕的尺寸,所以是精确值。
MeasureSpec.AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。
MeasureSpec.UNSPECIFIED是未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。
因此,在重写onMeasure方法时要根据模式不同进行尺寸计算。下面代码就是一种比较典型的方式:
[java]
view plaincopy
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getMeasuredLength(widthMeasureSpec, true), getMeasuredLength(heightMeasureSpec, false));
}
private int getMeasuredLength(int length, boolean isWidth) {
int specMode = MeasureSpec.getMode(length);
int specSize = MeasureSpec.getSize(length);
int size;
int padding = isWidth ? getPaddingLeft() + getPaddingRight()
: getPaddingTop() + getPaddingBottom();
if (specMode == MeasureSpec.EXACTLY) {
size = specSize;
} else {
size = isWidth ? padding + mWave.length / 4 : DEFAULT_HEIGHT
+ padding;
if (specMode == MeasureSpec.AT_MOST) {
size = Math.min(size, specSize);
}
}
return size;
}
解决ScrollView嵌套ListView和GridView冲突的方法
[html]
view plaincopy
public class MyListView extends ListView {
public MyListView(Context context) {
super(context);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
public class MyGridView extends GridView {
private boolean haveScrollbar = true;
public MyGridView(Context context) {
super(context);
}
public MyGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* 设置是否有ScrollBar,当要在ScollView中显示时,应当设置为false。 默认为 true
*
* @param haveScrollbars
*/
public void setHaveScrollbar(boolean haveScrollbar) {
this.haveScrollbar = haveScrollbar;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (haveScrollbars == false) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
在自定义View和ViewGroup的时候,我们经常会遇到int型的MeasureSpec来表示一个组件的大小,这个变量里面不仅有组件的尺寸大小,还有大小的模式。
这个大小的模式,有点难以理解。在系统中组件的大小模式有三种:
1.精确模式(MeasureSpec.EXACTLY)
在这种模式下,尺寸的值是多少,那么这个组件的长或宽就是多少。
2.最大模式(MeasureSpec.AT_MOST)
这个也就是父组件,能够给出的最大的空间,当前组件的长或宽最大只能为这么大,当然也可以比这个小。
3.未指定模式(MeasureSpec.UNSPECIFIED)
这个就是说,当前组件,可以随便用空间,不受限制。
可能有很多人想不通,一个int型整数怎么可以表示两个东西(大小模式和大小的值),一个int类型我们知道有32位。而模式有三种,要表示三种状
态,至少得2位二进制位。于是系统采用了最高的2位表示模式。如图:
最高两位是00的时候表示"未指定模式"。即MeasureSpec.UNSPECIFIED
最高两位是01的时候表示"'精确模式"。即MeasureSpec.EXACTLY
最高两位是11的时候表示"最大模式"。即MeasureSpec.AT_MOST
很多人一遇到位操作头就大了,为了操作简便,于是系统给我提供了一个MeasureSpec工具类。
这个工具类有四个方法和三个常量(上面所示)供我们使用:
//这个是由我们给出的尺寸大小和模式生成一个包含这两个信息的int变量,这里这个模式这个参数,传三个常量中的一个。
public static int makeMeasureSpec(int size, int mode)
//这个是得到这个变量中表示的模式信息,将得到的值与三个常量进行比较。
public static int getMode(int measureSpec)
//这个是得到这个变量中表示的尺寸大小的值。
public static int getSize(int measureSpec)
//把这个变量里面的模式和大小组成字符串返回来,方便打日志
public static String toString(int measureSpec)
MeasureSpec.EXACTLY:当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。
MeasureSpec.AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。
MeasureSpec.UNSPECIFIED是未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。
因此,在重写onMeasure方法时要根据模式不同进行尺寸计算。下面代码就是一种比较典型的方式:
[java]
view plaincopy
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getMeasuredLength(widthMeasureSpec, true), getMeasuredLength(heightMeasureSpec, false));
}
private int getMeasuredLength(int length, boolean isWidth) {
int specMode = MeasureSpec.getMode(length);
int specSize = MeasureSpec.getSize(length);
int size;
int padding = isWidth ? getPaddingLeft() + getPaddingRight()
: getPaddingTop() + getPaddingBottom();
if (specMode == MeasureSpec.EXACTLY) {
size = specSize;
} else {
size = isWidth ? padding + mWave.length / 4 : DEFAULT_HEIGHT
+ padding;
if (specMode == MeasureSpec.AT_MOST) {
size = Math.min(size, specSize);
}
}
return size;
}
解决ScrollView嵌套ListView和GridView冲突的方法
[html]
view plaincopy
public class MyListView extends ListView {
public MyListView(Context context) {
super(context);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
public class MyGridView extends GridView {
private boolean haveScrollbar = true;
public MyGridView(Context context) {
super(context);
}
public MyGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* 设置是否有ScrollBar,当要在ScollView中显示时,应当设置为false。 默认为 true
*
* @param haveScrollbars
*/
public void setHaveScrollbar(boolean haveScrollbar) {
this.haveScrollbar = haveScrollbar;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (haveScrollbars == false) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
Paint方法解析
/*** Paint即画笔,在绘图过程中起到了极其重要的作用,画笔主要保存了颜色,
* 样式等绘制信息,指定了如何绘制文本和图形,画笔对象有很多设置方法,
* 大体上可以分为两类,一类与图形绘制相关,一类与文本绘制相关。
*
* <strong><span style="color: #800000; font-size: small;">1.图形绘制 </span></strong>
* setARGB(int a,int r,int g,int b);
* 设置绘制的颜色,a代表透明度,r,g,b代表颜色值。
*
* setAlpha(int a);
* 设置绘制图形的透明度。
*
* setColor(int color);
* 设置绘制的颜色,使用颜色值来表示,该颜色值包括透明度和RGB颜色。
*
* setAntiAlias(boolean aa);
* 设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
*
* setDither(boolean dither);
* 设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰
*
* setFilterBitmap(boolean filter);
* 如果该项设置为true,则图像在动画进行中会滤掉对Bitmap图像的优化操作,加快显示
* 速度,本设置项依赖于dither和xfermode的设置
*
* setMaskFilter(MaskFilter maskfilter);
* 设置MaskFilter,可以用不同的MaskFilter实现滤镜的效果,如滤化,立体等 *
* setColorFilter(ColorFilter colorfilter);
* 设置颜色过滤器,可以在绘制颜色时实现不用颜色的变换效果
*
* setPathEffect(PathEffect effect);
* 设置绘制路径的效果,如点画线等
*
* setShader(Shader shader);
* 设置图像效果,使用Shader可以绘制出各种渐变效果
*
* setShadowLayer(float radius ,float dx,float dy,int color);
* 在图形下面设置阴影层,产生阴影效果,radius为阴影的角度,dx和dy为阴影在x轴和y轴上的距离,color为阴影的颜色
*
* setStyle(Paint.Style style);
* 设置画笔的样式,为FILL,FILL_OR_STROKE,或STROKE
*
* setStrokeCap(Paint.Cap cap);
* 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式,如圆形样式
* Cap.ROUND,或方形样式Cap.SQUARE
*
* setSrokeJoin(Paint.Join join);
* 设置绘制时各图形的结合方式,如平滑效果等
*
* setStrokeWidth(float width);
* 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细度
*
* setXfermode(Xfermode xfermode);
* 设置图形重叠时的处理方式,如合并,取交集或并集,经常用来制作橡皮的擦除效果
*
*<span style="color: #800000; font-size: small;"><strong> 2.文本绘制 </strong></span>
* setFakeBoldText(boolean fakeBoldText);
* 模拟实现粗体文字,设置在小字体上效果会非常差
*
* setSubpixelText(boolean subpixelText);
* 设置该项为true,将有助于文本在LCD屏幕上的显示效果
*
* setTextAlign(Paint.Align align);
* 设置绘制文字的对齐方向
*
* setTextScaleX(float scaleX);
* 设置绘制文字x轴的缩放比例,可以实现文字的拉伸的效果
*
* setTextSize(float textSize);
* 设置绘制文字的字号大小
*
* setTextSkewX(float skewX);
* 设置斜体文字,skewX为倾斜弧度
*
* setTypeface(Typeface typeface);
* 设置Typeface对象,即字体风格,包括粗体,斜体以及衬线体,非衬线体等
*
* setUnderlineText(boolean underlineText);
* 设置带有下划线的文字效果
*
* setStrikeThruText(boolean strikeThruText);
* 设置带有删除线的效果
*
**/
AvoidXfermode学习 颜色容差器
Paint.setStrokeJoin(Join join)设置结合处的样子,Miter:结合处为锐角, Round:结合处为圆弧:BEVEL:结合处为直线。setStrokeMiter(float miter )是设置笔画的倾斜度,如:小时候用的铅笔,削的时候斜与垂直削出来的笔尖效果是
1.下面的Xfermode子类可以改变这种行为:
AvoidXfermode 指定了一个颜色和容差,强制Paint避免在它上面绘图(或者只在它上面绘图)。
PixelXorXfermode 当覆盖已有的颜色时,应用一个简单的像素XOR操作。
PorterDuffXfermode 这是一个非常强大的转换模式,使用它,可以使用图像合成的16条Porter-Duff规则的任意一条来控制Paint如何与已有的Canvas图像进行交互。
要应用转换模式,可以使用setXferMode方法,如下所示:
AvoidXfermode avoid = new AvoidXfermode(Color.BLUE, 10, AvoidXfermode.Mode. AVOID); borderPen.setXfermode(avoid);
2.Porter-Duff 效果图:
1.PorterDuff.Mode.CLEAR
所绘制不会提交到画布上。
2.PorterDuff.Mode.SRC
显示上层绘制图片
3.PorterDuff.Mode.DST
显示下层绘制图片
4.PorterDuff.Mode.SRC_OVER
正常绘制显示,上下层绘制叠盖。
5.PorterDuff.Mode.DST_OVER
上下层都显示。下层居上显示。
6.PorterDuff.Mode.SRC_IN
取两层绘制交集。显示上层。
7.PorterDuff.Mode.DST_IN
取两层绘制交集。显示下层。
8.PorterDuff.Mode.SRC_OUT
取上层绘制非交集部分。
9.PorterDuff.Mode.DST_OUT
取下层绘制非交集部分。
10.PorterDuff.Mode.SRC_ATOP
取下层非交集部分与上层交集部分
11.PorterDuff.Mode.DST_ATOP
取上层非交集部分与下层交集部分
12.PorterDuff.Mode.XOR
13.PorterDuff.Mode.DARKEN
14.PorterDuff.Mode.LIGHTEN
15.PorterDuff.Mode.MULTIPLY
16.PorterDuff.Mode.SCREEN
相关文章推荐
- 收集整理 View必须要了解的Android坐标知识
- Android 操作系统你必须了解的知识
- android系统你必须了解的知识 http://bbs.21ic.com/icview-623225-1-1.html
- android中view坐标相关的知识
- 作为 Android 开发者必须了解的 Gradle 知识
- Android中高手必须了解的关于View的细节(进阶必备)
- Android开发者们和设计师们必须了解的颜色知识
- JavaScript 页面坐标相关知识整理
- javascript中的107个基础知识收集整理 推荐
- Android中继承View的研究(二) -- 绘图的基本知识
- android页面Layout知识整理
- Android 分享两个你学习android 平台开发必须碰到的几个知识点的组件【天气预报、日期】View 组件 推荐
- 引用别人的文章:整理出来的一些Android基础知识
- Android新浪微博-项目整理总结 二[使用WebView获取OAuth验证码]
- android.os.handler相关知识整理
- JavaScript 页面坐标相关知识整理
- Android 分享两个你学习android 平台开发必须碰到的几个知识点的组件【天气预报、日期】View 组件
- javascript中的107个基础知识收集整理