Android 重写ViewGroup 分析onMeasure()和onLayout()方法

Android 重写ViewGroup 分析onMeasure()和onLayout()方法

在继承ViewGroup类时,需要重写两个方法,分别是onMeasure和onLayout。

1,在方法onMeasure中调用setMeasuredDimension方法

void android.view.View.setMeasuredDimension(int measuredWidth, int measuredHeight)

在onMeasure(int, int)中,必须调用setMeasuredDimension(int width, int height)来存储测量得到的宽度和高度值,如果没有这么去做会触发异常IllegalStateException。

2,在方法onMeasure中调用孩子的measure方法

void android.view.View.measure(int widthMeasureSpec, int heightMeasureSpec)

这个方法用来测量出view的大小。父view使用width参数和height参数来提供constraint信息。实际上,view的测量工作在onMeasure(int, int)方法中完成。因此,只有onMeasure(int, int)方法可以且必须被重写。参数widthMeasureSpec提供view的水平空间的规格说明,参数heightMeasureSpec提供view的垂直空间的规格说明。

3,解析onMeasure(int, int)方法

void android.view.View.onMeasure(int widthMeasureSpec, int heightMeasureSpec)

测量view及其内容来确定view的宽度和高度。这个方法在measure(int, int)中被调用,必须被重写来精确和有效的测量view的内容。

在重写这个方法时,必须调用setMeasuredDimension(int, int)来存储测量得到的宽度和高度值。执行失败会触发一个IllegalStateException异常。调用父view的onMeasure(int, int)是合法有效的用法。

view的基本测量数据默认取其背景尺寸,除非允许更大的尺寸。子view必须重写onMeasure(int, int)来提供其内容更加准确的测量数值。如果被重写,子类确保测量的height和width至少是view的最小高度和宽度(通过getSuggestedMinimumHeight()和getSuggestedMinimumWidth()获取)。

4,解析onLayout(boolean, int, int, int, int)方法

void android.view.ViewGroup.onLayout(boolean changed, int l, int t, int r, int b)

调用场景:在view给其孩子设置尺寸和位置时被调用。子view,包括孩子在内,必须重写onLayout(boolean, int, int, int, int)方法,并且调用各自的layout(int, int, int, int)方法。

参数说明:参数changed表示view有新的尺寸或位置;参数l表示相对于父view的Left位置;参数t表示相对于父view的Top位置;参数r表示相对于父view的Right位置;参数b表示相对于父view的Bottom位置。.

5,解析View.MeasureSpec类

android.view.View.MeasureSpec

MeasureSpec对象,封装了layout规格说明,并且从父view传递给子view。每个MeasureSpec对象代表了width或height的规格。

MeasureSpec对象包含一个size和一个mode,其中mode可以取以下三个数值之一:

  • UNSPECIFIED,1073741824 [0x40000000],未加规定的,表示没有给子view添加任何规定。
  • EXACTLY,0 [0x0],精确的,表示父view为子view确定精确的尺寸。
  • AT_MOST,-2147483648 [0x80000000],子view可以在指定的尺寸内尽量大。

在这里给大家举一个例子demo:

第一步:自定义一个View实现ViewGroup接口,即自定义ViewGroup:

package net.loonggg.viewgroup; 

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup; 

public class MyViewGroup extends ViewGroup { 

  public MyViewGroup(Context context) {
    super(context);
  } 

  public MyViewGroup(Context context, AttributeSet attrs) {
    super(context, attrs);
  } 

  public MyViewGroup(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
  } 

  /**
   * 计算控件的大小
   */
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int measureWidth = measureWidth(widthMeasureSpec);
    int measureHeight = measureHeight(heightMeasureSpec);
    // 计算自定义的ViewGroup中所有子控件的大小
    measureChildren(widthMeasureSpec, heightMeasureSpec);
    // 设置自定义的控件MyViewGroup的大小
    setMeasuredDimension(measureWidth, measureHeight);
  } 

  private int measureWidth(int pWidthMeasureSpec) {
    int result = 0;
    int widthMode = MeasureSpec.getMode(pWidthMeasureSpec);// 得到模式
    int widthSize = MeasureSpec.getSize(pWidthMeasureSpec);// 得到尺寸 

    switch (widthMode) {
    /**
     * mode共有三种情况,取值分别为MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY,
     * MeasureSpec.AT_MOST。
     *
     *
     * 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方法传入的模式。
     */
    case MeasureSpec.AT_MOST:
    case MeasureSpec.EXACTLY:
      result = widthSize;
      break;
    }
    return result;
  } 

  private int measureHeight(int pHeightMeasureSpec) {
    int result = 0; 

    int heightMode = MeasureSpec.getMode(pHeightMeasureSpec);
    int heightSize = MeasureSpec.getSize(pHeightMeasureSpec); 

    switch (heightMode) {
    case MeasureSpec.AT_MOST:
    case MeasureSpec.EXACTLY:
      result = heightSize;
      break;
    }
    return result;
  } 

  /**
   * 覆写onLayout,其目的是为了指定视图的显示位置,方法执行的前后顺序是在onMeasure之后,因为视图肯定是只有知道大小的情况下,
   * 才能确定怎么摆放
   */
  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    // 记录总高度
    int mTotalHeight = 0;
    // 遍历所有子视图
    int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
      View childView = getChildAt(i); 

      // 获取在onMeasure中计算的视图尺寸
      int measureHeight = childView.getMeasuredHeight();
      int measuredWidth = childView.getMeasuredWidth(); 

      childView.layout(l, mTotalHeight, measuredWidth, mTotalHeight
          + measureHeight); 

      mTotalHeight += measureHeight; 

    }
  } 

}

第二步,布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#00f0f0"
  tools:context=".MainActivity" > 

  <net.loonggg.viewgroup.MyViewGroup
    android:id="@+id/myViewGroup"
    android:layout_width="480dp"
    android:layout_height="300dp"
    android:background="#0f0f0f" > 

    <TextView
      android:layout_width="200dp"
      android:layout_height="100dp"
      android:background="#000000"
      android:gravity="center"
      android:text="第一个TextView" /> 

    <TextView
      android:layout_width="100dp"
      android:layout_height="200dp"
      android:background="#ffffff"
      android:gravity="center"
      android:text="第二个TextView" />
  </net.loonggg.viewgroup.MyViewGroup> 

</RelativeLayout>

第三步,MainActivity.java:

package net.loonggg.viewgroup; 

import android.os.Bundle;
import android.app.Activity; 

public class MainActivity extends Activity { 

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  } 

}

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • Android自定义ViewGroup实现标签流容器FlowLayout

    本篇文章讲的是Android 自定义ViewGroup之实现标签流式布局-FlowLayout,开发中我们会经常需要实现类似于热门标签等自动换行的流式布局的功能,网上也有很多这样的FlowLayout,但不影响我对其的学习.和往常一样,主要还是想总结一下自定义ViewGroup的开发过程以及一些需要注意的地方. 按照惯例,我们先来看看效果图 一.写代码之前,有几个是问题是我们先要弄清楚的: 1.什么是ViewGroup:从名字上来看,它可以被翻译为控件组,言外之意是ViewGroup内部包含了许

  • Android ViewDragHelper完全解析 自定义ViewGroup神器

    一.概述 在自定义ViewGroup中,很多效果都包含用户手指去拖动其内部的某个View(eg:侧滑菜单等),针对具体的需要去写好onInterceptTouchEvent和onTouchEvent这两个方法是一件很不容易的事,需要自己去处理:多手指的处理.加速度检测等等. 好在官方在v4的支持包中提供了ViewDragHelper这样一个类来帮助我们方便的编写自定义ViewGroup.简单看一下它的注释: ViewDragHelper is a utility class for writin

  • Android中使用自定义ViewGroup的总结

    分类 自定义Layout可以分为两种情况. 自定义ViewGroup,创造出一些不同于LinearLayout,RelativeLayout等之类的ViewGroup.比如:API 14以后增加的GridLayout.design support library中的CoordinatorLayout等等. 自定义一些已经有的Layout然后加一些特殊的功能.比如:TableLayout以及percent support library中的PercentFrameLayout等等. 流程 自定义V

  • Android控件PullRefreshViewGroup实现下拉刷新和上拉加载

    本文实例为大家分享了Android实现下拉刷新和上拉加载更多的具体代码,供大家参考,具体内容如下 先分享下源码:Android实现下拉刷新和上拉加载更多 实现思路:由PullRefreshViewGroup控件来接管标准控件(比如RecyclerView.ListView等)的滑动,调用标准控件的内部方法进行短距离滑动,不再由标准控件自己来处理事件,而完全由PullRefreshViewGroup控件来处理触摸事件.标准控件内部的滑动距离等属性,通过反射获得computeVerticalScro

  • Android自定义控件之继承ViewGroup创建新容器

    欢迎大家来学习本节内容,前几节我们已经学习了其他几种自定义控件,分别是Andriod 自定义控件之音频条及 Andriod 自定义控件之创建可以复用的组合控件还没有学习的同学请先去学习下,因为本节将使用到上几节所讲述的内容. 在学习新内容之前,我们先来弄清楚两个问题: 1 . 什么是ViewGroup? ViewGroup是一种容器.它包含零个或以上的View及子View. 2 . ViewGroup有什么作用? ViewGroup内部可以用来存放多个View控件,并且根据自身的测量模式,来测量

  • Android自定义ViewGroup(侧滑菜单)详解及简单实例

    自定义侧滑菜单的简单实现 不少APP中都有这种侧滑菜单,例如QQ这类的,比较有名开源库如slidingmenu. 有兴趣的可以去研究研究这个开源库. 这里我们将一种自己的实现方法,把学习的 东西做个记录,O(∩_∩)O! 首先看效果图: 这里我们实现的侧滑菜单,是将左侧隐藏的菜单和主面板看作一个整体来实现的,而左侧隐藏的菜单和主面板相当于是这个自定义View的子View. 首先来构造该自定义View的布局: 自定义的SlideMenuView包含两个子view,一个是menuView,另一个是m

  • Android 重写ViewGroup 分析onMeasure()和onLayout()方法

    Android 重写ViewGroup 分析onMeasure()和onLayout()方法 在继承ViewGroup类时,需要重写两个方法,分别是onMeasure和onLayout. 1,在方法onMeasure中调用setMeasuredDimension方法 void android.view.View.setMeasuredDimension(int measuredWidth, int measuredHeight) 在onMeasure(int, int)中,必须调用setMeas

  • Android编程重写ViewGroup实现卡片布局的方法

    本文实例讲述了Android编程重写ViewGroup实现卡片布局的方法.分享给大家供大家参考,具体如下: 实现效果如图: 实现思路 1. 重写onMeasure(int widthMeasureSpec, int heightMeasureSpec)设置每个子View的大小 2. 重写onLayout(boolean changed, int l, int t, int r, int b) 设置每个子View的位置 第一步:新建FlowLayout继承ViewGroup package com

  • Android继承ViewGroup实现Scroll滑动效果的方法示例

    本文实例讲述了Android继承ViewGroup实现Scroll滑动效果的方法.分享给大家供大家参考,具体如下: extends ViewGroup需要重写onMeasure和onLayout方法 onMeasure方法是去测量ViewGroup需要的大小以及包含的子View需要的大小. 执行完上面的方法后,再执行onLayout方法去设置子View的摆放位置. 实现Scroll滑动效果需要去检测滑动速率,即要知道每个单位时间滑动了多少像素值,根据这个像素值去判断Scroll滑动到下一页还是上

  • Android自定义ViewGroup之WaterfallLayout(二)

    上一篇我们学习了自定义ViewGroup的基本步骤,并做了一个CustomGridLayout的实例,这篇我们继续来说说自定义ViewGroup. Android中当有大量照片需要展示的时候,我们可以用GridView作为照片墙,但是GridView太整齐了,有时候不规则也是一种美,瀑布流模型就是这样一个不规则的展示墙,接下来我们尝试用自定义ViewGroup来实现瀑布流. 实现瀑布流的方式也有很多,下面我们一一道来: 一.继承ViewGroup 其实这种实现方式我们只需要在上篇博客的基础上稍作

  • Android自定义ViewGroup的实现方法

    在android中提供了常见的几种ViewGroup的实现,包括LinearLayout.Relativeayout.FrameLayout等.这些ViewGroup可以满足我们一般的开发需求,但是对于界面要求复杂的,这几个布局就显得捉襟见肘了.所以自定义的ViewGroup在我们接触过的应用中比比皆是. 要想实现一个自定义的ViewGroup,第一步是学会自定义属性,这些自定义的属性将让我们配置布局文件的时候更加的灵活.自定义属性是在value目录下声明一个attrs.xml文件. <?xml

  • android流式布局onLayout()方法详解

    在上一篇中及就写了自定义view中的onMeausre()和onDraw()两个方法.在这里就用简单的流式布局来介绍一下onLayout()方法. 在onLayout方法中有四个参数,我画了一个简单的图来分清楚值哪里. 好啦,现在就直接看代码吧. FlowLayout.Java package com.example.my_view; import android.content.Context; import android.util.AttributeSet; import android.

  • Android重写View并自定义属性实例分析

    本文实例分析了Android重写View并自定义属性的方法.分享给大家供大家参考,具体如下: 这里通过自定义属性 实现如下图所示效果: 第一步:在res\values的目录下新建一个文件attrs.xml 声明一些自定义属性 <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CustomViewStyle">

  • Android事件分发机制 ViewGroup分析

    目录 整体流程 源码分析 前言: 事件分发从手指触摸屏幕开始,即产生了触摸信息,被底层系统捕获后会传递给Android的输入系统服务IMS,通过Binder把消息发送到activity,activity会通过phoneWindow.DecorView最终发送给ViewGroup.这里就直接分析ViewGroup的事件分发 整体流程 配合图在看一段伪代码: public boolean dispatchTouchEvent(MotionEvent ev) :Boolean{ val result

  • Android重写TextView实现文字整齐排版的方法(附demo源码下载)

    本文实例讲述了Android重写TextView实现文字整齐排版的方法.分享给大家供大家参考,具体如下: XRTextView类 package rong.android.test; import org.json.JSONArray; import org.json.JSONException; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; impor

  • Android编程使用Intent传递对象的方法分析

    本文实例分析了Android编程使用Intent传递对象的方法.分享给大家供大家参考,具体如下: 之前的文章中,介绍过Intent的用法,比如启动活动,发送广播,启发服务等,并且可以使用Intent时传递一些数据.如下代码所示: Intent intent = new Intent(this,SecondActivity.class); intent.putExtra("info", "I am fine"); startActivity(intent); 在传递数

随机推荐