Android 自定义View 二

在上一篇文章android 自定义view 中我们总结了自定义View的几大步骤如下:

1、自定义View的属性

2、在View的构造方法中获得我们自定义的属性

3、重写onMesure方法

4、重写onLayout方法

5、重写onDraw方法

因为上篇文章中我们完成了LabelImageView中的一个LabelView子view,所以不用重写onLayout方法,下面接着上篇文章我们来完成LabelImageView这个ViewGroup控件。既然LabelImageView是一个ViewGroup,那么我们就必须自己决定其子控件的布局,我们的子控件就是一个ImgeView和许多的LabelView。而onLayout就是给子控件布局的地方。在上代码前,我们先看看效果:


上面的效果就是在ImageView里面可以添加我们自己的标签,这个在某些APP里面可以应用到,下面是LabelImageView的onLayout方法代码:

 @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if(changed){
            int childCount = getChildCount();
            for(int i=0;i<childCount;i++){
                View v = getChildAt(i);
                if(v instanceof ImageView){ //布局ImageView
                    v.layout(l+getPaddingLeft(),t+getPaddingTop(),r-getPaddingRight(),b-getPaddingBottom());
                }else if(v instanceof LabelView){//布局LabelView
                    LabelData labelData = ((LabelView)v).getLabelData();
                    float pos[] = labelData.getPosition();
                    int cl = (int) (getWidth()*pos[0]);
                    int ct = (int) (getHeight()*pos[1]);
                    int cr = cl+ v.getMeasuredWidth();
                    int cb = ct+ v.getMeasuredHeight();
                    v.layout(cl,ct,cr,cb);
                }
            }
        }
    }

其实就是获取子控件,然后调用子控件的layout方法进行布局,注意这个changed属性,google官方api里说当view有新的size或position时changed为true,但当我用动画改变一个view的大小或位置时,onlayout方法是不会调用的,用setLayoutParams重新设置View的宽高,changed确实会为true。


在android开发时,有时我们需要得到某个View的宽高,最简单的方法就是调用View的getHeight()和getWidth()了,但是使用时很多时候都发现他们返回的是0,得不到我们想要的值。下面打印下activity的启动流程来分析下:


只有在activity调用了onAttachToWindow后,才会开启view的onMeasure方法,测量view的大小,只有在调用了view的onlayout方法后,view才有真正的宽高,view的getWidth()和getHeight()才能得到真的的值。所以我们在activity的onResume方法里及其前面的生命周期里都无法得到view的宽高。如果我们必须要得到view的宽高呢,有办法么?答案当然是有,下面介绍三种常见的方法:

方法一:

//手动调用测量方法。 制定测量规则 参数表示size + mode
    int width =View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
    int height =View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
    mLabelImageView.measure(width,height);
    //调用measure方法之后就可以获取宽高。
    height=mLabelImageView.getMeasuredHeight();
    width=mLabelImageView.getMeasuredWidth();
    Log.e(TAG,"-----------  measure  width: "+ width + "  height: " + height);

注意,measure()方法是实际测量的方法,而在绘制布局过程中调用的onMeasure()只是制定测量规则. 在自定义布局中我们一般重写onMeasure()方法,measure()方法是final的,子类无法重写。 measure函数有2个参数,int widthMeasureSpec 和 int heightMeasureSpec表示具体的测量规则。 
这两个数值不是普通的数值, 它表示: size + mode ,例如: 
int widthMeasureSpec= View.MeasureSpec.makeMeasureSpec(1000,View.MeasureSpec.EXACTLY); 
int heightMeasureSpec= View.MeasureSpec.makeMeasureSpec(1000,View.MeasureSpec.AT_MOST); 
模式分为: 
View.MeasureSpec.EXACTLY:表示父视图希望子类的大小是specSize中制定的大小. 
View.MeasureSpec.AT_MOST:父试图希望子类的大小最高不超过specSize中制定的大小. 
View.MeasureSpec.UNSPECIFIED:父试图不对子类实施任何限制,子试图可以得到自己想得到的任意大小. 

值得一提的是View.MeasureSpec.UNSPECIFIED 其值为0,所以前面可以简写成:view.measure(0,0),但是值得注意的是,这个只是测量值,一般情况下view的测量值宽高跟真实值宽高是一致的,但是也有不一样的情况,比如ImageView设置不同的scaleType,测量值跟真实值有可以就不一样。

方法二(设置View树桩结构监听器):

  mLabelImageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
            @Override
            public void onGlobalLayout() {
                mLabelImageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                int h = mLabelImageView.getHeight();
                int w = mLabelImageView.getWidth();
                Log.e(TAG,"-----------  onGlobalLayout  width: "+ w + "  height: " + h);
            }
        });

方法三(增加组件绘制之前的监听):

   mLabelImageView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                mLabelImageView.getViewTreeObserver().removeOnPreDrawListener(this);
                int h = mLabelImageView.getHeight();
                int w = mLabelImageView.getWidth();
                Log.e(TAG,"-----------  onPreDraw  width: "+ w + "  height: " + h);
                return false;
            }
        });
下面看看打印信息:


   如上正确打印出了view的宽高,推荐使用后面两种方式。

未完待续!

更多精彩Android技术可以关注我们的微信公众号,扫一扫下方的二维码或搜索关注公共号:  Android老鸟

                                                



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值