自动换行容器的自定义FlowContainer

固定容器的宽高信息,往容器内添加子view,子view按行排列,当宽度快超出容器宽度时,换行继续排列

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

public class FlowContainer extends ViewGroup{
    private final static int VIEW_MARGIN = 2;//子view自己的间距
    private int clientWidth;
    public FlowContainer(Context context) {
        super(context);
    }
    public FlowContainer(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public FlowContainer(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
        //获取整个flowContainer的宽度
        clientWidth = MeasureSpec.getSize(widthMeasureSpec);
        //从第一行开始测量
        int row = 0;
        int lengthX = 0;
        int lengthY = 0;
        //循环测量子view,得到子view最终占flowContainer宽高
        for(int index = 0; index < getChildCount(); index++){
            View child = getChildAt(index);
            if(child.getVisibility() == View.GONE){
                continue;
            }
            //子view的宽高不限制,获取子view自己给定的宽高信息
            child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
            int width = child.getMeasuredWidth();
            int height = child.getMeasuredHeight();

            lengthX += width + VIEW_MARGIN;
            lengthY = row * (height + VIEW_MARGIN) + VIEW_MARGIN + height;
            //第一行 所有子view的宽度>父容器宽度  准备换行
            if((lengthX + VIEW_MARGIN) > clientWidth){
                //下一行的 宽度开始计算
                lengthX = width + VIEW_MARGIN;
                row++;
                //下一行后 总宽度
                lengthY = row * (height +VIEW_MARGIN) + VIEW_MARGIN + height;
            }
        }
        lengthY = lengthY + VIEW_MARGIN;
        //设置容器所需的宽度和高度(宽度为父宽,高为所有子view算出来的)
        setMeasuredDimension(clientWidth, lengthY);
    }
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        int row = 0;
        top = VIEW_MARGIN;
        int lengthX = left;
        int lengthY = 0;
        for(int i = 0; i < getChildCount(); i++){
            View child = this.getChildAt(i);
            if(child.getVisibility() == View.GONE){
                continue;
            }
            int width = child.getMeasuredWidth();
            int height = child.getMeasuredHeight();

            lengthX += width + VIEW_MARGIN;
            lengthY = row * (height + VIEW_MARGIN) + VIEW_MARGIN + height + top;
            if((lengthX + VIEW_MARGIN) > right){
                lengthX = width + VIEW_MARGIN + left;
                row++;
                lengthY = row * (height + VIEW_MARGIN) +VIEW_MARGIN + height + top;
            }
            child.layout(lengthX - width, lengthY - height, lengthX, lengthY);
        }
    }
}

布局文件如下:这里的容器高度随子view的多少撑高,但是高度超出屏幕后无法滑动,所以外部套一个scrollView来滑动

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.whc.view.FlowContainer
        android:id="@+id/flow_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </com.whc.view.FlowContainer>
</ScrollView>

activity中就随便放置多个textView来展示效果吧

import java.util.ArrayList;
import java.util.List;

import com.whc.view.FlowContainer;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class FlowActivity extends Activity {

    private FlowContainer flowContainer;
    private List<String> list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.flow_activity);
        flowContainer = (FlowContainer) findViewById(R.id.flow_container);

        initData();
        initView();
    }

    private void initView() {
        for(int i = 0; i<list.size(); i++){
            TextView tv = new TextView(this);
            tv.setText(list.get(i));
            flowContainer.addView(tv);
        }
    }

    private void initData() {
        list = new ArrayList<String>();
        for(int i = 0; i < 500; i++){
            list.add("第"+i+"几个view");
        }
    }
}

这样容器内的子textView就会横向排列,当快超出容器宽度时,换行继续

时间: 2024-08-02 07:06:46

自动换行容器的自定义FlowContainer的相关文章

STL中map容器使用自定义key类型报错详解

引言 STL的map容器中,key的类型是不是随意的呢? 实践 编写测试代码 定义一个结构体来试试: struct a { char* pName; int m_a; }; map<a, int> mp; a a1; a1.m_a = 100; a1.pName = "a1"; a a2; a2.m_a = 200; a2.pName = "a2"; mp.insert(std::make_pair(a1, 1)); mp.insert(std::mak

STL容器set()---&gt;自定义数据类型

set容器中不能插入重复的元素,需要其插入的元素有比较大小(<).相等(==) 的逻辑判断,这是因为set中的元素是有序排列, 默认从小到大排列 std::set<type,std::less<type>> mySet ; 等同于 std::set<type> mySet; 所以需要元素的数据类型 具有 大小.相等判断的函数. 对于编译器标准定义数据类型(如 int,float,double等),系统已经定义大小判断:但对于自定义数据就要注意自己动手添加这些函数.

stl容器之--自定义结构体作为stl容器元素成员的使用

自定义结构体作为stl容器元素成员的设计要求之一是:在对待自定义类型时和内置类型必须是一致的,甚至自定义类型的支持更好. <C++标准程序库>: set和multiset set和multiset会根据特定的排序准则,自动将元素排序.两者不同在于multiset允许重复而set不允许. 只要是assignable.copyable.comparable(根据某个排序准则)的型别T,都可以成为set或multiset的元素型别.没有传入特别排序准则,就采用缺省准则less(这是一个仿函数,以op

MySQL 5.6容器使用自定义配置文件的权限问题

提出问题: ???????? 在使用Rancher2.0.2部署一个mysql deployment时,我们会发现,如果只设置/var/lib/mysql数据目录时,mysql容器(pod)能够正常启动,一旦数据目录和配置目录同时挂载时,mysql容器(pod)就无法启动. 解决思路: ???????? 我们运行一个MySQL 5.6的容器,观察正常运行时,容器内数据目录.配置目录.日志目录的所有者及权限,并查看容器内组文件,对比宿主机中的组文件,应该能找到原因所在,进而可以通过在宿主机中设置正

Django容器(上): 自定义基础镜像

开始之前 某个 Python 项目,基于 Python:3.6 与 Django:1.11 框架开发,希望项目能够容器化,然后可以通过 docker-compose 等工具编排容器/应用,本篇文章的目标是自定义Django基础镜像. 与<为什么需要自定义一个基础镜像?>文章相同,基础镜像作用是为项目镜像提供支持.它构建在 Python 官方镜像之上,添加项目一些需要的扩展模块,例如 Django.pymysql.Gunicorn等常用模块,具体以项目实际需求为准. 最后为能够高效的处理静态文件

Spring Boot使用嵌入式容器,自定义Filter如何配置?

Listener.Filter和Servlet是Java Web开发过程中常用的三个组件,其中Filter组件的使用频率最高,经常被用来做简单的权限处理.请求头过滤和防止XSS***等.如果我们使用的是传统的Spring MVC进行开发,那么只需要在Tomcat的web.xml文件中进行如下配置即可: <!-- 配置Listener --> <listener> <listener-class>org.springframework.web.util.WebAppRoo

用类模板实现容器存储自定义数据类型(类似于STL里面的vector)

上一节里面已经提到了,用类模板存储自定义的数据类型,如Teacher类型时,需要重载Teacher类的拷贝构造函数,"="操作符,"<<"操作符,特别要注意深拷贝和浅拷贝的问题. 例如: 1 //1.myvector.h文件 2 #ifndef MYVECTOR_H 3 #define MYVECTOR_H 4 5 #include <iostream> 6 using namespace std; 7 8 template<typen

Docker使用自定义网络实现容器互联

目录 容器互联 步骤 新建网络 连接容器 测试连接 添加已经运行的容器到自定义网络 容器互联 随着 Docker 网络的完善,强烈建议大家将容器加入自定义的 Docker 网络来实现互联,而不是使用 --link 参数 步骤 新建网络 docker network create -d bridge my-net 连接容器 docker run -it --rm --name busybox1 --network my-net busybox sh docker run -it --rm --na

容器互联

1.linking系统 docker有一个linking系统可以连接多个容器.它会创建一对父子关系,父容器可以看到所选择的自容器的信息.该系统会在源和接受容器之间建立一个隧道.接受容器(父容器)可以看到源容器指定的信息. linking系统的好处:子容器可以不用暴露到局域网中,子容器只能被父容器访问. 创建步骤: 1)先创建子容器,自定义容器的名称 2)再创建父容器 --link标记的格式:--link name:alias,name是子容器的名称,alias是这个子容器的别名. 3)测试 执行