全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

Android中使用自定义ViewGroup的总结

分类

自定义Layout可以分为两种情况。

  • 自定义ViewGroup,创造出一些不同于LinearLayout,RelativeLayout等之类的ViewGroup。比如:API 14以后增加的GridLayout、design support library中的CoordinatorLayout等等。
  • 自定义一些已经有的Layout然后加一些特殊的功能。比如:TableLayout以及percent support library中的PercentFrameLayout等等。

流程

自定义View的流程是:onMeasure()->onLayout()->onDraw()。自定义ViewGroup的时候一般是不要去实现onDraw的,当然也可能有特殊的需求,比如:CoordinatorLayout。

所以onMeasure和onLayout基本能做大部分我们接触的ViewGroup。但是仅仅的知道在onMeasure中测量ViewGroup的大小以及在onLayout中计算Child View的位置还是不够。

比如:怎么可以给ViewGroup的Child View设置属性?

一个例子。

写一个自定义的ViewGroup,增加一个属性控制Child View的大小(长宽)占ViewGroup的比例。

假设是一个LinearLayout,那么就先定义一个CustomLinearLayout。

public class CustomLinearLayout extends LinearLayout {
  public CustomLinearLayout(Context context) {
    super(context);
  }

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

  public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }

  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
  }
}

其它抛开不说,我们是需要增加属性用来控制Child View的大小。那么就先在value/attr.xml中定义那个属性(使用CustomLinearLayout_Layout来与CustomLinearLayout区分下,当然这个名字是随意的)。

<declare-styleable name="CustomLinearLayout_Layout">
  <!-- 定义比例 -->
  <attr name="inner_percent" format="float"/>
</declare-styleable>

ViewGroup调用addView()的时候最终都会调用到这个方法。

public void addView(View child, int index, LayoutParams params)

这个params代表的就是View的配置,ViewGroup.LayoutParams中就包含了width、height,LinearLayout.LayoutParams增加了weight属性等等。那么我们就应该实现一个LayoutParams。那么现在就是这样了。

public class CustomLinearLayout extends LinearLayout {
  public CustomLinearLayout(Context context) {
    super(context);
  }

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

  public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }

  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
  }
  public static class LayoutParams extends LinearLayout.LayoutParams {

    private float innerPercent;

    private static final int DEFAULT_WIDTH = WRAP_CONTENT;
    private static final int DEFAULT_HEIGHT = WRAP_CONTENT;

    public LayoutParams() {
      super(DEFAULT_WIDTH, DEFAULT_HEIGHT);
      innerPercent = -1.0f;
    }

    public LayoutParams(float innerPercent) {
      super(DEFAULT_WIDTH, DEFAULT_HEIGHT);
      this.innerPercent = innerPercent;
    }

    public LayoutParams(ViewGroup.LayoutParams p) {
      super(p);
    }

    @TargetApi(Build.VERSION_CODES.KITKAT)
    public LayoutParams(LinearLayout.LayoutParams source) {
      super(source);
    }

    @TargetApi(Build.VERSION_CODES.KITKAT)
    public LayoutParams(LayoutParams source) {
      super(source);
      this.innerPercent = source.innerPercent;
    }

    public LayoutParams(Context c, AttributeSet attrs) {
      super(c, attrs);
      init(c, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
      TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomLinearLayout_Layout);
      innerPercent = a.getFloat(R.styleable.CustomLinearLayout_Layout_inner_percent, -1.0f);
      a.recycle();
    }
  }
}

现在就可以在xml使用我们的属性了。

<?xml version="1.0" encoding="utf-8"?>
<com.egos.samples.custom_layout.CustomLinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:layout_width="200dp"
  android:layout_height="200dp"
  android:id="@+id/test_layout"
  android:background="#ffff0000"
  android:gravity="center"
  android:orientation="vertical">
  <ImageView
    android:text="Egos"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:onClick="add"
    android:background="#ff00ff00"
    app:inner_percent="0.8"/>
</com.egos.samples.custom_layout.CustomLinearLayout>

只是然并软,并没有作用。

那么到底是怎么去控制Child View的大小呢?当然是在onMeasure控制的。addView会执行下面的代码。

requestLayout();
invalidate(true);

这样的话就会重新的走一遍onMeasure(),onLayout()了。实现onMeasure()的方法以后直接去处理Child View的大小,因为我继承的是LinearLayout,所以其实是会处理到measureChildBeforeLayout()。最终是在measureChildBeforeLayout的时候来处理Child View的大小。

@Override 
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec,
                    int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
  // 在xml强制写成match_parent,然后在这里强制设置成
  if (child != null && child.getLayoutParams() instanceof LayoutParams &&
      ((LayoutParams) child.getLayoutParams()).innerPercent != -1.0f) {
    parentWidthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (MeasureSpec.getSize(parentWidthMeasureSpec) *
        ((LayoutParams) child.getLayoutParams()).innerPercent), MeasureSpec.getMode(parentWidthMeasureSpec));
    parentHeightMeasureSpec = MeasureSpec.makeMeasureSpec((int) (MeasureSpec.getSize(parentHeightMeasureSpec) *
        ((LayoutParams) child.getLayoutParams()).innerPercent), MeasureSpec.getMode(parentHeightMeasureSpec));
    super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
      parentHeightMeasureSpec, heightUsed);
  } else {
    super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
        parentHeightMeasureSpec, heightUsed);
  }
}

这样就可以实现最开始的需求了。

其实还有一些细节是需要处理的,下面的代码就是。

/**
 * 当checkLayoutParams返回false的时候就会执行到这里的generateLayoutParams
 */
 @Override
protected LinearLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
  return super.generateLayoutParams(lp);
}

/**
 * 当addView的时候没有设置LayoutParams的话就会默认执行这里的generateDefaultLayoutParams
 */
@Override
protected LayoutParams generateDefaultLayoutParams() {
  return new LayoutParams();
}

/**
 * 写在xml中属性的时候就会执行这里的generateLayoutParams
 */
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
  return new LayoutParams(getContext(), attrs);
}

总结一下

  • 自定义View和ViewGroup需要多多注意的都是onMeasure、onLayout、onDraw。
  • 把ViewGroup自身的属性和Child View的属性区分开。
  • 可以多多的参考support包中的代码,调试也非常的方便。

做Android开发,自身需要自定义View的地方确实是比较的多,只是大部分都会有相应的开源库。但是我们还是应该需要熟练的知道该如何自定义一个ViewGroup。

自己是一个比较健忘的人,所以写的详细点。

完整的代码戳这里。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


# 自定义viewgroup  # 安卓自定义viewgroup  # viewgroup用法  # Android ViewDragHelper完全解析 自定义ViewGroup神器  # Android控件PullRefreshViewGroup实现下拉刷新和上拉加载  # Android自定义ViewGroup(侧滑菜单)详解及简单实例  # Android自定义控件之继承ViewGroup创建新容器  # Android自定义ViewGroup实现标签流容器FlowLayout  # Android 重写ViewGroup 分析onMeasure()和onLayout()方法  # 自定义  # 就会  # 是一个  # 是在  # 就可以  # 的人  # 的是  # 都是  # 在这里  # 是怎么  # 两种  # 要去  # 也可  # 一遍  # 能有  # 该如何  # 能做  # 写在  # 就先  # 还有一些 


相关文章: 建站之星后台搭建步骤解析:模板选择与产品管理实操指南  香港服务器网站搭建教程-电商部署、配置优化与安全稳定指南  如何在沈阳梯子盘古建站优化SEO排名与功能模块?  制作网站公司那家好,网络公司是做什么的?  巅云智能建站系统:可视化拖拽+多端适配+免费模板一键生成  如何用5美元大硬盘VPS安全高效搭建个人网站?  c++怎么用jemalloc c++替换默认内存分配器【性能】  如何在建站宝盒中设置产品搜索功能?  制作网站的网址是什么,请问后缀为.com和.com.cn还有.cn的这三种网站是分别是什么类型的网站?  如何选择靠谱的建站公司加盟品牌?  北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?  在线流程图制作网站手机版,谁能推荐几个好的CG原画资源网站么?  详解jQuery停止动画——stop()方法的使用  模具网站制作流程,如何找模具客户?  建设网站制作价格,怎样建立自己的公司网站?  建站主机选哪家性价比最高?  建站之星如何实现PC+手机+微信网站五合一建站?  网站制作网站,深圳做网站哪家比较好?  小米网站链接制作教程,请问miui新增网页链接调用服务有什么用啊?  深圳防火门网站制作公司,深圳中天明防火门怎么编码?  如何通过主机屋免费建站教程十分钟搭建网站?  制作公司内部网站有哪些,内网如何建网站?  c# 在ASP.NET Core中管理和取消后台任务  如何用已有域名快速搭建网站?  香港服务器选型指南:免备案配置与高效建站方案解析  再谈Python中的字符串与字符编码(推荐)  如何高效生成建站之星成品网站源码?  零服务器AI建站解决方案:快速部署与云端平台低成本实践  网站代码制作软件有哪些,如何生成自己网站的代码?  网站制作大概多少钱一个,做一个平台网站大概多少钱?  建站之星体验版:智能建站系统+响应式设计,多端适配快速建站  Dapper的Execute方法的返回值是什么意思 Dapper Execute返回值详解  东莞市网站制作公司有哪些,东莞找工作用什么网站好?  python的本地网站制作,如何创建本地站点?  建站之星IIS配置教程:代码生成技巧与站点搭建指南  代刷网站制作软件,别人代刷火车票靠谱吗?  如何在局域网内绑定自建网站域名?  高端云建站费用究竟需要多少预算?  建站之星安装路径如何正确选择及配置?  制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?  b2c电商网站制作流程,b2c水平综合的电商平台?  如何选择CMS系统实现快速建站与SEO优化?  如何快速登录WAP自助建站平台?  建站之星导航如何优化提升用户体验?  如何彻底卸载建站之星软件?  如何通过虚拟主机空间快速建站?  如何在腾讯云服务器快速搭建个人网站?  如何通过VPS建站无需域名直接访问?  深圳网站制作平台,深圳市做网站好的公司有哪些?  建站之星后台管理:高效配置与模板优化提升用户体验 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。