当前位置: 移动技术网 > IT编程>移动开发>Android > Android中使用自定义ViewGroup的总结

Android中使用自定义ViewGroup的总结

2019年07月24日  | 移动技术网IT编程  | 我要评论

广州市委书记万庆良,雷电之王的勇士,juicy_krissy

分类

自定义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。

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

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网