`

自定义ViewGroup

 
阅读更多

 

一、个人理解,容器的布局执行过程

 *  1 父容器调用measureChildren 

 *  2 子容器调用measure 计算当前容器的宽高和其子容器的宽高

 *  3 当前执行的这个子容器触发onMeasure ,设置当前子容器的宽高 

 *  4 当前容器调用onLayout , 来展示当前容器的显示 

 

 

二、自定义布局:

 1 ViewGroup职责为:计算自己的尺寸,并给childView计算出建议的宽高和测量模式 

 2  childView:根据父类传递的测量模式和建议宽高,计算自己的尺寸,draw自己

 

 当childView设置其宽高为精确值或者wrap_content(不确定值),

   onMeasur

   ViewGroup会传测量模式为EXACTLY宽高哪一个模式为EXACTLY则其值就固定为ViewGroup传过来的值,否则为计算值 

 

 

三、自定义容器中方法的功能

onMeasure方法:

  1 得出当前ViewGroup的宽高,和模式 

  2 如果宽或高的哪一个模式为MeasureSpec.EXACTLY ,则直接赋值

  3 如果宽或高哪一个模式不是EXACTLY,则根据计算子类的宽高和Margin

    来计算容器的宽或者高 

  4 调用setMeasuredDimension,设置容器宽和高

 

onLayout:

    根据子控件的宽高和各种margin计算子控件的坐标,完成子控件定位布局 

 

四、三种模式

EXACTLY:表示设置了精确的值,一般当childView设置其宽、高为精确值、match_parent时,ViewGroup会将其设置为EXACTLY;

AT_MOST:表示子布局被限制在一个最大值内,一般当childView设置其宽、高为wrap_content时,ViewGroup会将其设置为AT_MOST;

UNSPECIFIED:表示子布局想要多大就多大,一般出现在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此种模式比较少见。

  

五、测试代码

  1) 自定义容器

/**
 * 个人理解,容器的布局执行过程
 *  1 父容器调用measureChildren 
 *  2 子容器调用measure 计算当前容器的宽高和其子容器的宽高
 *  3 当前执行的这个子容器触发onMeasure ,设置当前子容器的宽高 
 *  4 当前容器调用onLayout , 来展示当前容器的显示 
 */
public class CustomImgContainer extends ViewGroup {

	public CustomImgContainer(Context context) {
		super(context);
	}
	
	//attrs 是xml中配置的参数会传到这里
	public CustomImgContainer(Context context, AttributeSet attrs) {
		super(context, attrs);
	}
	
	@Override  
    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs)  
    {  
        return new MarginLayoutParams(getContext(), attrs);  
    }  
	
	/**
	 * 
	 * 根据父类传入的 widthMeasureSpec和heightMeasureSpec计算本类和其子类的宽高
	 */
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		//这里得到的宽高单位是像素,但xml中配置的单位都是dp,所以数值可能不一样 
		//1 容器宽和高的模式   2 容器宽和高 
		 int widthMode = MeasureSpec.getMode( widthMeasureSpec); 
		 int heightMode = MeasureSpec.getMode( heightMeasureSpec);
		 int sizeWidth = MeasureSpec.getSize(widthMeasureSpec) ; 
		 int sizeHeight = MeasureSpec.getSize( heightMeasureSpec) ;  
		 //根据本容器传的两个参数,子控件计会调用measure和onMeasure方法来计算子控件的宽高
		 measureChildren(widthMeasureSpec, heightMeasureSpec);	
		 /**
		  * 如果wrap_content是设置的宽和高 
		  */
		 int width = 0 ;
		 int height  = 0 ; 
		 
		 int count = getChildCount();
		 int cWidth = 0;  
         int cHeight = 0;  
         MarginLayoutParams cParams = null;  
  
         // 用于计算左边两个childView的高度  
         int lHeight = 0;  
         // 用于计算右边两个childView的高度,最终高度取二者之间大值  
         int rHeight = 0;  
  
         // 用于计算上边两个childView的宽度  
         int tWidth = 0;  
         // 用于计算下面两个childiew的宽度,最终宽度取二者之间大值  
         int bWidth = 0;  
         for(int i=0;i<count;i++ ){
        	 
        	  View view  = getChildAt(i) ;
        	  cWidth = view.getMeasuredWidth();
        	  cHeight = view.getMeasuredHeight();
        	  cParams = (MarginLayoutParams) view.getLayoutParams() ; 

              // 上面两个childView  
              if (i == 0 || i == 1)  
              {  
                  tWidth += cWidth + cParams.leftMargin + cParams.rightMargin;  
              }  
    
              if (i == 2 || i == 3)  
              {  
                  bWidth += cWidth + cParams.leftMargin + cParams.rightMargin;  
              }  
    
              if (i == 0 || i == 2)  
              {  
                  lHeight += cHeight + cParams.topMargin + cParams.bottomMargin;  
              }  
    
              if (i == 1 || i == 3)  
              {  
                  rHeight += cHeight + cParams.topMargin + cParams.bottomMargin;  
              }  
         }
         width = Math.max(tWidth, bWidth);  
         height = Math.max(lHeight, rHeight);  
   
         /** 
          * 如果是wrap_content设置为我们计算的值 ,这个必须设置 
          * 否则:直接设置为父容器计算的值 
          * 
          * 计算:如宽度传进来的模式为EXACTLY(容器具体数值或者fill_parent) ,则容器宽度就用当前传进来的数值
          *      如模式不是EXACTLY,则容器宽度需要重新计算,本例子中宽度为上面两个控件宽度和或者下面两个控件宽度和中最大的 
          *     
          *      高度计算同上
          */  
         setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth  
                 : width, (heightMode == MeasureSpec.EXACTLY) ? sizeHeight  
                 : height);        
		if(widthMode == MeasureSpec.EXACTLY){
			System.out.println( "EXACTLY");
		}else{
			System.out.println( "not EXACTLY");
		}
		
		if(heightMode == MeasureSpec.EXACTLY){
			System.out.println( "EXACTLY");
		}else{
			System.out.println( "not EXACTLY");
		}
	}
	
	/**
	 * 对其所有childView进行定位
	 */
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		 int cCount = getChildCount();  
         int cWidth = 0;  
         int cHeight = 0;  
         MarginLayoutParams cParams = null; 
         /** 
          * 遍历所有childView根据其宽和高,以及margin进行布局 
          */  
         for (int i = 0; i < cCount; i++)  
         {  
             View childView = getChildAt(i);  
             cWidth =  childView.getMeasuredWidth();  
             cHeight = childView.getMeasuredHeight();  
             cParams = (MarginLayoutParams) childView.getLayoutParams();  
   
             int cl = 0, ct = 0, cr = 0, cb = 0;  
   
             switch (i)  
             {  
             case 0:  
                 cl = cParams.leftMargin;  
                 ct = cParams.topMargin;  
                 break;  
             case 1:  
                 cl = getWidth() - cWidth - cParams.leftMargin  
                         - cParams.rightMargin;  
                 ct = cParams.topMargin;  
   
                 break;  
             case 2:  
                 cl = cParams.leftMargin;  
                 ct = getHeight() - cHeight - cParams.bottomMargin;  
                 break;  
             case 3:  
                 cl = getWidth() - cWidth - cParams.leftMargin  
                         - cParams.rightMargin;  
                 ct = getHeight() - cHeight - cParams.bottomMargin;  
                 break;  
   
             }  
             cr = cl + cWidth;  
             cb = cHeight + ct;  
             //根据容器和子控件的尺寸计算出各个控件的具体位置
             childView.layout(cl, ct, cr, cb);  
         }  
	}
}

 2) 所用的xml ,测试的时候容器的宽和高用固定宽高、fill_parent/wrap_content 分别进行测试 

   

<?xml version="1.0" encoding="utf-8"?>
<com.example.fragmentdemo1.widget.CustomImgContainer xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:tools="http://schemas.android.com/tools"  
    android:layout_width="fill_parent"  
    android:layout_height="wrap_content"  
    android:background="#AA333333" >  
    
    <TextView  
        android:layout_width="50dp"  
        android:layout_height="50dp"  
        android:background="#FF4444"  
        android:gravity="center"  
        android:text="0"  
        android:textColor="#FFFFFF"  
        android:textSize="22sp"  
        android:textStyle="bold" />  
  
    <TextView  
        android:layout_width="50dp"  
        android:layout_height="50dp"  
        android:background="#00ff00"  
        android:gravity="center"  
        android:text="1"  
        android:textColor="#FFFFFF"  
        android:textSize="22sp"  
        android:textStyle="bold" />  
  
    <TextView  
        android:layout_width="50dp"  
        android:layout_height="50dp"  
        android:background="#ff0000"  
        android:gravity="center"  
        android:text="2"  
        android:textColor="#FFFFFF"  
        android:textSize="22sp"  
        android:textStyle="bold" />  
  
    <TextView  
        android:layout_width="50dp"  
        android:layout_height="50dp"  
        android:background="#0000ff"  
        android:gravity="center"  
        android:text="3"  
        android:textColor="#FFFFFF"  
        android:textSize="22sp"  
        android:textStyle="bold" />  
  
</com.example.fragmentdemo1.widget.CustomImgContainer>  

 3)Activity 的使用

  

public class CustomContainActivity extends Activity implements OnClickListener {
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_custom_contain );
		//DisplayUtil displayUtil = new DisplayUtil(getWindowManager());
	}
	@Override
	public void onClick(View v) {

		switch (v.getId()) {
		case R.id.start:
		 
			break;
		default:
			break;
		}
	}
}

 

 

 参考: http://blog.csdn.net/luohai859/article/details/38435827

 

 

 

 

分享到:
评论

相关推荐

    自定义ViewGroup 显示一个TextView到ViewGroup

    在Android开发中,自定义ViewGroup是实现复杂布局和交互效果的重要手段。本文将深入探讨如何创建一个自定义的ViewGroup,并将一个TextView显示在其中。我们将基于标题“自定义ViewGroup 显示一个TextView到ViewGroup...

    Android 手把手教您自定义ViewGroup(一)

    在Android开发中,自定义ViewGroup是提升应用界面复杂度和交互体验的重要手段。本教程将引导您逐步了解如何从零开始创建一个自定义的ViewGroup。我们将通过分析标题"Android 手把手教您自定义ViewGroup(一)"以及...

    自定义ViewGroup组合控件

    在Android开发中,自定义ViewGroup是创建复杂布局和交互式控件的重要手段。自定义ViewGroup意味着扩展Android的 ViewGroup 类或其子类,如 LinearLayout、RelativeLayout 或 FrameLayout,以实现特定的功能或视觉...

    android 自动换行的自定义viewgroup

    在Android开发中,自定义ViewGroup是实现复杂布局和交互的关键技术之一。标题"android 自动换行的自定义viewgroup"所指的,就是创建一个能够根据子视图的数量和大小自动调整布局,实现自动换行效果的自定义 ...

    自定义ViewGroup 显示两个TextView

    在Android开发中,自定义ViewGroup是实现复杂布局和交互的关键技术之一。本文将深入探讨如何创建一个自定义的ViewGroup,并在其内部显示两个TextView。我们将详细讲解自定义ViewGroup的过程,包括布局设计、测量、...

    自定义VIewGroup实现仿淘宝商品详情页

    在Android开发中,自定义ViewGroup是实现复杂布局和交互效果的重要手段。本教程将深入讲解如何通过自定义ViewGroup来实现类似淘宝商品详情页的滚动效果,即当页面滑动到底部时出现粘滞效果,继续上拉则切换至下一页...

    自定义ViewGroup实现滑屏等动作

    在Android开发中,自定义ViewGroup是实现复杂交互和独特界面设计的重要手段。这篇博客“自定义ViewGroup实现滑屏等动作”显然探讨了如何利用自定义ViewGroup来实现滑动和其他动态效果,这对于增强用户体验和创新应用...

    自定义ViewGroup实现瀑布流

    在Android开发中,自定义ViewGroup来实现瀑布流是一项技术挑战,但也是实现个性化布局的关键。 首先,我们需要理解Android中的ViewGroup。ViewGroup是所有布局容器的基类,如LinearLayout、RelativeLayout等。...

    自定义ViewGroup实现拖动跟快速滚动的效果实例

    在Android开发中,自定义ViewGroup是实现独特交互效果的重要手段。本实例主要探讨如何创建一个自定义的ViewGroup,实现拖动跟快速滚动的效果,让用户体验更加流畅和直观。以下将详细介绍这一过程。 首先,我们需要...

    Android代码-自定义ViewGroup

    自定义ViewGroup(高度不同的列表) MIT License Copyright (c) 2017 JiaoZhengXiang Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated ...

    自定义ViewGroup垂直水平滑动解决ViewPager冲突

    在Android开发中,自定义ViewGroup是扩展UI组件能力的重要方式。ViewGroup作为一个容器,可以包含多个子View,并管理它们的布局和交互。本话题主要探讨如何创建一个自定义的ViewGroup,使其既能支持垂直滑动,又能...

    Android自定义ViewGroup实现Title自动隐藏功能.rar

    今天看到很多app都有Title自动隐藏功能,1.这是一个Gradle工程,Gradle的版本是2.10,项目的Compile sdk Version是 23 , Build ...Android自定义ViewGroup实现Title自动隐藏功能源代码分享,Android开发者必看示例。

    Android中使用自定义ViewGroup的总结

    在Android开发中,自定义ViewGroup是实现复杂布局或独特交互效果的重要手段。本文将对如何在Android中创建和使用自定义ViewGroup进行总结,并提供一个具体的实例来演示如何为Child View设置属性。 首先,我们要了解...

    Android自定义ViewGroup-----流式布局

    在Android开发中,自定义ViewGroup是实现复杂布局或创新交互设计的重要手段。本文将深入探讨如何创建一个自定义的“流式布局”(MyFlowLayout),这个布局会根据屏幕尺寸自动调整子视图的位置,使其从左到右、从上到...

    实现侧滑上下滑自定义ViewGroup

    在Android开发中,自定义ViewGroup是实现复杂交互和界面设计的重要手段,它允许开发者根据需求定制特定的布局和交互行为。"实现侧滑上下滑自定义ViewGroup"的主题涉及了Android开发中的几个核心概念,包括自定义View...

    自定义ViewGroup实现垂直滚动

    在Android开发中,自定义ViewGroup是提升用户体验和实现复杂布局的重要手段。本文将深入探讨如何实现一个可以垂直滚动的自定义ViewGroup,这在许多应用程序中都非常实用,比如滚动列表或者滑动页面。我们将基于博客...

    基于Kotlin的自定义ViewGroup试卷试题功能设计源码

    本源码是基于Kotlin开发的基于自定义ViewGroup的试卷试题功能设计,包含66个文件,其中包括28个.xml文件,16个.kt文件,以及10个.webp文件。此外,还包括3个.gitignore文件,3个.gradle文件,2个.properties文件,1...

    自定义viewgroup拦截事件

    在Android开发中,自定义`ViewGroup`是一个常见的需求,特别是在构建复杂布局或者需要实现特殊交互效果时。本文将深入探讨如何实现一个自定义的`ViewGroup`,并重点讲解如何进行事件拦截,使得在这样的布局中,可以...

    自定义ViewGroup+Adapter

    在Android开发中,自定义`ViewGroup`和`Adapter`是构建复杂用户界面的重要技术。这两者结合使用,可以创建出具有高度定制性和交互性的UI组件。本篇将深入探讨`ViewGroup`与`Adapter`的基本概念、实现原理以及如何在...

Global site tag (gtag.js) - Google Analytics