美文网首页
Android onLayout()

Android onLayout()

作者: 就爱烫卷发 | 来源:发表于2019-07-24 00:16 被阅读0次

    Called from layout when this view should assign a size and position to each of its children

           onLayout()方法的注释就是安排自己的子View的位置,我们继承View的时候好像很少用到这个玩意。因为只是写一个控件根本不会存在子View的问题。
           接手别人的代码有个FlowLayout,搜索的时候出现历史记录的类似的View,但是换行的时候会出现问题。所以觉得可以自己搞个试试。
           首先要继承自ViewGroup这个类。直接重写方法,会发现一定要重写一个onLayout的方法。

    public class FlowLayout extends ViewGroup {
    
    public FlowLayout(Context context) {
        this(context,null);
    }
    
    public FlowLayout(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    
    public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
    
    }
    }
    

        之前的onMeasure()是对View的测量。这个onLayout()就是对View的位置进行摆放。写个简单的xml


    xml布局.png

    运行之后发现什么画面没有。这是当然的因为我们什么都没搞。那好现在开始填代码,我们在onLayout()方法中摆放控件。

        int count   = getChildCount() ;
        Log.e(TAG, "onLayout: "+count );
        for(int i =0 ;i<count;i++){
            View child = getChildAt(i);
            child.layout(100*i+100,100*i+100,200*i+200,200*i+200);
        }
    

           这里是随便选的几个位置放一下,这里的layout四个参数分别是左上右下离父布局的距离。

    初设layout.png
    这样一看这个简单的代码能实现布局好像有点东西的。既然知道了onLayout()的功能了我们就直接一把梭吧,思路:一个View 挨着一个View 当一行View多的放不下的时候自动换行。两个View之间来电间距。
        int count   = getChildCount() ;
        int indexX = 20;
        int indexY = 20;
        for(int i =0 ;i<count;i++){
            View child = getChildAt(i);
            int width = child.getMeasuredWidth();
            int height = child.getMeasuredHeight();
             if(i==0){
                child.layout(indexX,indexY,indexX+width,indexY+height);
            }else{
                child.layout(indexX,indexY,indexX+width,indexY+height);
            }
            indexX+=width;
        }
    }
    

           一把梭哈,先完成两个的View摆放,这个简单没什么问题。直接放第一个View 初始化一个开始位置(20,20)坐标点,然后开始向右边排。运行起来之后尴尬的事情发生了,UI上啥都没有。我们好像少了什么,一般都要先Measure的吧.ViewGroup中onMeasure主要是来调用measureChildren()方法。通过测量子View的大小然后在自己的View中设置宽高。加上方法,这里其实还有measureChildWithMargins(),这个可以获取到margins。先简单的设置一波。(这里其实要算子View的宽高的总和,再来设置FlowLayout的大小)

     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        measureChildren(widthMeasureSpec,heightMeasureSpec);
    }
    

           实现效果如:

    初见效果.png
           现在要设置两个View之间的间距,我们可以加一个属性来实现,现在改变一下代码,中间的间隔就出来了。
    indexX=indexX+width+padding,下面得看一下换行的效果,思路:当一行容不下要添加的View的时候我们要进行换行,即A+B >ScreenWidth------->height+Y;还是直接走代码吧:
        int count   = getChildCount() ;
        int indexX = 20;
        int indexY = 20;
        for(int i =0 ;i<count;i++){
            View child = getChildAt(i);
            int width = child.getMeasuredWidth();
            int height = child.getMeasuredHeight();
            if(width+indexX > getMeasuredWidth()){
               indexX = 20;
               indexY =indexY+ getChildAt(i-1).getMeasuredHeight()+padding;
            }
            child.layout(indexX,indexY,indexX+width,indexY+height);
            indexX=indexX+width+padding;
        }
    

           简单的代码就实现了,历史纪录的View,我们可以想想这里是通过xml中自己写的,那岂不是很傻,有时间写好像不如自己把东西画出来,当然当Item数量不固定的时候,好像也挺尴尬的。


    再见效果图.png
    • 好吧我们进行第一次改良,需求设计成动态配置View的数量。

       public void addViewWithString(ArrayList<String> list){
        this.removeAllViews();
        for(String string:list) {
            TextView textView = new TextView(mContext);
            textView.setText(string);
            this.addView(textView);
        }
       }
      

           首先移除之前的View,然后把TextView添加上去 ,实现效果


    动态添加效果.png

    这样看好丑,人家的TextView至少还有背景,这个动态添加的背景怎么搞,当然是在setTextView后面加上setBackground属性啦。当然最好是开一个属性在FlowLayout这个控件上面,让写布局的时候可以进行配置。
           但是问题来了,显示没毛病我要点击怎么办?设置一个点击事件吧,我们把方法改进一下直接上代码:

    public void initDate(ArrayList<String> list){
        this.removeAllViews();
        for(int i = 0;i<list.size();i++){
            addViewWithString(list.get(i),i);
        }
    
    }
    
    
    public void addViewWithString(final String name , final int position){
            TextView textView = new TextView(mContext);
            textView.setText(name);
            textView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(iChildClickListener!=null){
                        iChildClickListener.childClickListener(name,position);
                    }
                }
            });
            this.addView(textView);
    
    }
    interface IChildClickListener{
        void childClickListener(String name,int position);
    }
    IChildClickListener iChildClickListener ;
    
    public void setiChildClickListener(IChildClickListener listener){
        this.iChildClickListener = listener;
    }
    

    外部调用:

     FlowLayout flowLayout = findViewById(R.id.flowLayout);
         flowLayout.initDate(list);
        flowLayout.setiChildClickListener(new FlowLayout.IChildClickListener() {
            @Override
            public void childClickListener(String name, int position) {
                Toast.makeText(MainActivity.this,"第"+position+"个"+"--->"+name,Toast.LENGTH_LONG).show();
            }
        });
    

    那么就可以实现点击事件以及效果了。好了,把代码整理一下加上背景。


    加上点击效果.png

    相关文章

      网友评论

          本文标题:Android onLayout()

          本文链接:https://www.haomeiwen.com/subject/qovzlctx.html