Android View measure (五) 支持margin属性,从一个异常说起

来源:互联网 发布:靠谱的网络招聘平台 编辑:程序博客网 时间:2024/06/06 01:03
先来看下代码


一、查看夏目

1. 自定义控件
[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. public class CustomViewGroup extends ViewGroup {  
  2.       
  3.     ......  
  4.   
  5.   
  6.     @Override  
  7.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  8.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  9.           
  10.         // 遍历所有子视图,进行measure操作  
  11.         for (int i = 0; i < getChildCount(); i++) {  
  12.             View child = getChildAt(i);  
  13.             if (child != null && child.getVisibility() != View.GONE) {  
  14.                 measureChild(child, widthMeasureSpec, heightMeasureSpec);  
  15.                   
  16.                 // 支持子视图设置的android:layout_margin属性  
  17.                 MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams();  
  18.                 int marginLeft = layoutParams.leftMargin;  
  19.             }  
  20.         }  
  21.           
  22.         setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);  
  23.     }  
  24.   
  25.   
  26.     @Override  
  27.     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  
  28.         for (int i = 0; i < getChildCount(); i++) {  
  29.             View childView = getChildAt(i);  
  30.             // 只为最简单代码复现BUG,所有子视图都随便放  
  31.             childView.layout(left, top, left + childView.getMeasuredWidth(), top + childView.getMeasuredHeight());  
  32.         }  
  33.     }  
  34. }  

    继承自ViewGroup,主要是演示自定义视图中如何支持margin属性,重点在child.getLayoutParams()一行,接下来看下布局文件中如何使用


2. 布局文件
[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. <com.example.android.apis.CustomViewGroup  
  2.     android:id="@+id/custom_view_group"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="wrap_content" >  
  5.   
  6.   
  7.     <TextView  
  8.         android:layout_width="wrap_content"  
  9.         android:layout_height="wrap_content"  
  10. oid:layout_margin="10dip"  
  11.         android:text="love_world_" />  
  12. </com.example.android.apis.CustomViewGroup>  


重头戏:android:layout_margin="10dip" margin属性


3. 异常
[plain] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. java.lang.ClassCastException: android.view.ViewGroup$LayoutParams cannot be cast to android.view.ViewGroup$MarginLayoutParams  
  2.     at com.example.android.apis.CustomViewGroup.onMeasure(CustomViewGroup.java:50)  
  3.     at android.view.View.measure(View.java:16831)  
  4.     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5245)  
  5.     at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1410)  
  6.     at android.widget.LinearLayout.measureHorizontal(LinearLayout.java:1052)  
  7.     at android.widget.LinearLayout.onMeasure(LinearLayout.java:590)  
  8.     at android.view.View.measure(View.java:16831)  
  9.     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5245)  
  10.     at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1410)  
  11.     at android.widget.LinearLayout.measureVertical(LinearLayout.java:695)  
  12.     at android.widget.LinearLayout.onMeasure(LinearLayout.java:588)  
  13.     at android.view.View.measure(View.java:16831)  
  14.     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5245)  
  15.     at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)  
  16.     at android.view.View.measure(View.java:16831)  
  17.     at android.widget.LinearLayout.measureVertical(LinearLayout.java:847)  
  18.     at android.widget.LinearLayout.onMeasure(LinearLayout.java:588)  
  19.     at android.view.View.measure(View.java:16831)  
  20.     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5245)  
  21.     at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)  
  22.     at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2586)  
  23.     at android.view.View.measure(View.java:16831)  
  24.     at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2189)  
  25.     at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1352)  
  26.     at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1535)  
  27.     at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1249)  
  28.     at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6364)  
  29.     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:791)  
  30.     at android.view.Choreographer.doCallbacks(Choreographer.java:591)  
  31.     at android.view.Choreographer.doFrame(Choreographer.java:561)  
  32.     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:777)  
  33.     at android.os.Handler.handleCallback(Handler.java:730)  
  34.     at android.os.Handler.dispatchMessage(Handler.java:92)  
  35.     at android.os.Looper.loop(Looper.java:176)  
  36.     at android.app.ActivityThread.main(ActivityThread.java:5419)  
  37.     at java.lang.reflect.Method.invokeNative(Native Method)  
  38.     at java.lang.reflect.Method.invoke(Method.java:525)  
  39.     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1209)  
  40.     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1025)  
  41.     at dalvik.system.NativeStart.main(Native Method)  






出现以上异常的原因,LayoutParams从哪里来的?
视图的加载有两种方式一种是代码addView 一种是inflate 。

1. inflate 方法加载并添加LayoutParams
Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析(上)




2. 以下演示addView方式添加LayoutParams


[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. public class MainActivity extends Activity {  
  2.   
  3.   
  4.     @Override  
  5.     protected void onCreate(Bundle savedInstanceState) {  
  6.         super.onCreate(savedInstanceState);  
  7.         setContentView(R.layout.activity_main);  
  8.           
  9.         CustomViewGroup customViewGroup = (CustomViewGroup) findViewById(R.id.custom_view_group);  
  10.           
  11.         customViewGroup.addView(createTextView("Love_world_"));  
  12.           
  13.     }  
  14.   
  15.   
  16.     private View createTextView(String value) {  
  17.         TextView textView = new TextView(this);    
  18.         textView.setText("a child view");  
  19.         textView.setText(value);  
  20.         textView.setLayoutParams(new ViewGroup.MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));  
  21.         return textView;  
  22.     }  
  23.   
  24.   
  25. }     




2. ViewGroup.addView源码,查找何处添加LayoutParams

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. public abstract class ViewGroup extends View implements ViewParent, ViewManager {  
  2.   
  3.   
  4.     public void addView(View child) {  
  5.         addView(child, -1);  
  6.     }  
  7.   
  8.   
  9.     public void addView(View child, int index) {  
  10.         LayoutParams params = child.getLayoutParams();  
  11.         // 子视图LayoutParams为为空是处理方式  
  12.         if (params == null) {  
  13.             params = generateDefaultLayoutParams();  
  14.             if (params == null) {  
  15.                 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");  
  16.             }  
  17.         }  
  18.         addView(child, index, params);  
  19.     }  
  20.   
  21.   
  22.     public void addView(View child, int width, int height) {  
  23.         final LayoutParams params = generateDefaultLayoutParams();  
  24.         params.width = width;  
  25.         params.height = height;  
  26.         addView(child, -1, params);  
  27.     }  
  28.   
  29.   
  30.     public void addView(View child, LayoutParams params) {  
  31.         addView(child, -1, params);  
  32.     }  
  33.   
  34.   
  35.     public void addView(View child, int index, LayoutParams params) {  
  36.         if (DBG) {  
  37.             System.out.println(this + " addView");  
  38.         }  
  39.   
  40.   
  41.         // addViewInner() will call child.requestLayout() when setting the new LayoutParams  
  42.         // therefore, we call requestLayout() on ourselves before, so that the child's request  
  43.         // will be blocked at our level  
  44.         requestLayout();  
  45.         invalidate();  
  46.         addViewInner(child, index, params, false);  
  47.     }  
  48.   
  49.   
  50.     // 子视图默认LayoutParams实例  
  51.     protected LayoutParams generateDefaultLayoutParams() {  
  52.         return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);  
  53.     }  
  54.   
  55.   
  56.     // 定义LayoutParams类  
  57.     public static class LayoutParams {  
  58.         public int width;  
  59.         public int height;  
  60.                   
  61.         public LayoutParams(int width, int height) {  
  62.             this.width = width;  
  63.             this.height = height;  
  64.         }  
  65.     }  
  66.       
  67. }  


以下是关键

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. public abstract class ViewGroup extends View implements ViewParent, ViewManager {  
  2.     private void addViewInner(View child, int index, LayoutParams params,  
  3.             boolean preventRequestLayout) {  
  4.   
  5.   
  6.         if (child.getParent() != null) {  
  7.             throw new IllegalStateException("The specified child already has a parent. " +  
  8.                     "You must call removeView() on the child's parent first.");  
  9.         }  
  10.   
  11.   
  12.         if (!checkLayoutParams(params)) {  
  13.             params = generateLayoutParams(params);  
  14.         }  
  15.   
  16.   
  17.         if (preventRequestLayout) {  
  18.             child.mLayoutParams = params;  
  19.         } else {  
  20.             child.setLayoutParams(params);  
  21.         }  
  22.   
  23.   
  24.         ......  
  25.     }  
  26.   
  27.   
  28.     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {  
  29.         return  p != null;  
  30.     }  
  31. }  


   通过查看以上addView添加LayoutParams代码可以发现解决方案,复写这些函数,创建当前自定义视图的LayoutParams继承自MarginLayoutParams。


[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. public class CustomViewGroup extends ViewGroup {  
  2.   
  3.   
  4.     @Override  
  5.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  6.         ......  
  7.     }  
  8.       
  9.     @Override  
  10.     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  
  11.         ......    
  12.     }  
  13.   
  14.   
  15.     @Override  
  16.     protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {  
  17.         return new LayoutParams(p);  
  18.     }  
  19.       
  20.     @Override  
  21.     protected LayoutParams generateDefaultLayoutParams() {  
  22.         return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);  
  23.     }      
  24.       
  25.     @Override  
  26.     public LayoutParams generateLayoutParams(AttributeSet attrs) {  
  27.         return new LayoutParams(getContext(), attrs);  
  28.     }      
  29.   
  30.   
  31.     // 继承自margin,支持子视图android:layout_margin属性  
  32.     public static class LayoutParams extends MarginLayoutParams {  
  33.   
  34.   
  35.         public LayoutParams(Context c, AttributeSet attrs) {  
  36.             super(c, attrs);  
  37.         }  
  38.   
  39.   
  40.         public LayoutParams(int width, int height) {  
  41.             super(width, height);  
  42.         }  
  43.   
  44.   
  45.         public LayoutParams(ViewGroup.LayoutParams source) {  
  46.             super(source);  
  47.         }  
  48.   
  49.   
  50.         public LayoutParams(ViewGroup.MarginLayoutParams source) {  
  51.             super(source);  
  52.         }  
  53.     }  
  54. }    
0 0
原创粉丝点击