自定义流式布局

自定义流式布局自定义流式布局

效果图:

自定义流式布局

先梳理下整个流程:

  1. 继承ViewGroup ,重写onMeasure,onLayout 方法;
  2. 在onMeasure方法里通过加入其中的子view个数来计算父级的宽高;
  3. 在onLayout 方法里对子view进行排版(横向排列),即超过父级宽度就另换一行
    注意:当父级宽高发生变化时会重新执行onMeasure,onLayout 方法,即会被调用2遍。

详细代码如下:

FlowLayout:

import java.util.ArrayList; import java.util.List; import android.annotation.SuppressLint; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; / * 自定义流式布局 */ public class FlowLayout extends ViewGroup{ 
    / view距离父布局的边界信息*/ private MarginLayoutParams marginLayoutParams; / child宽度*/ private int childwidth; / child高度*/ int childheight; / 父级实际的宽度*/ int factwidth; / 父级实际的高度*/ int factheight; / 每行宽度*/ int linewidth; / 每行高度*/ int lineheight; / 用于记录上一次的行高*/ int lastchildheight=0; public FlowLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public FlowLayout(Context context, AttributeSet attrs) { super(context, attrs,0); } public FlowLayout(Context context) { super(context,null); } / * 根据子view来确定宽高 * */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //添加了子视图后,父级视图布局会发生改变,onMeasure方法会被执行两遍 factheight=0; factwidth=0; int widthsize=MeasureSpec.getSize(widthMeasureSpec); int widthmode=MeasureSpec.getMode(widthMeasureSpec); int heightsize=MeasureSpec.getSize(heightMeasureSpec); int heightmode=MeasureSpec.getMode(heightMeasureSpec);
        int childcount=getChildCount(); for(int i=0;i 
  
    //如果child的宽度和加起来大于了父级宽度,则计算最大值,并作为父级实际宽度 
   if(linewidth+childwidth>widthsize){ 
   //计算父级宽度 factwidth=Math.max(widthsize,linewidth); 
   //计算父级高度 factheight+=lastchildheight; 
   //重置行宽 linewidth= 
   0; 
   //重置行高 lastchildheight= 
   0; lineheight= 
   0; } linewidth+=childwidth; 
   //取最大行高(用于应对特殊字体大小) lineheight=Math.max(lastchildheight,childheight); lastchildheight=lineheight; 
   //特殊情况,处理最后一个child(有可能该行只有一个view,也可能最后一个view刚好处于最后一行最后位置) 
   if(i==childcount- 
   1){ factwidth=Math.max(widthsize,linewidth); factheight+=lastchildheight; lastchildheight= 
   0; lineheight= 
   0; linewidth= 
   0; } } 
   //把测量结果设置为父级宽高 setMeasuredDimension(widthmode==MeasureSpec.EXACTLY?widthsize:factwidth, heightmode==MeasureSpec.EXACTLY?heightsize:factheight); } 
   / 每行view的集合*/ 
   private List 
   
     > AllChildView= 
    new ArrayList 
    
      >(); 
     / 每行高度的集合*/ 
     private List 
     
       LineHeight= 
      new ArrayList 
      
        (); 
       / * 排版 (横向排列) * */ 
       @SuppressLint( 
       "DrawAllocation") 
       @Override 
       protected 
       void 
       onLayout( 
       boolean changed, 
       int l, 
       int t, 
       int r, 
       int b) { factwidth=getMeasuredWidth(); AllChildView.clear(); LineHeight.clear(); List 
       
         linelist= 
        new ArrayList 
        
          (); 
         //计算每行可以放view的个数,并放进集合 
         int childcount=getChildCount(); 
         for( 
         int i= 
         0;i 
         
           int childwidth=view.getMeasuredWidth()+marginLayoutParams.leftMargin+marginLayoutParams.rightMargin; 
          int childheight=view.getMeasuredHeight()+marginLayoutParams.topMargin+marginLayoutParams.bottomMargin; 
          //每行子view加起来的宽度大于父级宽度 就把该行子view集合放进所有行的集合里 
          if(linewidth+childwidth>=factwidth){ LineHeight.add(lastchildheight); 
          //行高集合 AllChildView.add(linelist); 
          //行数集合 
          //重置 linewidth= 
          0; lastchildheight= 
          0; 
          //重新创建一个集合 linelist= 
          new ArrayList 
          
            (); } 
           //取每行的最大高度 lineheight=Math.max(childheight,lastchildheight); lastchildheight=lineheight; linewidth+=childwidth; 
           //每行的view集合 linelist.add(view); 
           //如果最后一行没有大于父级宽度,需要特殊处理 
           if(i==childcount- 
           1){ LineHeight.add(lastchildheight); 
           //行高集合 AllChildView.add(linelist); 
           //行数集合 lastchildheight= 
           0; linewidth= 
           0; } } 
           int left= 
           0; 
           int top= 
           0; 
           //设置子view的位置 
           for( 
           int w= 
           0;w 
            
            //总共多少行 linelist=AllChildView.get(w); lineheight=LineHeight.get(w); 
            for( 
            int m= 
            0;m 
             
             //每行排版 View childview=linelist.get(m); 
             //隐藏状态的子view不参与排版 
             if(childview.getVisibility()==View.GONE){ 
             continue; } marginLayoutParams=(MarginLayoutParams) childview.getLayoutParams(); 
             int cleft=left+marginLayoutParams.leftMargin; 
             int ctop=top+marginLayoutParams.topMargin+(lineheight/ 
             2-childview.getHeight()/ 
             2); 
             int cright=cleft+childview.getMeasuredWidth(); 
             int cbottom=ctop+childview.getMeasuredHeight(); childview.layout(cleft, ctop, cright, cbottom); left+=childview.getMeasuredWidth()+marginLayoutParams.leftMargin+marginLayoutParams.rightMargin; } 
             //每行排完之后重新设置属性 left= 
             0; top+=lineheight; } } 
             // @Override 
             // public LayoutParams generateLayoutParams(AttributeSet attrs) { 
               
             // return new MarginLayoutParams(getContext(), attrs); 
             // } } 
             
            
           
          
         
        
       
      
     
    
  

FlowLayoutActivity:

import java.util.Random; import android.app.Activity; import android.graphics.Color; import android.os.Bundle; import android.view.Gravity; import android.view.ViewGroup.LayoutParams; import android.view.ViewGroup.MarginLayoutParams; import android.widget.TextView; public class FlowLayoutActivity extends Activity { private String mNames[] = { "welcome","android","TextView", "apple","jamy","kobe bryant", "jordan","layout","viewgroup", "margin","padding","text","地方了开始讲道理","第三方","的飞洒", "name","type","search","logcat","逗你玩", "罚款圣诞节疯狂绝对是垃圾分类看见冻死了快解放了跨世纪的离开房间了少打飞放得开收垃圾费考虑到就是浪费", "发神经" }; private Random random; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_flow_layout); random=new Random(); init(); } private void init() { FlowLayout flowLayout=(FlowLayout) findViewById(R.id.flowlayout); MarginLayoutParams lp = new MarginLayoutParams( LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT); lp.leftMargin = 5; lp.rightMargin = 5; lp.topMargin = 5; lp.bottomMargin = 5; int a=0,i=0; while(a<40){ ++a; i=random.nextInt(mNames.length); TextView view = new TextView(this); view.setText(mNames[i]); view.setTextColor(Color.WHITE); view.setGravity(Gravity.CENTER); view.setBackground(getResources().getDrawable(R.drawable.textview_bg)); if(i%3==0){ view.setTextSize(20); } view.setTextColor(Color.rgb(random.nextInt(255), random.nextInt(255), random.nextInt(255))); flowLayout.addView(view,lp); } } }

R.layout.activity_flow_layout:

 
  
    "http://schemas.android.com/apk/res/android" xmlns:tools= 
   "http://schemas.android.com/tools" android:layout_width= 
   "match_parent" android:layout_height= 
   "match_parent" android:orientation= 
   "vertical" 
   tools:context= 
   "com.example.mytoolutils.FlowLayoutActivity" > < 
   com 
   .example 
   .mytoolutils 
   .FlowLayout android:id= 
   "@+id/flowlayout" android:layout_width= 
   "match_parent" android:layout_height= 
   "wrap_content" android:background= 
   "@android:color/holo_blue_light" > 
   com 
   .example 
   .mytoolutils 
   .FlowLayout> 
  
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/199669.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月20日 下午12:29
下一篇 2026年3月20日 下午12:29


相关推荐

  • Cursor 0.47最新MCP配置教程,手把手教你搞定!

    Cursor 0.47最新MCP配置教程,手把手教你搞定!

    2026年3月16日
    2
  • TreeNode实用方法

    TreeNode实用方法treeToString 获取输出树节点以及所有子节点并生成字符串 convertArrTo 数组生成树

    2026年3月18日
    2
  • ffmpeg参数详解_ffmpeg个版本

    ffmpeg参数详解_ffmpeg个版本-c:v指定编码器默认值:mpeg4mpeg4编码器,编码速度快,清晰度不够,处理后的文件比较大libx264编码器,编码比较慢,清晰度高,处理后的文件比较小-preset编码速度默认值:medium当编码器指定为libx264时可以指定该参数,编码速度越慢,清晰度越高,处理后的文件大小相同可选值:ultrafast、superfast、veryfast、

    2026年1月23日
    9
  • ModelState.AddModelError使用

    ModelState.AddModelError使用

    2022年3月1日
    43
  • ntp网络时间协议_ntp网络时间协议特性

    ntp网络时间协议_ntp网络时间协议特性NTP是网络时间协议(NetworkTimeProtocol),它是用来同步网络中各个计算机的时间的协议。  原理:NTP要提供准确的时间,就必须有准确的时间来源,那可以用格林尼治时间吗?答案是否定的。因为格林尼治时间是以地球自转为基础的时间计量系统,但是地球每天的自转是有些不规则的,而且正在缓慢加速,因此,格林尼治时间已经不再被作为标准时间使用。新的标准时间,是由原子钟报时的

    2022年10月12日
    3
  • 怎么查看idea的激活码【2021.8最新】

    (怎么查看idea的激活码)JetBrains旗下有多款编译器工具(如:IntelliJ、WebStorm、PyCharm等)在各编程领域几乎都占据了垄断地位。建立在开源IntelliJ平台之上,过去15年以来,JetBrains一直在不断发展和完善这个平台。这个平台可以针对您的开发工作流进行微调并且能够提供…

    2022年3月26日
    59

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号