自定义ViewGroup实现流式布局
2016-12-10 21:22
537 查看
要实现的就是这样的效果
指定每个孩子测量规则
实例化内部类【行】,进行逻辑处理(行宽够用,则加入孩子;行宽不够用,则换行)
重写Onlayout方法
for循环遍历行集合(拿到每行,调用行类的layout方法分配行内孩子位置,纵坐标t不断加上行宽和间隔)
FlowLayout.java
根据父视图的规则模式确定控件孩子的规则模式
测量每个孩子
设定父视图尺寸
自定义ViewGroup
重写OnMeasure方法指定每个孩子测量规则
实例化内部类【行】,进行逻辑处理(行宽够用,则加入孩子;行宽不够用,则换行)
重写Onlayout方法
for循环遍历行集合(拿到每行,调用行类的layout方法分配行内孩子位置,纵坐标t不断加上行宽和间隔)
FlowLayout.java
public class FlowLayout extends ViewGroup { private int horizontolSpacing=UIutil.dip2px(13);//横向控件间隔 private int verticalSpacing=UIutil.dip2px(13);//纵向行间隔 private Line currentline;// 当前的行 private int useWidth=0;// 当前行使用的宽度,即当前行拥有的控件孩子所占用的宽度 private List<Line> mLines=new ArrayList<>();//保存行的行集合 private int width;//FlowLayout宽度 public FlowLayout(Context context) { super(context); } public FlowLayout(Context context, AttributeSet attrs) { super(context, attrs); } public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } // 测量 当前控件Flowlayout // 父类是有义务测量每个孩子的 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { /**初始化【行】对象相关的变量*/ mLines.clear();//清空行集合 currentline=null; useWidth=0; /**重新指定每个孩子测量规则*/ int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); width = widthSize-getPaddingLeft()-getPaddingRight(); int height = heightSize-getPaddingBottom()-getPaddingTop(); // 获取到父控件FlowLayout宽和高 int childeWidthMode; int childeHeightMode; // 为了测量每个孩子 需要指定每个孩子测量规则 childeWidthMode = widthMode == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : widthMode; childeHeightMode = heightMode == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : heightMode; int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,childeWidthMode); int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,childeHeightMode); /**开始【行】操作*/ currentline=new Line();// 创建了第一行 /**遍历FlowLayout中的孩子,分配每行拥有的控件*/ for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i);//拿到FlowLayout的孩子 System.out.println("孩子的数量:"+getChildCount()); child.measure(childWidthMeasureSpec,childHeightMeasureSpec);//测量每个孩子 int measuredWidth = child.getMeasuredWidth();//孩子控件宽度即所占FlowLayout的宽度 useWidth += measuredWidth;// 让当前行加上使用的长度 if(useWidth <= width){//控件孩子们占用的宽度小于当前行宽(行宽=FlowLayout的宽) currentline.addChild(child);//这时候证明当前的孩子是可以放进当前的行里,放进去 useWidth += horizontolSpacing;//加上孩子之间的间隔 if(useWidth>width){//使用宽度大于行宽 //换行 newLine(); } }else{ if(currentline.getChildCount() < 1){ currentline.addChild(child);// 保证当前行里面最少有一个孩子 } newLine();//换行 } } if(!mLines.contains(currentline)){ mLines.add(currentline);// 添加当前行 } int totalHeight = 0;//总高度,使用的高度 for(Line line : mLines){ totalHeight += line.getHeight();//等于所有控件的高度和 } totalHeight += verticalSpacing*(mLines.size() - 1) + getPaddingTop() + getPaddingBottom();//加上所有行与行之间的间隔,paddingtop,paddingbottom。 System.out.println(totalHeight); /**设置父视图FlowLayout的尺寸*/ setMeasuredDimension(width+getPaddingLeft()+getPaddingRight(),resolveSize(totalHeight, heightMeasureSpec));//resolveSize方法是通过比较当前的高度判定规则与父视图的规则以及totalHeight与父视图尺寸的大小,选取适当高度尺寸;看源码很容易理解 } /**换行*/ private void newLine() { mLines.add(currentline);// 记录之前的行 currentline=new Line(); // 创建新的一行 useWidth=0;//新的一行重置使用宽度 } // 分配每个孩子的位置 @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { l+=getPaddingLeft(); t+=getPaddingTop();//改变起点 for (int i = 0; i < mLines.size(); i++) { Line line = mLines.get(i); line.layout(l,t);//交给每一行去分配 t+= line.getHeight() + verticalSpacing;//改变高度t+=行高、行与行间隔之和 } } /**内部类行*/ private class Line { int height = 0; //当前行的高度 int lineWidth = 0;//行宽 private List<View> children = new ArrayList<View>();//控件集合 /** * 添加一个孩子 * * @param child */ public void addChild(View child) { children.add(child); if (child.getMeasuredHeight() > height) { height = child.getMeasuredHeight();//行高等于当前行最大孩子的高 } lineWidth += child.getMeasuredWidth(); } /**当前行高*/ public int getHeight() { return height; } /** * 返回孩子的数量 * * @return */ public int getChildCount() { return children.size(); } /**在一行内分配各孩子位置*/ public void layout(int l, int t) { lineWidth += horizontolSpacing*(children.size()-1);//行宽+=当前行所有孩子之间的水平间隔 int surplusChild = 0;//将剩余宽度平均分配给每个孩子的宽度 int surplus = width - lineWidth;//当前行分配孩子后剩余的宽度 if(surplus > 0 && children.size() > 0){ System.out.println("children:" + children.size()); surplusChild = surplus / children.size();//算出将剩余宽度平均分配给每个孩子的宽度 } for (int i = 0; i < children.size(); i++) { View child = children.get(i); child.layout(l,t,l+child.getMeasuredWidth()+surplusChild,t+child.getMeasuredHeight());//确定当前控件的位置,绘出控件 l+=child.getMeasuredWidth()+ surplusChild+ horizontolSpacing ;//计算下一个孩子的横坐标(加上当前控件宽度,控件间隔,分给每个控件的剩余宽度) } } } }
自定义ViewGroup的OnMeasure():
指定控件孩子的判定规则根据父视图的规则模式确定控件孩子的规则模式
测量每个孩子
设定父视图尺寸
在Activity中应用自定义布局
创建并实例化一些文本控件显示文本,将文本控件填充到FlowLayout中。private List<String> datas; public void onCreate(){ ScrollView scrollView = new ScrollView(getContext());//最上层布局ScrollView scrollView.setBackgroundResource(R.drawable.grid_item_bg_normal); FlowLayout layout= new FlowLayout(getContext());//实例化FlowLayout int padding=UIutil.dip2px(13); layout.setPadding(padding, padding, padding, padding); int backColor = 0xffcecece;//背景色,用于创建Drawable的背景色 Drawable pressedDrawable = DrawableUtil.createShape(backColor);//创建点击后的显示的Drawable /**根据文本数据的数量确定文本控件*/ for (int i = 0; i < datas.size(); i++) { TextView textView = new TextView(getContext()); final String s = datas.get(i); textView.setText(s); Random random=new Random(); //创建随机对象 int red = random.nextInt(255)+1; int green = random.nextInt(255)+1; int blue = random.nextInt(255)+1;//创建随机色 int color = Color.rgb(red, green, blue);//范围 0-255 随机色 GradientDrawable createShape = DrawableUtil.createShape(color);// 默认显示的图片 StateListDrawable selectorDrawable = DrawableUtil.createSelectorDrawable(pressedDrawable, createShape);//Drawable选择器 /**设置文本控件参数和点击事件*/ textView.setBackground(selectorDrawable);//将选择器设成背景 textView.setTextColor(Color.WHITE); textView.setTextSize(18); textView.setGravity(Gravity.CENTER); int textPaddingV = UIutil.dip2px(4); int textPaddingH = UIutil.dip2px(7); textView.setPadding(textPaddingH,textPaddingV,textPaddingH,textPaddingV); textView.setClickable(true); textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getContext(),s,Toast.LENGTH_SHORT).show(); } }); /**FlowLayout添加孩子*/ layout.addView(textView,new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,-2));// -2 包裹内容 } /**scrollView添加FlowLayout为孩子*/ scrollView.addView(layout); setContentView(scrollView); }
相关文章推荐
- 自定义ViewGroup实现流式布局
- 自定义viewGroup 实现 流式布局
- 自定义ViewGroup实现流式布局
- ViewGroup2——自定义实现流式布局
- Android自定义ViewGroup实现流式布局
- android 自定义ViewGroup实现流式布局过程
- 自定义ViewGroup实现流式布局(支持ViewGroup Padding, 子View margin,每行高度可以不一样)
- android之自定义ViewGroup和自动换行的布局的实现
- android之自定义ViewGroup和自动换行的布局的实现
- Android自定义ViewGroup自动换行实现滑动任意布局及事件处理效果
- android之自定义ViewGroup和自动换行的布局的实现
- 自定义ViewGroup(3):自定义流式布局
- android之自定义ViewGroup和自动换行的布局的实现
- android之自定义ViewGroup和自动换行的布局的实现
- android之自定义ViewGroup和自动换行的布局的实现
- android自定义viewgroup实现等分格子布局
- android之自定义ViewGroup和自动换行的布局的实现
- android之自定义ViewGroup和自动换行的布局的实现
- android之自定义ViewGroup和自动换行的布局的实现
- 基于ViewGroup自定义自动换行的布局的实现(用于备忘)