Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

《Android群英传》笔记6——自定义ViewGroup

$
0
0

自定义ViewGroup

本文是读了《Android 群英传》第三章--Android体控件架构与自定义空间详解--之后的读书笔记,感谢作者,在此特别推荐此书。

      之前说过了自定义View,而对于自定义ViewGroup来说,主要是定义和管理该ViewGroup里的子View。所以自定义ViewGroup里往往需要重写onMearsure()方法来完成对各子View的测量,重写onLayout()方法来完成对各子View的位置布局,重写onTouchEvent()来完成响应事件。至于为啥没有说onDraw()方法呢,其实在draw阶段,ViewGroup都是通过drawchild方法调用子View的draw方法来绘制各子View,而作为各子View的容器,一般情况下不会再做额外的修饰。所以往往在绘制阶段里,只需要调用ViewGroup的默认实现方法就可以了。

      以一个简单的ViewGroup为例子吧,我打算做一个含有三个控件,并且三个控件斜着排在我们自定义ViewGroup中。首先依然是新建一个类继承ViewGroup,写好构造函数,并给它添加上三个子View。

public MyViewGroup(Context context) {
    super(context);
    mContext = context ;
    init() ;
}

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

//为MyViewGroup添加三个子View
private void init(){
    //调用ViewGroup父类addView()方法添加子View
    //child 对象一 : Button
    Button btn= new Button(mContext) ;
    btn.setText("I am Button") ;
    this.addView(btn) ;

    //child 对象二 : ImageView
    ImageView img = new ImageView(mContext) ;
    img.setBackgroundResource(R.mipmap.ic_launcher) ;
    this.addView(img) ;

    //child 对象三 : TextView
    TextView txt = new TextView(mContext) ;
    txt.setText("Only Text") ;
    this.addView(txt) ;
}
      然后是onMearsure()方法,遍历每个子View进行测量,获取每个子View的实际宽,高,然后计算出我们自定义ViewGroup需要的宽和高。

@Override
//对每个子View进行measure():设置每子View的大小,即实际宽和高
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
    int childCount = getChildCount() ;
    Log.i(TAG, "**** the size of this ViewGroup is ----> " + childCount+" ****") ;
    Log.i(TAG, "**** onMeasure start *****") ;

    //获取该ViewGroup的实际长和宽  涉及到MeasureSpec类的使用
    int specSize_Width = MeasureSpec.getSize(widthMeasureSpec) ;
    int specSize_Height = MeasureSpec.getSize(heightMeasureSpec) ;
    Log.i(TAG, "**** specSize_Width=== " + specSize_Width+ " * specSize_Height===" + specSize_Height+" ****") ;

    int new_Width=0;
    int new_Height=0;

    for(int i=0 ;i<childCount ; i++){
        View child = getChildAt(i) ;   //获得每个对象的引用
        //或者可以调用ViewGroup父类方法measureChild()或者measureChildWithMargins()方法
        this.measureChild(child, widthMeasureSpec, heightMeasureSpec) ;
        new_Width+=child.getMeasuredWidth()+10;
        new_Height+=child.getMeasuredHeight()+10;
    }

    Log.i(TAG, "**** new_Width=== " + new_Width+ " * new_Height===" + new_Height+" ****") ;
    //设置本ViewGroup的宽高
    setMeasuredDimension(new_Width , new_Height) ;
}
      接下来就是布局每个子View了,设置好每个子View的起始位置以及长宽。

@Override
//对每个子View视图进行布局
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    // TODO Auto-generated method stub
    int childCount = getChildCount() ;

    int startLeft = 0 ;//设置每个子View的起始横坐标
    int startTop = 0 ; //设置每个子View的起始纵坐标;

    Log.i(TAG, "**** onLayout start ****") ;
    for(int i=0 ;i<childCount ; i++){
        View child = getChildAt(i) ;   //获得每个对象的引用
        child.layout(startLeft, startTop, startLeft+child.getMeasuredWidth(), startTop+child.getMeasuredHeight()) ;
        startLeft =startLeft+child.getMeasuredWidth() + 10;  //校准startLeft值,View之间的横向间距设为10px ;
        startTop =startTop+child.getMeasuredHeight() + 10;   //校准startTop值,View之间的高度间距设为10px ;
        Log.i(TAG, "**** onLayout startLeft===" +startLeft+" ****") ;
        Log.i(TAG, "**** onLayout startTop===" +startTop+" ****") ;
    }
}
      最后我们还是写上绘制方法,仅仅来观察下整个流程。

protected boolean drawChild(Canvas canvas , View child, long drawingTime){
    Log.i(TAG, "**** drawChild start ****") ;

    return super.drawChild(canvas, child, drawingTime) ;
}
      我们看下运行结果:


      看看我们的运行日志:


      书中举的例子包含了点击事件,其实就是重写onTouchEvent()方法,监听触摸点击写出响应事件,这里我也写了个简单例子,代码如下所示:

@Override
public boolean onTouchEvent(MotionEvent event){
    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
            child.layout((int)event.getX()-child.getMeasuredWidth()/2,(int)event.getY()-child.getMeasuredHeight()/2,
                    (int)event.getX()+child.getMeasuredWidth()/2,(int)event.getY()+child.getMeasuredHeight()/2);
            break;
        case MotionEvent.ACTION_MOVE:
            child.layout((int)event.getX()-child.getMeasuredWidth()/2,(int)event.getY()-child.getMeasuredHeight()/2,
                    (int)event.getX()+child.getMeasuredWidth()/2,(int)event.getY()+child.getMeasuredHeight()/2);
            break;
    }
    postInvalidate();
    return true;
}
      效果就是我点击以及滑动屏幕时候,child这个控件会跟着我手指头移动,在这就不贴图了。

作者:bit_kaki 发表于2017/5/26 15:09:05 原文链接
阅读:34 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>