Config

package com.tan.searchhistory.constants;public class Config {//数据库public static final int DATABASE_VERSION = 1;//如果数据库升级此处需+1public static final String DATABASE_NAME = "search.db";public static final String DB_PATH = "/search_db";public static final int DEFAULT_HISTORY_SEARCH_LIST_NUM = 5;}

DatabaseContext

package com.tan.searchhistory.db;import android.content.Context;
import android.content.ContextWrapper;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;import com.tan.searchhistory.constants.Config;import java.io.File;
import java.io.IOException;/*** 用于支持对存储在SD卡上的数据库的访问**/
public class DatabaseContext extends ContextWrapper {/*** 构造函数* @param    base 上下文环境*/public DatabaseContext(Context base){super(base);}/*** 获得数据库路径,如果不存在,则创建对象对象* @param    name*/ @Overridepublic File getDatabasePath(String name) {//判断是否存在sd卡boolean sdExist = android.os.Environment.MEDIA_MOUNTED.equals(android.os.Environment.getExternalStorageState());if(!sdExist){//如果不存在,Log.e("SD卡管理:", "SD卡不存在,请加载SD卡");return null;}else{//如果存在//获取sd卡路径String dbDir=android.os.Environment.getExternalStorageDirectory().toString();dbDir += Config.DB_PATH;//数据库所在目录String dbPath = dbDir+"/"+name;//数据库路径//判断目录是否存在,不存在则创建该目录File dirFile = new File(dbDir);if(!dirFile.exists())dirFile.mkdirs();//数据库文件是否创建成功boolean isFileCreateSuccess = false;//判断文件是否存在,不存在则创建该文件File dbFile = new File(dbPath);if(!dbFile.exists()){try {isFileCreateSuccess = dbFile.createNewFile();//创建文件} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}elseisFileCreateSuccess = true;//返回数据库文件对象if(isFileCreateSuccess)return dbFile;elsereturn null;}}/*** 重载这个方法,是用来打开SD卡上的数据库的,android 2.3及以下会调用这个方法。** @param    name* @param    mode* @param    factory*/@Overridepublic SQLiteDatabase openOrCreateDatabase(String name, int mode,SQLiteDatabase.CursorFactory factory) {SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);return result;}/*** Android 4.0会调用此方法获取数据库。** @see ContextWrapper#openOrCreateDatabase(String, int,*              SQLiteDatabase.CursorFactory,*              DatabaseErrorHandler)* @param    name* @param    mode* @param    factory* @param     errorHandler*/@Overridepublic SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory,DatabaseErrorHandler errorHandler) {SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);return result;}
}

DBHelper

package com.tan.searchhistory.db;import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;import com.tan.searchhistory.constants.Config;
import com.tan.searchhistory.utils.Dog;public class DBHelper extends SQLiteOpenHelper {private static final String TAG = DBHelper.class.getSimpleName();private static DatabaseContext mDatabaseContext;private static DBHelper mInstance;private Context mContext;/**** @param context*/private DBHelper(Context context) {super(context, Config.DATABASE_NAME, null, Config.DATABASE_VERSION);this.mContext = context;}public static DBHelper getInstance (Context context){if (null == mInstance){synchronized (DBHelper.class){if (null == mInstance){mDatabaseContext = new DatabaseContext(context);mInstance = new DBHelper(mDatabaseContext);}}}return mInstance;}@Overridepublic void onOpen(SQLiteDatabase db) {super.onOpen(db);Dog.d(TAG,"--onOpen---");}@Overridepublic void onCreate(SQLiteDatabase db) {Dog.d(TAG , "---onCreate start ---");db.execSQL(DBTable.CREATE_HISTORY);Dog.d(TAG , "---onCreate finish---");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {Dog.d(TAG , "--onUpgrade ---oldVersion=" + oldVersion + "----newVersion=" + newVersion);}
}

DBManager

package com.tan.searchhistory.db;import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;import com.tan.searchhistory.models.HistoryItem;
import com.tan.searchhistory.utils.Dog;import java.util.ArrayList;
import java.util.List;/*** Created by pateo on 17-12-27.*/public class DBManager {protected static final String TAG = DBManager.class.getSimpleName();private static DBManager mInstance;private static DBHelper mDBHelper;private static SQLiteDatabase mDB;private DBManager(Context context){mDBHelper = DBHelper.getInstance(context);mDB = mDBHelper.getReadableDatabase();}public static DBManager getInstance(Context context){if (null == mInstance){synchronized (DBManager.class){if (null == mInstance){mInstance = new DBManager(context);}}}return mInstance;}//获取所有的记录列表public List<HistoryItem> queryAllHistoryList(){mDB = mDBHelper.getReadableDatabase();List<HistoryItem> historyItemList = new ArrayList<HistoryItem>();Cursor cursor = null ;try{cursor = mDB.query(DBTable.HISTORY_TABLE_NAME , null ,null ,null , null ,null , null);Dog.d(TAG , "--queryAllHistoryList---cursor=" + cursor.getCount());if (null != cursor && cursor.getCount() > 0){cursor.moveToFirst();do {int id = cursor.getInt(cursor.getColumnIndex(DBTable.ID));String name = cursor.getString(cursor.getColumnIndex(DBTable.NAME));String createTime = cursor.getString(cursor.getColumnIndex(DBTable.CREATE_TIME));HistoryItem historyItem = new HistoryItem(   name ,  createTime);Dog.d(TAG , " ---queryAllHistoryList----historyItem=" + historyItem);historyItemList.add(historyItem);}while (cursor.moveToNext());}}catch ( Exception e ){Dog.d(TAG , "Exception=" + e.toString());}finally {if (null != cursor){cursor.close();}closeDatabase();}return historyItemList;}//orderby  查询最新的几条数据//查询所有数据public static List<HistoryItem> queryLatestHistoryListByNum(int num){mDB = mDBHelper.getReadableDatabase();List<HistoryItem> historyItemList = new ArrayList<HistoryItem>();Cursor cursor = null ;try{cursor = mDB.query(DBTable.HISTORY_TABLE_NAME , null ,null ,null,null ,null , DBTable.ID + " desc" );Dog.d(TAG, "cursor.size ======" + cursor.getCount());if (cursor != null && cursor.getCount() > 0) {cursor.moveToFirst();do {Dog.d(TAG , "----queryLatestHistoryListByNum----num=" + num + "----historyItemList.size()=" + historyItemList.size());if (historyItemList.size() > num - 1){break;}int id = cursor.getInt(cursor.getColumnIndex(DBTable.ID));String name = cursor.getString(cursor.getColumnIndex(DBTable.NAME));String createTime = cursor.getString(cursor.getColumnIndex(DBTable.CREATE_TIME));HistoryItem historyItem = new HistoryItem(   name ,  createTime);Dog.d(TAG , " ---queryLatestHistoryListByNum----historyItem=" + historyItem);historyItemList.add(historyItem);} while (cursor.moveToNext());}}catch ( Exception  e ){Dog.d(TAG , "Exception=" + e.toString());}finally {if (null != cursor){cursor.close();}closeDatabase();}return historyItemList;}//查询最新的一条数据public HistoryItem queryLatestHistoryItem (){HistoryItem historyItem = null ;mDB = mDBHelper.getReadableDatabase();Cursor cursor = null ;try{cursor = mDB.query(DBTable.HISTORY_TABLE_NAME , null ,null ,null , null ,null , null);Dog.d(TAG , "--queryLatestHistoryItem---cursor=" + cursor.getCount());if (null != cursor && cursor.getCount() > 0){cursor.moveToLast();int id = cursor.getInt(cursor.getColumnIndex(DBTable.ID));String name = cursor.getString(cursor.getColumnIndex(DBTable.NAME));String createTime = cursor.getString(cursor.getColumnIndex(DBTable.CREATE_TIME));historyItem = new HistoryItem( name,  createTime);Dog.d(TAG , " ---queryLatestHistoryItem----historyItem=" + historyItem);}}catch ( Exception e ){Dog.d(TAG , "Exception=" + e.toString());}finally {if (null != cursor){cursor.close();}closeDatabase();}return historyItem;}//更新数据public boolean updateHistoryItem(HistoryItem historyItem){boolean result = false;mDB = mDBHelper.getWritableDatabase();if (null != historyItem){int id = historyItem.getId();String name = historyItem.getName();ContentValues cValue = new ContentValues();cValue.put(DBTable.NAME , historyItem.getName());mDB.update(DBTable.HISTORY_TABLE_NAME , cValue , DBTable.ID + "=?" , new String[]{id + ""});result = true;}return result;}//插入数据public boolean insertHistoryItem (HistoryItem historyItem){mDB = mDBHelper.getReadableDatabase();if (null != historyItem){//插入前应该先判断数据库是否存在ContentValues cValue = new ContentValues();cValue.put(DBTable.NAME , historyItem.getName());cValue.put(DBTable.CREATE_TIME , historyItem.getCreateTime());mDB.insert(DBTable.HISTORY_TABLE_NAME , null ,cValue);closeDatabase();return true;}closeDatabase();return false;}//删除快速查询public static boolean deleteHistory (HistoryItem historyItem){mDB = mDBHelper.getReadableDatabase();int result = 0;//删除条件String whereClause = ""+DBTable.ID+"=?";//删除条件参数String[] whereArgs = { historyItem.getId() + ""};//执行删除try {result = mDB.delete(DBTable.HISTORY_TABLE_NAME ,whereClause,whereArgs);Dog.d(TAG , "deleteHistory- ---result=" + result);}catch (Exception e){Dog.d(TAG , "deleteHistory- ---Exception=" + e.toString());}finally {closeDatabase();}if (result > 0){return  true;}return false;}//删除多个快速查询public static boolean deleteQuickQuery( List<HistoryItem> historyItemList){mDB = mDBHelper.getReadableDatabase();try{for (HistoryItem item : historyItemList){//删除条件String whereClause = ""+DBTable.ID+"=?";//删除条件参数String[] whereArgs = { item.getId() + ""};//执行删除mDB.delete(DBTable.HISTORY_TABLE_NAME ,whereClause,whereArgs);}return  true;}catch (Exception e ){Dog.d(TAG , "deleteQuickQuery--list ---Exception=" + e.toString());}finally {closeDatabase();}return false;}//删除所有的数据public static boolean deleteAllHistory (){mDB = mDBHelper.getReadableDatabase();try{//执行删除mDB.delete(DBTable.HISTORY_TABLE_NAME ,null,null);return  true;}catch (Exception e ){Dog.d(TAG , "deleteAllHistory--list ---Exception=" + e.toString());}finally {closeDatabase();}return false;}// 关闭数据库对象public static void closeDatabase() {if (mDB != null && mDB.isOpen()) {mDB.close();}}}

DBTable

package com.tan.searchhistory.db;/*** 数据库表字段** @author richard.tan*/
public class DBTable {public static final String HISTORY_TABLE_NAME = "history";public static final String ID = "id";public static final String NAME = "name";public static final String CREATE_TIME = "createTime";public static final String CREATE_HISTORY = "create table if not exists " + HISTORY_TABLE_NAME + "  ("+ ID + " INTEGER PRIMARY KEY AUTOINCREMENT ,"+ NAME + " text ,"+ CREATE_TIME + " text) ";}

HistoryItem

package com.tan.searchhistory.models;import android.os.Parcel;
import android.os.Parcelable;import java.io.Serializable;/*** Created by pateo on 18-1-2.*/public class HistoryItem implements Serializable, Parcelable {private static final long serialVersionUID = 1L;//idprivate int id;//nameprivate String name;//dateprivate String createTime;public HistoryItem(){}public HistoryItem(String name, String createTime) {this.name = name;this.createTime = createTime;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getCreateTime() {return createTime;}public void setCreateTime(String createTime) {this.createTime = createTime;}@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeInt(id);dest.writeString(name);dest.writeString(createTime);}public static final Parcelable.Creator<HistoryItem> CREATOR = new Creator<HistoryItem>() {@Overridepublic HistoryItem createFromParcel(Parcel source) {HistoryItem deviceCodes = new HistoryItem();deviceCodes.id = source.readInt();deviceCodes.name = source.readString();deviceCodes.createTime = source.readString();return deviceCodes;}@Overridepublic HistoryItem[] newArray(int size) {return new HistoryItem[size];}};@Overridepublic String toString() {return "HistoryItem{" +"id=" + id +", name='" + name + '\'' +", createTime='" + createTime + '\'' +'}';}
}

MainActivity

mHistoryList.clear();
mStrHistoryList.clear();
vHistoryFlowLayout.removeAllViews();
vHistoryTitleLayout.setVisibility(View.GONE);
package com.tan.searchhistory.ui;import android.content.DialogInterface;import android.os.Message;import android.service.carrier.CarrierMessagingService;import android.support.v7.app.AlertDialog;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.text.TextUtils;import android.view.KeyEvent;import android.view.View;import android.view.inputmethod.EditorInfo;import android.widget.EditText;import android.widget.ImageView;import android.widget.RelativeLayout;import android.widget.ScrollView;import android.widget.TextView;import com.tan.searchhistory.R;import com.tan.searchhistory.constants.Config;import com.tan.searchhistory.db.DBManager;import com.tan.searchhistory.models.HistoryItem;import com.tan.searchhistory.utils.Utils;import com.tan.searchhistory.view.XCFlowLayout;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity {    private static final String TAG = MainActivity.class.getSimpleName();    private EditText vSearchEditText;    private ImageView vClearEditImg;    private RelativeLayout vHistoryTitleLayout;    private XCFlowLayout vHistoryFlowLayout;    private ImageView vDeleteHistoryView;    private List<HistoryItem> mHistoryList = new ArrayList<>();    private List<String> mStrHistoryList = new ArrayList<>();    private DBManager mDBManager;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initData();        initView();        initListener();    }    private void initData(){        mDBManager = DBManager.getInstance(this);        //数据库返回的数据是降序排列的最新的五条数据        //但是,XCFlowLayout 会传入的数据,再倒序排列展示        //所以.数据库查询到的数据,再反一次.穿个XCFlowLayout        List<HistoryItem> tempHistoryList  = mDBManager.queryLatestHistoryListByNum(Config.DEFAULT_HISTORY_SEARCH_LIST_NUM);        for (int i = tempHistoryList.size()-1; i >= 0 ;i--){            mHistoryList.add(tempHistoryList.get(i));        }        if (null != mHistoryList && mHistoryList.size() > 0){            for (HistoryItem item : mHistoryList){                mStrHistoryList.add(item.getName());            }        }    }    private void initView(){        vSearchEditText = (EditText) findViewById(R.id.edit_text);        vClearEditImg = (ImageView) findViewById(R.id.iv_clear);        vHistoryTitleLayout = (RelativeLayout) findViewById(R.id.layout_history_search_title);        vHistoryFlowLayout = (XCFlowLayout) findViewById(R.id.search_history_flow_view);        vDeleteHistoryView = (ImageView) findViewById(R.id.search_history_clear_view);        initHistoryData();    }    private void initListener(){        vSearchEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {            @Override            public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {                if (actionId == EditorInfo.IME_ACTION_SEARCH) {                    Utils.hideSoftInput(vSearchEditText);                    if (!TextUtils.isEmpty(textView.getText().toString())){                        saveHistoryData(textView.getText().toString());                    }                }                return true;            }        });        //清空输入        vClearEditImg.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                vSearchEditText.setText("");            }        });        //删除历史搜索        vDeleteHistoryView.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                showDeleteHistoryDialog();            }        });        vHistoryFlowLayout.setOnItemClickListener(new XCFlowLayout.OnItemClickListener() {            @Override            public void onItemClick(View view) {                if (view instanceof TextView) {                    String value = ((TextView) view).getText().toString();                    vSearchEditText.setText(value);                    vSearchEditText.setSelection(value.length());                    Utils.hideSoftInput(vSearchEditText);                }            }        });    }    /**     * 初始化搜索历史数据     */    private void initHistoryData() {        if (mStrHistoryList.size() > 0) {            vHistoryTitleLayout.setVisibility(View.VISIBLE);            vHistoryFlowLayout.setData(mStrHistoryList);        } else {            vHistoryTitleLayout.setVisibility(View.GONE);        }    }    /**     * 存储搜索历史数据     */    private void saveHistoryData(String value) {        if (!mStrHistoryList.contains(value)) {            long time = System.currentTimeMillis();//获取系统时间            String strTime = String.valueOf(time);            HistoryItem historyItem = new HistoryItem(value ,strTime );            if (mStrHistoryList.size() < Config.DEFAULT_HISTORY_SEARCH_LIST_NUM) {                mStrHistoryList.add(value);                mHistoryList.add(historyItem);                mDBManager.insertHistoryItem(historyItem);            } else {                mStrHistoryList.remove(0);                mStrHistoryList.add(value);                mHistoryList.remove(0);                mHistoryList.add(historyItem);                mDBManager.insertHistoryItem(historyItem);            }            //刷新列表            vHistoryFlowLayout.setData(mStrHistoryList);        }        vHistoryTitleLayout.setVisibility(View.VISIBLE);    }    private void showDeleteHistoryDialog(){        new AlertDialog.Builder(MainActivity.this)                .setTitle(R.string.search_delete_history_dialog_title)                .setMessage(R.string.search_delete_history_dialog_tips)                .setPositiveButton(R.string.confirm,                        new DialogInterface.OnClickListener() {                            @Override                            public void onClick(DialogInterface dialog, int which) {                                mDBManager.deleteAllHistory();                                mHistoryList.clear();                                mStrHistoryList.clear();                                vHistoryFlowLayout.removeAllViews();                                vHistoryTitleLayout.setVisibility(View.GONE); }                        }).setNegativeButton(R.string.cancel, null)                .show();    }}

Dog

package com.tan.searchhistory.utils;import android.util.Log;/*** Created by keith on 17-8-16.*/public class Dog {private static final String TAG = "search_history";private static final boolean DEBUG = true;public static void d(String className, String message) {if (DEBUG) {Log.d(TAG, '[' + className + "]:" + message);}}public static void e(String className, String message) {if (DEBUG) {Log.e(TAG, '[' + className + "]:" + message);}}public static void e(String className, String message, Throwable tr) {if (DEBUG) {Log.e(TAG, '[' + className + "]:" + message + "; tr:" + tr);}}public static void i(String className, String message) {if (DEBUG) {Log.i(TAG, '[' + className + "]:" + message);}}}

Utils

package com.tan.searchhistory.utils;import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Environment;
import android.text.TextUtils;
import android.view.View;
import android.view.inputmethod.InputMethodManager;import com.tan.searchhistory.MainApplication;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;/*** Created by pateo on 17-3-15.*/public class Utils {private static final String TAG = Utils.class.getSimpleName();/*** 获取一个统一的路径名,以便统一清除,但项目一定要继承BaseApplication,否则将导致此方法无法正常运行* @return 路径名*/public static String getPath(){String path=null;if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){path=Environment.getExternalStorageDirectory()+"/Android/data/"+ MainApplication.getInstance().getPackageName()+"/cache/";}else{path=MainApplication.getInstance().getCacheDir()+"";}return path;}/*** 隐藏软键盘* @param v*/public static void hideSoftInput(View v){InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);if (imm.isActive()) {imm.hideSoftInputFromWindow(v.getApplicationWindowToken(), 0);}}/*** 显示软键盘* @param v*/public static void showSoftInput(View v){InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);//如果edittext 从gone --visible 。设置获取焦点,仍不弹出键盘。去掉此处判断。强制弹出imm.showSoftInput(v, InputMethodManager.SHOW_FORCED);imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
//      if (!imm.isActive()) {
//          imm.showSoftInput(v, InputMethodManager.SHOW_FORCED);
//          imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
//      }}//去掉String中的所有空格public static String removeAllSpaceInString (String str ){return str = str.replaceAll(" " , "");}//格式化当前 时间为 2017/03/19, 返回Stringpublic static String formatCurDate(){long time=System.currentTimeMillis();//long now = android.os.SystemClock.uptimeMillis();SimpleDateFormat format=new SimpleDateFormat("yyyy/MM/dd");Date d1=new Date(time);String curDate=format.format(d1);return curDate;}/*** 判断网络是否连接**/public static boolean isConnected() {Context context = MainApplication.getInstance();// 这个context是不可能为空的,除非application停止了ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);if (manager == null) {return false;}NetworkInfo networkinfo = manager.getActiveNetworkInfo();return networkinfo != null && networkinfo.isAvailable();}/*** 新闻列表Item 新闻发布时间  , 去掉时间,只显示日期**/public static String  formatDateForNewsListItem (String date){String strDate = "";String [] arr = date.split("\\s+");if (arr.length > 0){strDate = arr[0];}return strDate;}/*** 新闻列表 ,刷新时间 显示日期**/public static String  formatDateForRefreshNewsList (String date){String result = "";String strDate = "";String [] arr = date.split("\\s+");if (arr.length > 0){strDate = arr[0];String [] arrDate = strDate.split("-");for (int i = 0 ; i < arrDate.length ; i++){if (i == arrDate.length -1){result = result + arrDate[i];}else {result = result + arrDate[i] + "/";}}}return result;}/***比较两个日期的大小,日期格式为yyyy-MM-dd*/public static boolean isDateOneBigger(String strDate1 , String strDate2){boolean isBigger = false;if (TextUtils.isEmpty(strDate1) || TextUtils.isEmpty(strDate2)){//如果有一个为空.就判断为需要更新.isBigger = true;return isBigger;}SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date dt1 = null ;Date dt2 = null ;try{dt1 = sdf.parse(strDate1);dt2 = sdf.parse(strDate2);}catch (ParseException e) {e.printStackTrace();Dog.d(TAG , "-isDateOneBigger--ParseException=" + e.toString());}if (dt1.getTime() > dt2.getTime()) {isBigger = true;} else if (dt1.getTime() < dt2.getTime()) {isBigger = false;}return isBigger;}
}

XCFlowLayout

package com.tan.searchhistory.view;import android.content.Context;
import android.graphics.Rect;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;import com.tan.searchhistory.R;import java.util.ArrayList;
import java.util.List;public class XCFlowLayout extends ViewGroup {private OnItemClickListener onItemClickListener;private TextPaint mPaint;private Rect mRect;private float mItemHeight;private float mTextSize;public XCFlowLayout(Context context) {this(context, null);}public XCFlowLayout(Context context, AttributeSet attrs) {this(context, attrs, 0);}public XCFlowLayout(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);mItemHeight = getResources().getDimensionPixelSize(R.dimen.search_history_item_text_height);mTextSize = getResources().getDimensionPixelSize(R.dimen.search_history_item_text_size);mPaint = new TextPaint();mPaint.setTextSize(mTextSize);mRect = new Rect();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//父控件传进来的宽度和高度以及对应的测量模式int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);int modeWidth = MeasureSpec.getMode(widthMeasureSpec);int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);int modeHeight = MeasureSpec.getMode(heightMeasureSpec);//如果当前ViewGroup的宽高为wrap_content的情况int width = 0;//自己测量的 宽度int height = 0;//自己测量的高度//记录每一行的宽度和高度int lineWidth = 0;int lineHeight = 0;//获取子view的个数int childCount = getChildCount();for (int i = 0; i < childCount; i++) {View child = getChildAt(i);//测量子View的宽和高measureChild(child, widthMeasureSpec, heightMeasureSpec);//得到LayoutParamsMarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();//子View占据的宽度int childWidth =child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;//子View占据的高度int childHeight =child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;//换行时候if (lineWidth + childWidth > sizeWidth) {//对比得到最大的宽度width = Math.max(width, lineWidth);//重置lineWidthlineWidth = childWidth;//记录行高height += lineHeight;lineHeight = childHeight;} else {//不换行情况//叠加行宽lineWidth += childWidth;//得到最大行高lineHeight = Math.max(lineHeight, childHeight);}//处理最后一个子View的情况if (i == childCount - 1) {width = Math.max(width, lineWidth);height += lineHeight;}}//wrap_contentsetMeasuredDimension(modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width,modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height);}//存储所有子Viewprivate List<List<View>> mAllChildViews = new ArrayList<List<View>>();//每一行的高度private List<Integer> mLineHeight = new ArrayList<Integer>();@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {mAllChildViews.clear();mLineHeight.clear();//获取当前ViewGroup的宽度int width = getWidth();int lineWidth = 0;int lineHeight = 0;//记录当前行的viewList<View> lineViews = new ArrayList<View>();int childCount = getChildCount();for (int i = 0; i < childCount; i++) {View child = getChildAt(i);MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();int childWidth = child.getMeasuredWidth();int childHeight = child.getMeasuredHeight();//如果需要换行if (childWidth + lineWidth + lp.leftMargin + lp.rightMargin > width) {//记录LineHeightmLineHeight.add(lineHeight);//记录当前行的ViewsmAllChildViews.add(lineViews);//重置行的宽高lineWidth = 0;lineHeight = childHeight + lp.topMargin + lp.bottomMargin;//重置view的集合lineViews = new ArrayList();}lineWidth += childWidth + lp.leftMargin + lp.rightMargin;lineHeight = Math.max(lineHeight,childHeight + lp.topMargin + lp.bottomMargin);lineViews.add(child);}//处理最后一行mLineHeight.add(lineHeight);mAllChildViews.add(lineViews);//设置子View的位置int left = 0;int top = 0;//获取行数int lineCount = mAllChildViews.size();for (int i = 0; i < lineCount; i++) {//当前行的views和高度lineViews = mAllChildViews.get(i);lineHeight = mLineHeight.get(i);for (int j = 0; j < lineViews.size(); j++) {View child = lineViews.get(j);//判断是否显示if (child.getVisibility() == View.GONE) {continue;}MarginLayoutParams lp =(MarginLayoutParams) child.getLayoutParams();int cLeft = left + lp.leftMargin;int cTop = top + lp.topMargin;int cRight = cLeft + child.getMeasuredWidth();int cBottom = cTop + child.getMeasuredHeight();//进行子View进行布局child.layout(cLeft, cTop, cRight, cBottom);left +=child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;}left = 0;top += lineHeight;}}/*** 与当前ViewGroup对应的LayoutParams*/@Overridepublic LayoutParams generateLayoutParams(AttributeSet attrs) {return new MarginLayoutParams(getContext(), attrs);}@Overridepublic void addView(View child, LayoutParams params) {super.addView(child, params);child.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {if (null != onItemClickListener) {onItemClickListener.onItemClick(v);}}});}public void setData(List<String> list) {removeAllViews();for (int i = list.size() - 1; i >= 0; i--) {String cache = list.get(i);TextView view = new TextView(getContext());mPaint.getTextBounds(cache, 0, cache.length(), mRect);MarginLayoutParams lp = new MarginLayoutParams(mRect.width() + 20 * 2, (int) mItemHeight);lp.leftMargin = 10;lp.rightMargin = 10;lp.bottomMargin = 30;//设置圆角点九背景图view.setBackgroundResource(R.drawable.search_history_bg);view.setTextColor(getResources().getColor(R.color.search_gray));view.setGravity(Gravity.CENTER);view.setTextSize(mTextSize);view.setText(cache);view.setTag(cache);addView(view, lp);}}public interface OnItemClickListener {void onItemClick(View view);}public void setOnItemClickListener(OnItemClickListener onItemClickListener) {this.onItemClickListener = onItemClickListener;}
}

MainApplication

package com.tan.searchhistory;import android.app.Application;import com.tan.searchhistory.utils.Dog;/*** Created by pateo on 17-3-13.*/public class MainApplication extends Application{private static final String TAG = MainApplication.class.getSimpleName();protected  static MainApplication mInstance = null;@Overridepublic void onCreate() {super.onCreate();Dog.d(TAG , "--onCreate--");mInstance = this;}public static MainApplication getInstance(){return mInstance;}}

搜索历史记录流式布局展示相关推荐

  1. Flutter文本标签TextTagWidget,搜索记录流式布局显示文本标签

    题记 -- 执剑天涯,从你的点滴积累开始,所及之处,必精益求精,即是折腾每一天. 重要消息 网易云[玩转大前端]配套课程 EDU配套 教程 Flutter开发的点滴积累系列文章 1 添加依赖 flut ...

  2. 自定义 FlowLayout流式布局搜索框 加 GreenDao存取搜索记录,使用RecyclerView展示

    输入框布局的shape <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android ...

  3. Android第三方流式布局FlowLayout简单实用(搜索历史记录)

    效果图: 导入大Model下: maven { url 'https://jitpack.io' } builde.gradle依赖: implementation 'com.github.LRH19 ...

  4. android 搜索历史流布局,FlowLayout流式布局实现搜索清空历史记录

    本文实例为大家分享了FlowLayout实现搜索清空历史记录的具体代码,供大家参考,具体内容如下 效果图:点击搜索框将搜索的历史在流式布局中展示出来,清空历史记录就会将历史清空,每次搜索后都存入sp中 ...

  5. FlowLayout流式布局实现搜索清空历史记录

    效果图:点击搜索框将搜索的历史在流式布局中展示出来,清空历史记录就会将历史清空,每次搜索后都存入sp中,每次进入页面都先判断sp里是否有值并展示 首先需要导入一个module,下载地址:https:/ ...

  6. 仿唯品会/京东/淘宝搜索流式布局的隐藏与展示

    1. 项目需求: 如下,如果没有向下箭头(显示/隐藏剩余搜索词条)的话,采用flexbox-layout+Recycleview+FlexboxLayoutManager 可以实现流式布局. 加了这个 ...

  7. 手写一个淘宝、京东的搜索流式布局FlowLayout

    目录 一些叨叨 继承ViewGrop 实现自定义控件 重写构造器 提供对外接口 测量 摆放 使用方法 完整代码 一些叨叨 市面上所有的app只要有搜索功能,几乎都离不开流式布局,像淘宝.京东.小红书等 ...

  8. 流式布局清空历史搜索

    依赖: 1.添加依赖 ①.在项目的 build.gradle 文件中添加 allprojects {repositories {...maven { url 'https://jitpack.io' ...

  9. android搜索热词(热门标签)流式布局的实现

    先看下效果图 1.流式布局实现 继承ViewGroup,重写onMeasure,onLayout方法.代码如下: package com.example.lin.flowlayoutdemo;impo ...

最新文章

  1. 2020多校第1场A题【后缀数组+思维】
  2. Zabbi监控系统搭建
  3. hdu 3966( 树链剖分+点权更新)
  4. C# PrintDocument 打印表格
  5. 下载离线插件包 谷歌浏览器的方法
  6. 如何编写 Nagios 插件 (http://zener.blog.51cto.com/937811/727685)
  7. .NET 6 Preview 6 正式发布: 关注网络开发
  8. 实现非父子之间通信,兄弟组件之间的数据传递--eventBus
  9. SharePoint2007安装图文详解三:安装SqlServer2005
  10. Hadoop - 任务调度系统比较
  11. oracle 删除数据_2020最新最全数据库系统安全
  12. 你知道Message.obtain()什么原理吗?
  13. 水下导弹发射环境因素建模需求及其扫盲笔记
  14. 不要盲目跟风,看微信营销适合哪些行业
  15. 2017年10月23日提高组T2 灵知的太阳信仰 单调队列优化dp
  16. 单双号限行微信小程序源码
  17. Windows连接远程桌面时出现黑屏的解决办法
  18. dma-buf 由浅入深(三) —— map attachment
  19. java 入门专题 字符缓冲输入流HashMap 集合的简单综合应用:文本排序
  20. Proxifier 代理方式上网

热门文章

  1. IntelliJ IDEA 设置对象图片及改变透明度
  2. VC实现卡拉OK字幕叠加
  3. 解决win10任务栏软件图标变成白色的问题
  4. lpv4的地址格式由多少个字节组成_2020年智慧树艺术品拍卖第八单元章节测试答案...
  5. nginx、lvs、keepalived、f5、DNS轮询(lvs为何不能完全替代DNS轮询)
  6. 再一次100%通过华为中级认证考试,5G网优工程师高薪稳了!
  7. 中国移动云能力中心校招面试总结(一面)
  8. Nginx 教程-动静分离
  9. 爱情发生器:36个问题+4分钟对视=告别单身
  10. 1997-2022年市场化指数/市场化指数分享/含计算原始代码