- 除非你总是需要一个100×100像素的控件,否则,你必须要重写onMeasure。
- onMeasure方法在控件的父元素正要放置它的子控件时调用。它会问一个问题,“你想要用多大地方啊?”,然后传入两个参数——widthMeasureSpec和heightMeasureSpec。
- 它们指明控件可获得的空间以及关于这个空间描述的元数据。
- 比返回一个结果要好的方法是你传递View的高度和宽度到setMeasuredDimension方法里。
- 接下来的代码片段给出了如何重写onMeasure。注意,调用的本地空方法是来计算高度和宽度的。它们会译解widthHeightSpec和heightMeasureSpec值,并计算出合适的高度和宽度值。
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int measuredHeight = measureHeight(heightMeasureSpec);
- int measuredWidth = measureWidth(widthMeasureSpec);
- setMeasuredDimension(measuredHeight, measuredWidth);
- }
- private int measureHeight(int measureSpec) {
- // Return measured widget height.
- }
- private int measureWidth(int measureSpec) {
- // Return measured widget width.
- }
- 边界参数——widthMeasureSpec和heightMeasureSpec ,效率的原因以整数的方式传入。在它们使用之前,首先要做的是使用MeasureSpec类的静态方法getMode和getSize来译解,如下面的片段所示:
- int specMode = MeasureSpec.getMode(measureSpec);
- int specSize = MeasureSpec.getSize(measureSpec);
- 依据specMode的值,如果是AT_MOST,specSize 代表的是最大可获得的空间;如果是EXACTLY,specSize 代表的是精确的尺寸;如果是UNSPECIFIED,对于控件尺寸来说,没有任何参考意义。
- 当以EXACT方式标记测量尺寸,父元素会坚持在一个指定的精确尺寸区域放置View。在父元素问子元素要多大空间时,AT_MOST指示者会说给我最大的范围。在很多情况下,你得到的值都是相同的。
- 在两种情况下,你必须绝对的处理这些限制。在一些情况下,它可能会返回超出这些限制的尺寸,在这种情况下,你可以让父元素选择如何对待超出的View,使用裁剪还是滚动等技术。
- 接下来的框架代码给出了处理View测量的典型实现:
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int measuredHeight = measureHeight(heightMeasureSpec);
- int measuredWidth = measureWidth(widthMeasureSpec);
- setMeasuredDimension(measuredHeight, measuredWidth);
- }
- private int measureHeight(int measureSpec) {
- int specMode = MeasureSpec.getMode(measureSpec);
- int specSize = MeasureSpec.getSize(measureSpec);
- // Default size if no limits are specified.
- int result = 500;
- if (specMode == MeasureSpec.AT_MOST)
- {
- // Calculate the ideal size of your
- // control within this maximum size.
- // If your control fills the available
- // space return the outer bound.
- result = specSize;
- }
- else if (specMode == MeasureSpec.EXACTLY)
- {
- // If your control can fit within these bounds return that value.
- result = specSize;
- }
- return result;
- }
- private int measureWidth(int measureSpec) {
- int specMode = MeasureSpec.getMode(measureSpec);
- int specSize = MeasureSpec.getSize(measureSpec);
- // Default size if no limits are specified.
- int result = 500;
- if (specMode == MeasureSpec.AT_MOST)
- {
- // Calculate the ideal size of your control
- // within this maximum size.
- // If your control fills the available space
- // return the outer bound.
- result = specSize;
- }
- else if (specMode == MeasureSpec.EXACTLY)
- {
- // If your control can fit within these bounds return that value.
- result = specSize;
- }
- return result;
- }
除非你总是需要一个100×100像素的控件,否则,你必须要重写onMeasure。 onMeasure方法在控件的父元素正要放置它的子控件时调用。它会问一个问题,“你想要用多大地方啊?”,然后传入两个参数——widthMeasureSpec和heightMeasureSpec。 它们指明控件可获得的空间以及关于这个空间描述的元数据。 比返回一个结果要好的方法是你传递View的高度和宽度到setMeasuredDimension方法里。 接下来的代码片段给出了如何重写onMeasure。注意,调用的本地空方法是来计算高度和宽度的。它们会译解widthHeightSpec和heightMeasureSpec值,并计算出合适的高度和宽度值。 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int measuredHeight = measureHeight(heightMeasureSpec); int measuredWidth = measureWidth(widthMeasureSpec); setMeasuredDimension(measuredHeight, measuredWidth); } private int measureHeight(int measureSpec) { // Return measured widget height. } private int measureWidth(int measureSpec) { // Return measured widget width. } 边界参数——widthMeasureSpec和heightMeasureSpec ,效率的原因以整数的方式传入。在它们使用之前,首先要做的是使用MeasureSpec类的静态方法getMode和getSize来译解,如下面的片段所示: int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); 依据specMode的值,如果是AT_MOST,specSize 代表的是最大可获得的空间;如果是EXACTLY,specSize 代表的是精确的尺寸;如果是UNSPECIFIED,对于控件尺寸来说,没有任何参考意义。 当以EXACT方式标记测量尺寸,父元素会坚持在一个指定的精确尺寸区域放置View。在父元素问子元素要多大空间时,AT_MOST指示者会说给我最大的范围。在很多情况下,你得到的值都是相同的。 在两种情况下,你必须绝对的处理这些限制。在一些情况下,它可能会返回超出这些限制的尺寸,在这种情况下,你可以让父元素选择如何对待超出的View,使用裁剪还是滚动等技术。 接下来的框架代码给出了处理View测量的典型实现: @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int measuredHeight = measureHeight(heightMeasureSpec); int measuredWidth = measureWidth(widthMeasureSpec); setMeasuredDimension(measuredHeight, measuredWidth); } private int measureHeight(int measureSpec) { int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); // Default size if no limits are specified. int result = 500; if (specMode == MeasureSpec.AT_MOST) { // Calculate the ideal size of your // control within this maximum size. // If your control fills the available // space return the outer bound. result = specSize; } else if (specMode == MeasureSpec.EXACTLY) { // If your control can fit within these bounds return that value. result = specSize; } return result; } private int measureWidth(int measureSpec) { int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); // Default size if no limits are specified. int result = 500; if (specMode == MeasureSpec.AT_MOST) { // Calculate the ideal size of your control // within this maximum size. // If your control fills the available space // return the outer bound. result = specSize; } else if (specMode == MeasureSpec.EXACTLY) { // If your control can fit within these bounds return that value. result = specSize; } return result; }
getWidth得到是某个view的实际尺寸.
getMeasuredWidth是得到某view想要在parent view里面占的大小.
- getWidth在OnCreat的时候得到的是0. 当一个view对象创建时,android并不知道其大小,所以getWidth()和 getHeight()返回的结果是0,真正大小是在计算布局时才会计算.
2. getMeasuredWidth必须在parent view或者它自己调用measure()函数之后才能得到. measure函数就是计算该函数需要占用的空间大小.
3.onMeasure会在onLayout之前调用
1 public class MyViewGroup extends ViewGroup { 2 private final static String TAG = "MyViewGroup"; 3 4 private final static int VIEW_MARGIN=2; 5 6 public MyViewGroup(Context context) { 7 super(context); 8 } 9 @Override 10 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 11 Log.d(TAG, "widthMeasureSpec = "+widthMeasureSpec+" heightMeasureSpec"+heightMeasureSpec); 12 13 for (int index = 0; index < getChildCount(); index++) { 14 final View child = getChildAt(index); 15 // measure16 child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); 17 } 18 19 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 20 } 21 22 @Override 23 protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) { 24 Log.d(TAG, "changed = "+arg0+" left = "+arg1+" top = "+arg2+" right = "+arg3+" botom = "+arg4); 25 final int count = getChildCount(); 26 int row=0;// which row lay you view relative to parent27 int lengthX=arg1; // right position of child relative to parent28 int lengthY=arg2; // bottom position of child relative to parent29 for(int i=0;i<count;i++){ 30 31 final View child = this.getChildAt(i); 32 int width = child.getMeasuredWidth(); 33 int height = child.getMeasuredHeight(); 34 lengthX+=width+VIEW_MARGIN; 35 lengthY=row*(height+VIEW_MARGIN)+VIEW_MARGIN+height+arg2; 36 //if it can't drawing on a same line , skip to next line37 if(lengthX>arg3){ 38 lengthX=width+VIEW_MARGIN+arg1; 39 row++; 40 lengthY=row*(height+VIEW_MARGIN)+VIEW_MARGIN+height+arg2; 41 42 } 43 44 child.layout(lengthX-width, lengthY-height, lengthX, lengthY); 45 } 46 47 } 48 49 }
这里有个地方要注意,那就要明白ViewGroup的绘图流程:ViewGroup绘制包括两个步骤:1.measure 2.layout
在两个步骤中分别调用回调函数:1.onMeasure() 2.onLayout()
1.onMeasure() 在这个函数中,ViewGroup会接受childView的请求的大小,然后通过childView的 measure(newWidthMeasureSpec, heightMeasureSpec)函数存储到childView中,以便childView的getMeasuredWidth() andgetMeasuredHeight() 的值可以被后续工作得到。
2.onLayout() 在这个函数中,ViewGroup会拿到childView的getMeasuredWidth() andgetMeasuredHeight(),用来布局所有的childView。
3.View.MeasureSpec 与 LayoutParams 这两个类,是ViewGroup与childView协商大小用的。其中,View.MeasureSpec是ViewGroup用来部署 childView用的, LayoutParams是childView告诉ViewGroup 我需要多大的地方。
4.在View 的onMeasure的最后要调用setMeasuredDimension()这个方法存储View的大小,这个方法决定了当前View的大小。
效果图:
我们都知道在onCreate()里面获取控件的高度是0,这是为什么呢?我们来看一下示例:
首先我们自己写一个控件,这个控件非常简单:
[java]
public class MyImageView extends ImageView {
public MyImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyImageView(Context context) {
super(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
System.out.println("onMeasure 我被调用了"+System.currentTimeMillis());
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
System.out.println("onDraw 我被调用了"+System.currentTimeMillis());
}
}
布局文件:
[java]
<com.test.MyImageView
android:id="@+id/imageview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/test" />
测试的Activity的onCreate():
[java]
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
System.out.println("执行完毕.."+System.currentTimeMillis());
}
现在我们现在来看一下结果:
说明等onCreate方法执行完了,我们定义的控件才会被度量(measure),所以我们在onCreate方法里面通过view.getHeight()获取控件的高度或者宽度肯定是0,因为它自己还没有被度量,也就是说他自己都不知道自己有多高,而你这时候去获取它的尺寸,肯定是不行的.
现在碰到这个问题我们不能不解决,在网上找到了如下办法:
[java]
//------------------------------------------------方法一
int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
int h = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
imageView.measure(w, h);
int height =imageView.getMeasuredHeight();
int width =imageView.getMeasuredWidth();
textView.append("\n"+height+","+width);
//-----------------------------------------------方法二
ViewTreeObserver vto = imageView.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
int height = imageView.getMeasuredHeight();
int width = imageView.getMeasuredWidth();
textView.append("\n"+height+","+width);
return true;
}
});
//-----------------------------------------------方法三
ViewTreeObserver vto2 = imageView.getViewTreeObserver();
vto2.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
textView.append("\n\n"+imageView.getHeight()+","+imageView.getWidth());
}
});
这三个方法是哪里找到现在已经忘了.
现在要讨论的是当我们需要时候使用哪个方法呢?
现在把测试的Activity改成如下:
[java]
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final ImageView imageView = (ImageView) findViewById(R.id.imageview);
//------------------------------------------------方法一
int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
int h = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
imageView.measure(w, h);
int height =imageView.getMeasuredHeight();
int width =imageView.getMeasuredWidth();
textView.append("\n"+height+","+width);
System.out.println("执行完毕.."+System.currentTimeMillis());
}
接着来看下面几种方式输出结果:
把测试Activity改成如下:
[java]
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final ImageView imageView = (ImageView) findViewById(R.id.imageview);
-----------------------------------------------方法二
ViewTreeObserver vto = imageView.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
int height = imageView.getMeasuredHeight();
int width = imageView.getMeasuredWidth();
textView.append("\n"+height+","+width);
return true;
}
});
}
结果如下:
方法三就不再测试了同方法二!!!
那么方法而和方法三在执行上有什么区别呢?
我们在布局文件中加入一个TextView来记录这个控件的宽高.
[java]
<ScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</ScrollView>
先来测试方法而:
[java]
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final ImageView imageView = (ImageView) findViewById(R.id.imageview);
-----------------------------------------------方法二
ViewTreeObserver vto = imageView.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
int height = imageView.getMeasuredHeight();
int width = imageView.getMeasuredWidth();
textView.append("\n"+height+","+width);
return true;
}
});
}
结果如下:
我们再来测试方法三
[java]
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final ImageView imageView = (ImageView) findViewById(R.id.imageview);
//-----------------------------------------------方法三
ViewTreeObserver vto2 = imageView.getViewTreeObserver();
vto2.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
textView.append("\n\n"+imageView.getHeight()+","+imageView.getWidth());
}
});
}
输出结果如下:
我想这方法二和方法三之间的区别就不用说了吧.
总结:那么需要获取控件的宽高该用那个方法呢?
方法一: 比其他的两个方法多了一次计算,也就是多调用了一次onMeasure()方法,该方法虽然看上去简单,但是如果要目标控件计算耗时比较大的话,不见时使用,如listView等.
方法二,它的回调方法会调用很多次,并且滑动TextView的时候任然会调用,所以不建议使用.
方法三,比较合适.
当然,实际应用的时候需要根据实际情况而定.
相关推荐
这篇博客文章“Android自定义ViewGroup(一)”详细介绍了如何从零开始创建一个自定义的ViewGroup,以及在这个过程中可能遇到的问题和解决策略。下面将深入探讨这个主题。 首先,我们来了解ViewGroup的基本概念。...
瀑布流布局,也被称为...但总体来说,自定义ViewGroup实现瀑布流需要理解Android布局系统,熟悉测量和布局过程,以及掌握图片加载和内存管理策略。在实际项目中,这将是一个锻炼和提升Android开发技能的好机会。
在Android开发中,自定义ViewGroup是实现复杂布局和交互的关键技术之一。本实战篇将聚焦于如何实现一个名为FlowLayout的自定义布局,它允许子视图按行排列,类似于HTML中的`<div>`标签。在Android应用设计中,...
本文将对如何在Android中创建和使用自定义ViewGroup进行总结,并提供一个具体的实例来演示如何为Child View设置属性。 首先,我们要了解自定义ViewGroup的两大类别: 1. 创建全新的ViewGroup类型:这通常涉及构建...
在Android开发中,自定义ViewGroup是实现特定布局需求的重要手段。本文主要讲解如何自定义一个FlowLayout,这种布局方式能够适应各种需要动态排列元素的场景,例如关键字标签和搜索热词列表。在Android SDK中并未...
在Android开发中,自定义ViewGroup是实现复杂交互和界面设计的重要手段,它允许开发者根据需求定制特定的布局和交互行为。"实现侧滑上下滑自定义ViewGroup"的主题涉及了Android开发中的几个核心概念,包括自定义View...
在Android开发中,自定义ViewGroup是实现复杂布局和交互的关键技术之一。本文将深入探讨如何创建一个自定义的ViewGroup,并在其内部显示两个TextView。我们将详细讲解自定义ViewGroup的过程,包括布局设计、测量、...
自定义ViewGroup时,我们通常会继承自`android.view.ViewGroup`,并重写几个关键方法,如`onMeasure()`、`onLayout()`和`onDraw()`。 在FlowLayout的实现中,`onMeasure()`方法用来测量每个子视图的大小,并确定...
总之,Android自定义ViewGroup是实现独特布局和交互的关键,通过重写测量、布局和触摸事件处理方法,我们可以创建出满足各种需求的自定义视图组件。结合滑动功能,可以让用户界面更加生动和互动。通过对`...
总结,本项目“android自定义View-手绘地图”涵盖了Android自定义组件的核心技术,包括自定义View和自定义ViewGroup的使用,通过它们可以构建出一个可定制、可交互的地图视图。开发者可以根据实际需求调整绘制逻辑,...
这个过程涉及到了Android自定义View的基本原理,包括绘图、触摸事件处理、动画和布局管理等。了解并掌握这些知识,对于提升Android应用的用户体验具有重要意义。想要了解更多关于Android自定义View的内容,可以访问...
在Android开发中,自定义ViewGroup是实现复杂布局和交互效果的重要手段。`ZhyCustomViewgroup02`项目很可能是某位开发者分享的一个关于自定义ViewGroup的示例,适用于Android Studio。在这个项目中,我们可以学习到...
在Android开发中,自定义ViewGroup是实现复杂布局和交互效果的重要手段。它允许开发者根据需求创建具有独特功能和外观的视图容器。本篇将深入探讨如何利用谷歌官方的自定义ViewGroup实例来理解这一关键概念。 首先...
总之,理解并掌握Android自定义View的事件分发机制对于优化用户界面和提升应用交互体验至关重要。开发者需要熟练运用 `dispatchTouchEvent()`, `onInterceptTouchEvent()`, 和 `onTouchEvent()` 这些方法,以及了解...
在自定义ViewGroup时,我们需要重写`onMeasure()`方法来确定子View的大小。这个方法需要调用`measureChild()`或`measureChildren()`方法,对每个子View进行测量,并设置合适的尺寸。同时,要调用`...
在Android开发中,自定义ViewGroup是实现个性化界面和复杂交互的重要手段。"自定义ViewGroup仿ViewPager"这个主题涉及到滚动机制,包括`scrollTo`、`scrollBy`以及`Scroller`这三个关键知识点,这些都是Android视图...
自定义View首先要创建一个新的Java类,继承自Android提供的View或ViewGroup类。常见的基类选择有View(用于单个UI元素)和LinearLayout、RelativeLayout等(用于容器,管理多个子视图)。在新类中,我们通常需要重写...
通过这个项目,开发者不仅可以学习到自定义View的技巧,还能深入理解Android图形绘制和动画系统的工作原理,这对于提升Android开发能力非常有帮助。同时,这也是一个有趣的实践,能够锻炼开发者解决问题和创新设计的...
在Android开发中,自定义ViewGroup是一个非常重要的技能,它涉及到UI组件的底层实现和性能优化。本案例将深入解析自定义ViewGroup的过程,帮助开发者理解与自定义View的区别,并提供实践操作的经验。 首先,我们要...
在Android开发中,自定义ViewGroup是实现复杂布局和交互效果的重要手段。本主题将深入探讨如何通过自定义ViewGroup来实现类似ViewPager的滑动效果。ViewPager是一个强大的组件,它允许用户通过左右滑动来浏览多个...