效果图
瀑布流实现历史记录(根据宽高来设置换行)和数据库-LMLPHP
1主页面布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="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=".MainActivity">
    <!--标题放大镜和输入框的自定义view-->
    <com.example.day04_water_fall02.TitleBarView
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <!--搜索历史的自定义view和热门搜索公用一个-->
    <com.example.day04_water_fall02.WaterGroupNameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="搜索历史"
        android:textColor="#00ff00" />
    <!--显示内容的自定义view-->
    <com.example.day04_water_fall02.WaterFall
        android:id="@+id/water_fall_search"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp" />
    <Button
        android:id="@+id/clear"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="清空历史"
        />
    <!--热门搜索的自定义view-->
    <com.example.day04_water_fall02.WaterGroupNameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="热门搜索"
        android:textColor="#00ffff" />
    <!--显示内容的自定义view-->
    <com.example.day04_water_fall02.WaterFall
        android:id="@+id/water_fall_hot"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp" />
</LinearLayout>

2.标题放大镜和输入框的布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
<ImageView
    android:id="@+id/search_title"
    android:layout_width="20dp"
    android:layout_height="20dp"
    android:layout_centerVertical="true"
    android:background="@drawable/search"
    />
    <EditText
        android:id="@+id/edit_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/search_title"
        />
</RelativeLayout>

3.放大镜和输入框的自定义view类TitleBarView继承LinearLayout

package com.example.day04_water_fall02;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;

public class TitleBarView extends LinearLayout {
    private Context context;
    private EditText editText;
    private ImageView imageView;
    public TitleBarView(Context context) {
        super(context);
        this.context = context;
        init();
    }

    public TitleBarView(Context context,  AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        init();
    }
    //第三步:设置成员变量
    private OnButtonClientListener onButtonClientListener;
    private void init() {
        View view = View.inflate(context,R.layout.title,null);
        editText = view.findViewById(R.id.edit_title);
        imageView = view.findViewById(R.id.search_title);
        imageView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                //第六步:在将要回调的地方,首先判断非空
                if(onButtonClientListener!=null){
                    //第七步:执行回调方法,传参
                    onButtonClientListener.onButtonClick(editText.getText().toString());
                }
            }
        });
        addView(view);
    }
    //第一步 定义一个接口
    public interface OnButtonClientListener{
        //第二步 写好方法和回传参数
        void onButtonClick(String str);
    }
    //第四步:传入,并且给成员变量赋值
    //第五步:在想要接受回调的地方,调用set方法,设置监听详见MainActivity第41行
    public void setOnButtonClientListener(OnButtonClientListener onButtonClientListener) {
        this.onButtonClientListener = onButtonClientListener;
    }
}

4.自定义属性的xml(在values下建一个 attrs.xml)

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomTurntable">
        <!--name为属性的名字,可以随意起,只要符合规则看的懂-->
        <!--format为属性内容的类型-->
        <attr name="text" format="string"/>
    </declare-styleable>
   <declare-styleable name="WeekFlowLayout">
       <attr name="textColor" format="color"/>
   </declare-styleable>
</resources>

5.实现搜索历史和热门搜索的自定义view类WaterGroupNameLayout需要继承TextView

package com.example.day04_water_fall02;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import android.widget.TextView;

@SuppressLint("AppCompatCustomView")
public class WaterGroupNameLayout extends TextView {
    public WaterGroupNameLayout(Context context) {
        super(context);
    }

    public WaterGroupNameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        //自定义属性
        //第一步,在value文件夹下建立一个attr.xml文件,
        //第二步,写<declear....标签
        //第三步,写<attr 标签 name:方法名 format:属性
        //第四步,在自定义view中的有AttributeSet的构造方法里写以下代码
        TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.WeekFlowLayout);
        int color = array.getColor(R.styleable.WeekFlowLayout_textColor, Color.BLUE);
        //第五步,在布局文件根控件中写xmlns:app="http://schemas.android.com/apk/res-auto"
        //第六步,在想要调用自定义属性的控件中添加app:方法名=“想要设置的值”
        setTextColor(color);
        //第七步最后要回收
        array.recycle();
    }
}

6.显示内容的自定义view类WaterFall需要继承LinearLayout

package com.example.day04_water_fall02;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;

public class WaterFall extends LinearLayout {
    /**
     * 孩子中最高的一个
     */
    private int mChildMaxHeight;
    /**
     * 每个孩子的左右间距
     * 默认是20单位是px
     */
    private int mHSpace = 20;
    /**
     * 每一行的上下间距
     * 20是默认值,单位是px
     */
    private int mVSpace = 20;

    public WaterFall(Context context) {
        super(context);
    }

    public WaterFall(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //拿到父容器的宽高以及计算模式
        int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
        int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
        //测量孩子的大小
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        //寻找孩子中最高的的一个孩子,找到的值会放在mChildMaxHeight变量中
        findMaxChildMaxHeight();
        //初始化值
        int left = 0, top = 0;
        //循环所有的孩子
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View view = getChildAt(i);
            //判断是否是一行的开头
            if (left != 0) {
                //如果子元素的宽度+左边距>大于夫需要换行了,因为放不下
                if ((left + view.getMeasuredWidth()) > sizeWidth) {
                    //计算出下一行的top
                    top += mChildMaxHeight + mVSpace;
                    //重新计算 left需要归零
                    left = 0;
                }
            }

            left += view.getMeasuredWidth() + mHSpace;
        }
        setMeasuredDimension(sizeWidth, (top + mChildMaxHeight) > sizeHeight ? sizeHeight : top + mChildMaxHeight);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);

        findMaxChildMaxHeight();
        //开始安排孩子的位置
        //出始化
        int left = 0, top = 0;
        //循环所有的孩子
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View view = getChildAt(i);
            //判断是否是一行的开头
            if (left != 0) {
                //需要换行了,因为放不下
                if ((left + view.getMeasuredWidth()) > getWidth()) {
                    //计算出下一行的top
                    top += mChildMaxHeight + mVSpace;
                    left = 0;
                }
            }
            //安排孩子的位置
            view.layout(left, top, left + view.getMeasuredWidth(), top + mChildMaxHeight);
            left += view.getMeasuredWidth() + mHSpace;
        }
    }

    //寻找孩子中最高的一个孩子
    private void findMaxChildMaxHeight() {
        mChildMaxHeight = 0;
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            //返回指定位置的视图
            View view = getChildAt(i);
            //指定的高度是否打于最高的高度
            if (view.getMeasuredHeight() > mChildMaxHeight) {
                //如果大于就把指定的高度赋值最高的
                mChildMaxHeight = view.getMeasuredHeight();
            }
        }
    }
}

7.在drawable下创建shape textshape.xml用来给显示内容的textview自定义边框

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#00000000"/>
    <corners android:radius="10px"/>
    <stroke
        android:width="1px"
        android:color="#999999"
        />
</shape>

8.MainActivity页面分别获取自定义view的布局

package com.example.day04_water_fall02;

import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.example.day04_water_fall02.db.WaterDao;

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

public class MainActivity extends AppCompatActivity {

    private WaterFall waterFall;
    private EditText editText;
    private Button button;
    private List<String> list = new ArrayList<>();
    private WaterFall waterFall_seach;
    private WaterFall waterFall_hot;
    private TitleBarView title;
    private Button button1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    }

    private void init() {
        waterFall_seach = findViewById(R.id.water_fall_search);
        waterFall_hot = findViewById(R.id.water_fall_hot);
        title = findViewById(R.id.title);
        button = findViewById(R.id.clear);
        //点击
        title.setOnButtonClientListener(new TitleBarView.OnButtonClientListener() {
            private String uuid1;
            @Override
            public void onButtonClick(final String str) {
                if (str.equals("")) {
                    return;
                } else {
                   final TextView textView = new TextView(MainActivity.this);
                    //随机字符串当做唯一标识
                    UUID uuid = UUID.randomUUID();
                    textView.setTag(uuid);
                    uuid1 = String.valueOf(textView.getTag());
                    WaterDao.getIntance(MainActivity.this).add(str, uuid1);
                    textView.setText(str);
                    textView.setTextSize(20);
                    textView.setBackgroundResource(R.drawable.textshape);
                    waterFall_seach.addView(textView);
                    //长按删除
                    textView.setOnLongClickListener(new View.OnLongClickListener() {
                        @Override
                        public boolean onLongClick(View v) {
                            WaterDao.getIntance(MainActivity.this).del(uuid1);
                            waterFall_seach.removeView(v);
                            return true;
                        }
                    });
                    //点击提示
                    textView.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            Toast.makeText(MainActivity.this,"点击了"+str,Toast.LENGTH_SHORT).show();
                        }
                    });
                }
            }
        });
        //第二次进入页面从数据库查出来展示
        List<WaterBean> select = WaterDao.getIntance(MainActivity.this).select();
        if (select.size() != 0) {
            for (final WaterBean s : select) {
                final TextView textView = new TextView(MainActivity.this);
                textView.setText(s.getName());
                textView.setTextSize(20);
                textView.setBackgroundResource(R.drawable.textshape);
                waterFall_seach.addView(textView);
                //长按删除
                textView.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        WaterDao.getIntance(MainActivity.this).del(s.getUuid());
                        waterFall_seach.removeView(v);
                        return true;
                    }
                });
                //点击提示
                textView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(MainActivity.this,"点击了"+s.getName(),Toast.LENGTH_SHORT).show();
                    }
                });
            }
        }
        //热门搜索
        String[] str = new String[]{
                "水煮肉片", "锅包肉", "鱼香肉丝", "水煮鱼", "木须肉", "地三鲜", "尖椒干豆腐"
                , "干锅土豆片", "汉堡", "炸鸡", "可乐", "啤酒", "火腿", "米饭"
        };
        for (int i = 0; i < str.length; i++) {
            TextView textView1 = new TextView(MainActivity.this);
            textView1.setText(str[i]);
            textView1.setTextSize(20);
            textView1.setBackgroundResource(R.drawable.textshape);
            waterFall_hot.addView(textView1);
        }
        //清空历史
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                WaterDao.getIntance(MainActivity.this).delAll();
                waterFall_seach.removeAllViews();
            }
        });
    }
}

9.使用数据库需要的bean 里面有两个参数其中uuid是为了根据UUID来删除单个

package com.example.day04_water_fall02;

public class WaterBean {
    private String name;
    private String uuid;

    public WaterBean(String name, String uuid) {
        this.name = name;
        this.uuid = uuid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getUuid() {
        return uuid;
    }

    public void setUuid(String uuid) {
        this.uuid = uuid;
    }
}

10.数据库创建表页面

package com.example.day04_water_fall02.db;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class SqlitHelper extends SQLiteOpenHelper {
    public SqlitHelper(Context context) {
        super(context, "Water.db", null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table waters(id integer primary key autoincrement," +
                "name text," +
                "uuid text)");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

11.数据库写增删改查方法页

package com.example.day04_water_fall02.db;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.example.day04_water_fall02.WaterBean;

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

public class WaterDao {
    private static WaterDao intance;
    private Context context;
    private final SqlitHelper sqlitHelper;
    private final SQLiteDatabase sb;

    public WaterDao(Context context) {
        this.context = context;
        sqlitHelper = new SqlitHelper(context);
        sb = sqlitHelper.getWritableDatabase();
    }

    public static WaterDao getIntance(Context context) {
        if (intance==null){
            intance = new WaterDao(context);
        }
        return intance;
    }
    //添加
    public void add(String name,String uuid){
        ContentValues values = new ContentValues();
        values.put("name",name);
        values.put("uuid",uuid);
        sb.insert("waters",null,values);
    }
    //删除
    public void del(String uuid ){
        sb.delete("waters","uuid = ?",new String[]{uuid});
    }
    //删除所有
    public void delAll(){
        sb.delete("waters",null,null);
    }
    //查询
    public List<WaterBean> select(){
        List<WaterBean> list = new ArrayList<>();
        Cursor waters = sb.query("waters", null, null, null, null, null, null, null);
        if(waters!=null){
            while (waters.moveToNext()){
                String name = waters.getString(waters.getColumnIndex("name"));
                String uuid = waters.getString(waters.getColumnIndex("uuid"));
                WaterBean waterBean = new WaterBean(name,uuid);
                list.add(waterBean);
            }
        }
        return list;
    }
}

12-03 16:29