前言
搜索界面一直是一个APP至关重要的部分,也是用户用的最多的界面,那么历史搜索和热门标签的话,也是这个界面所需要的重要的组成部分。
本篇文章旨在帮助大家如何写好两个重要的部分。
用到的控件和框架
1.Android ORM框架 GreenDao3.0
2.SearchView在ToolBar中的使用
3.FlowLayoutTag 标签控件(也是本人写的控件,具体在引用如下)
compile 'com.daidingkang:FlowLayoutTag:1.0.0'
FlowLayoutTag主要代码
FlowLayout.class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | /** * 自定义流式布局 */ public class FlowLayout extends ViewGroup { private LayoutInflater mInflater; private boolean isColorful; public FlowLayout(Context context) { this(context, null); } public FlowLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FlowLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mInflater = LayoutInflater.from(getContext()); } @Override protected 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); // wrapContent int width = 0; int height = 0; // 记录每一行的宽和高 int lineWidth = 0; int lineHeight = 0; // 得到内部元素的个数 int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); // 测量子View的宽和高 measureChild(child, widthMeasureSpec, heightMeasureSpec); // 得到LayoutParams MarginLayoutParams lp = (MarginLayoutParams) child .getLayoutParams(); // 子view的占据的宽度 int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; // 子view占据的高度 int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; // 换行 if (lineWidth + childWidth > sizeWidth - getPaddingLeft() - getPaddingRight()) { // 对比得到最大的宽度 width = Math.max(width, lineWidth); // 重置lineWidth lineWidth = childWidth; // 记录行高 height += lineHeight; lineHeight = childHeight; } else { // 未换行 // 叠加行宽 lineWidth += childWidth; // 得到当前最大高度 lineHeight = Math.max(lineHeight, childHeight); } // 最后一个控件 if (i == count - 1) { width = Math.max(lineWidth, width); height += lineHeight; } } // Log.i("test", "sizeWidth" + sizeWidth); // Log.i("test", "sizeHeight" + sizeHeight); setMeasuredDimension(modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width + getPaddingLeft() + getPaddingRight(), modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height + getPaddingTop() + getPaddingBottom()); setPadding(dp2px(20), dp2px(10), dp2px(20), dp2px(10)); } // 储存所有的View private ArrayList<ArrayList<View>> mAllViews = new ArrayList<>(); // 储存每一行的高度 private ArrayList<Integer> mLineHeight = new ArrayList<>(); @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // TODO Auto-generated method stub // 清除一下list集合 mAllViews.clear(); mLineHeight.clear(); // 得到viewGroup当前宽度 int width = getWidth(); int lineWidth = 0; int lineHeight = 0; ArrayList<View> lineViews = new ArrayList<>(); int count = getChildCount(); for (int i = 0; i < count; 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 - getPaddingLeft() - getPaddingRight()) { // 记录当前行高 mLineHeight.add(lineHeight); // 记录当前行的view mAllViews.add(lineViews); // 重置行宽和行高 lineWidth = 0; lineHeight = childHeight + lp.topMargin + lp.bottomMargin; // 重置lineViews集合 lineViews = new ArrayList<>(); } lineWidth += childWidth + lp.leftMargin + lp.rightMargin; lineHeight = Math.max(lineHeight, childHeight + lp.topMargin + lp.bottomMargin); lineViews.add(child); } // 处理最后一行 mLineHeight.add(lineHeight); mAllViews.add(lineViews); // 设置子view的位置 int left = getPaddingLeft(); int top = getPaddingTop(); // 有多少行 int lineNum = mLineHeight.size(); for (int i = 0; i < lineNum; i++) { // 获取当前行的view lineViews = mAllViews.get(i); // 当前行高 lineHeight = mLineHeight.get(i); int lineViewSize = lineViews.size(); for (int j = 0; j < lineViewSize; j++) { View child = lineViews.get(j); // 判断子view的状态 if (child.getVisibility() == View.GONE) { continue; } MarginLayoutParams lp = (MarginLayoutParams) child .getLayoutParams(); int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); int lc = left + lp.leftMargin; int tc = top + lp.topMargin; int rc = lc + childWidth; int bc = tc + childHeight; // 为子view布局 child.layout(lc, tc, rc, bc); // 同一行view坐起点坐标的变换 left += childWidth + lp.leftMargin + lp.rightMargin; } // 换行时将left重置 left = getPaddingLeft(); // top要加上上一行的行高 top += lineHeight; } } /** * 默认返回的LayoutParams */ @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { // TODO Auto-generated method stub return new MarginLayoutParams(getContext(), attrs); } /** * 设置数据 */ public void setData(String[] strings) { int count = strings.length; for (int i = 0; i < count; i++) { final TextView tv = (TextView) mInflater.inflate(R.layout.flowlayout_textview, this, false); tv.setText(strings[i]); tv.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (onTagClickListener != null) onTagClickListener.TagClick(tv.getText().toString()); } }); if(isColorful){ Random random = new Random(); int ranColor = 0xff000000 | random.nextInt(0x00ffffff); tv.setBackgroundColor(ranColor); } this.addView(tv); } } /** * 设置数据 */ public void setListData(List<String> list) { int count = list.size(); for (int i = 0; i < count; i++) { final TextView tv = (TextView) mInflater.inflate(R.layout.flowlayout_textview, this, false); tv.setText(list.get(i)); tv.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (onTagClickListener != null) onTagClickListener.TagClick(tv.getText().toString()); } }); this.addView(tv); } } /** * 添加标签 * * @param text */ public void addTag(String text) { final TextView tv = (TextView) mInflater.inflate(R.layout.flowlayout_textview, this, false); tv.setText(text); tv.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (onTagClickListener != null) onTagClickListener.TagClick(tv.getText().toString()); } }); if(isColorful){ Random random = new Random(); int ranColor = 0xff000000 | random.nextInt(0x00ffffff); tv.setBackgroundColor(ranColor); } this.addView(tv); } /** * 设置多彩颜色 * @param isColorful */ public void setColorful(boolean isColorful) { this.isColorful = isColorful; } /** * 删除所有标签 */ public void cleanTag() { this.removeAllViews(); } public int dp2px(int dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics()); } private OnTagClickListener onTagClickListener; public void setOnTagClickListener(OnTagClickListener onTagClickListener) { this.onTagClickListener = onTagClickListener; } public interface OnTagClickListener { void TagClick(String text); } } |
SearchActivity主要代码
SearchActivity.class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | public class SearchActivity extends BaseActivity implements View.OnClickListener { @BindView(R.id.searchView) SearchView searchView; @BindView(R.id.toolbar) Toolbar toolbar; @BindView(R.id.hot_flowLayout) FlowLayout hotFlowLayout; @BindView(R.id.his_flowLayout) FlowLayout hisFlowLayout; @BindView(R.id.ll_history) LinearLayout lHistory; @BindView(R.id.delete) ImageView delete; SearchHistoryDao historyDao; @Override public void initView() { setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); initHotTag(); historyDao = GreenDaoHelper.getDaoSession().getSearchHistoryDao(); //设置我们的SearchView initSearchView(); delete.setOnClickListener(this); } private void initSearchView() { searchView.setIconifiedByDefault(true);//设置展开后图标的样式,这里只有两种,一种图标在搜索框外,一种在搜索框内 searchView.onActionViewExpanded();// 写上此句后searchView初始是可以点击输入的状态,如果不写,那么就需要点击下放大镜,才能出现输入框,也就是设置为ToolBar的ActionView,默认展开 // searchView.requestFocus();//输入焦点 searchView.setSubmitButtonEnabled(true);//添加提交按钮,监听在OnQueryTextListener的onQueryTextSubmit响应 // searchView.setFocusable(true);//将控件设置成可获取焦点状态,默认是无法获取焦点的,只有设置成true,才能获取控件的点击事件 searchView.setIconified(false);//输入框内icon不显示 // searchView.requestFocusFromTouch();//模拟焦点点击事件 searchView.setFocusable(false); searchView.clearFocus(); // mSearchView.setIconifiedByDefault(true); searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { SnackbarUtil.show(toolbar, query); //插入之前先查询,如果有相同的就不在插入进去 SearchHistory unique = historyDao.queryBuilder().where(SearchHistoryDao.Properties.SearchContent.eq(query)).unique(); if (unique == null) { historyDao.insert(new SearchHistory(null, query)); } return false; } @Override public boolean onQueryTextChange(String newText) { return false; } }); } /** * 历史搜索 */ private void initHistoryTag() { List<SearchHistory> searchHistories = historyDao.loadAll(); if (historyDao != null && searchHistories != null && searchHistories.size() != 0) { List<String> historyList = new ArrayList<>(); for (SearchHistory searchHistory : searchHistories) { historyList.add(searchHistory.getSearchContent()); } lHistory.setVisibility(View.VISIBLE); hisFlowLayout.setListData(historyList); hisFlowLayout.setOnTagClickListener(new FlowLayout.OnTagClickListener() { @Override public void TagClick(String text) { MyApplication.toastor.showToast(text); } }); } } /** * 热门搜索 */ private void initHotTag() { String[] mStrings = {"apple", "百度CEO", "阿里巴巴", "绩效股", "中国股市", "美团", "google", "淘宝", "雷军 小米公司", "大疆无人机"}; hotFlowLayout.setColorful(true); hotFlowLayout.setData(mStrings); hotFlowLayout.setOnTagClickListener(new FlowLayout.OnTagClickListener() { @Override public void TagClick(String text) { MyApplication.toastor.showToast(text); } }); } @Override protected int getContentViewLayoutID() { return R.layout.activity_search; } @Override protected void onResume() { super.onResume(); initHistoryTag(); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.delete: AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage("确定要删除全部历史记录?"); builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { lHistory.setVisibility(View.GONE); hisFlowLayout.cleanTag(); historyDao.deleteAll(); } }); builder.setNegativeButton("取消", null); builder.create().show(); break; } } } |
主要布局文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:visibility="gone" android:id="@+id/ll_history" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:paddingLeft="10dp" android:paddingRight="10dp" > <TextView android:id="@+id/his_vertical_bar" android:layout_width="3dp" android:layout_height="20dp" android:background="?attr/colorPrimary" android:layout_marginRight="4dp" /> <TextView android:layout_toRightOf="@+id/his_vertical_bar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:gravity="left" android:text="历史搜索" android:textColor="@color/font" android:textSize="15sp" /> <ImageView android:id="@+id/delete" android:layout_width="wrap_content" android:layout_height="15dp" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:src="@mipmap/ic_delete" /> </RelativeLayout> <com.pulamsi.photomanager.widght.fitsystemwindowlayout.FlowLayout android:id="@+id/his_flowLayout" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:paddingLeft="10dp" android:paddingRight="10dp" > <TextView android:id="@+id/hot_vertical_bar" android:layout_width="3dp" android:layout_height="20dp" android:background="?attr/colorPrimary" android:layout_marginRight="4dp" /> <TextView android:layout_toRightOf="@+id/hot_vertical_bar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:gravity="left" android:text="热门搜索" android:textColor="@color/font" android:textSize="15sp" /> </RelativeLayout> <com.pulamsi.photomanager.widght.fitsystemwindowlayout.FlowLayout android:id="@+id/hot_flowLayout" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout> </LinearLayout> |
如果有疑问和见解,也欢迎大家在下面留言,我会一一回复大家