Android搜索框(保存历史记录和自动匹配)带有ScrollView嵌套ListView阻尼效果
2015-08-08 16:37
411 查看
在很多项目中都用到了搜索功能,百度搜索的资源并不是很多,而且也都不是我想要的,就随便下了个改了改,看起来还有点顺眼,就与大家分享分享了。先上图吧。。。
那么现在来看看项目结构,
搜索界面的逻辑:
顶部是一个输入框加一个清空按钮后面是搜索按钮,当输入框没内容时,后面的清空按钮隐藏,搜索按钮显示“取消”,点击取消的时候退出当前Activity。当输入框有内容时,显示清空输入框内容的按钮,后面的搜索按钮显示“搜索”,点击搜索就可以去调用你们的接口了。(由于我只是做的一个demo,主要实现的是本地操作的逻辑)当搜索成功后,再来保存搜索记录到本地。输入框输内容时,会自动匹配本地的记录。
下面开始贴代码咯。
搜索界面的布局代码:activity_search.xml
搜索输入框的背景:et_search_bg.xml
清空全部历史记录的背景:btn_search_bg.xml
历史记录子项布局代码:item_search_history.xml
下面是java代码:
搜索界面的代码:SearchShopActivity.java
历史记录适配器的代码:SearchHistoryAdapter.java
搜索数据对象:SearchData.java
自定义ListView:CustomerListView.java
自定义ScrollView:CustomerScrollView.java
那么整个的就结束了,还有几个图片资源,就不贴了,源码下载:【源码】。
那么现在来看看项目结构,
搜索界面的逻辑:
顶部是一个输入框加一个清空按钮后面是搜索按钮,当输入框没内容时,后面的清空按钮隐藏,搜索按钮显示“取消”,点击取消的时候退出当前Activity。当输入框有内容时,显示清空输入框内容的按钮,后面的搜索按钮显示“搜索”,点击搜索就可以去调用你们的接口了。(由于我只是做的一个demo,主要实现的是本地操作的逻辑)当搜索成功后,再来保存搜索记录到本地。输入框输内容时,会自动匹配本地的记录。
下面开始贴代码咯。
搜索界面的布局代码:activity_search.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#FFFFFF" > <LinearLayout android:id="@+id/ly_search" android:layout_width="fill_parent" android:layout_height="50dp" android:gravity="center_vertical" android:orientation="horizontal" android:padding="8dp" > <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@drawable/et_search_bg" android:gravity="center_vertical" android:orientation="horizontal" > <EditText android:id="@+id/et_search" android:layout_width="0dp" android:layout_height="match_parent" android:layout_marginLeft="10dp" android:layout_weight="1" android:background="@android:color/transparent" android:drawableLeft="@drawable/icon_search" android:drawablePadding="10dp" android:hint="请输入商户名、地点" android:singleLine="true" android:textColor="#696969" android:textColorHint="#bbbaba" android:textSize="14sp" > <requestFocus /> </EditText> <ImageView android:id="@+id/iv_delete_search" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="10dp" android:contentDescription="@drawable/icon_delete" android:src="@drawable/icon_delete" android:visibility="invisible" /> </LinearLayout> <TextView android:id="@+id/tv_search" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_marginLeft="5dp" android:layout_marginRight="5dp" android:gravity="center" android:text="取消" android:textColor="#FF605C" android:textSize="16sp" /> </LinearLayout> <View android:id="@+id/v_line" android:layout_width="match_parent" android:layout_height="1dp" android:layout_below="@+id/ly_search" android:background="#bbbaba" /> <LinearLayout android:id="@+id/ly_history" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/v_line" android:gravity="center_vertical" android:orientation="horizontal" android:paddingBottom="8dp" android:paddingLeft="8dp" android:paddingTop="8dp" > <TextView android:id="@+id/tv_history" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="10dp" android:drawableLeft="@drawable/icon_timer" android:drawablePadding="5dp" android:text="历史记录" android:textColor="#bbbaba" android:textSize="12sp" /> <View android:layout_width="0dp" android:layout_height="1dp" android:layout_weight="1" android:background="#bbbaba" /> </LinearLayout> <com.xiaoxi.search.widget.CustomerScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/ly_history" android:layout_marginBottom="10dp" android:fadingEdge="none" android:overScrollMode="never" android:scrollbars="none" > <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal" android:orientation="vertical" > <com.xiaoxi.search.widget.CustomerListView android:id="@+id/lv_history" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:cacheColorHint="#00000000" android:divider="#bbbaba" android:dividerHeight="1dp" android:fadingEdge="none" android:listSelector="#00000000" android:overScrollMode="never" android:paddingLeft="24dp" /> <Button android:id="@+id/btn_clear_history" android:layout_width="match_parent" android:layout_height="40dp" android:layout_marginLeft="30dp" android:layout_marginRight="30dp" android:background="@drawable/btn_search_bg" android:gravity="center" android:singleLine="true" android:text="清除搜索记录" android:textColor="#a4a4a4" android:textSize="15sp" /> </LinearLayout> </com.xiaoxi.search.widget.CustomerScrollView> </RelativeLayout>
搜索输入框的背景:et_search_bg.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <!-- 背景颜色 --> <solid android:color="#F4F4F4" /> <!-- 边框颜色 --> <!-- <stroke android:width="1dp" android:color="@android:color/darker_gray" /> --> <!-- 圆角弧度 --> <!-- <corners android:radiu="10dp" /> --> <corners android:bottomLeftRadius="16dp" android:bottomRightRadius="16dp" android:topLeftRadius="16dp" android:topRightRadius="16dp" /> </shape>
清空全部历史记录的背景:btn_search_bg.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <!-- 背景颜色 --> <solid android:color="#E5E5E5" /> <!-- 边框颜色 --> <!-- <stroke android:width="1dp" android:color="@android:color/darker_gray" /> --> <!-- 圆角弧度 --> <!-- <corners android:radiu="6dp" /> --> <corners android:bottomLeftRadius="6dp" android:bottomRightRadius="6dp" android:topLeftRadius="6dp" android:topRightRadius="6dp" /> </shape>
历史记录子项布局代码:item_search_history.xml
<?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="40dp" > <TextView android:id="@+id/tv_content" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:layout_centerVertical="true" android:gravity="center" android:singleLine="true" android:textColor="#696969" android:textSize="15sp" /> </RelativeLayout>
下面是java代码:
搜索界面的代码:SearchShopActivity.java
package com.xiaoxi.search.activity; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import android.app.Activity; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.os.Bundle; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.xiaoxi.search.R; import com.xiaoxi.search.adapter.SearchHistoryAdapter; import com.xiaoxi.search.bean.SearchData; import com.xiaoxi.search.widget.CustomerListView; public class SearchShopActivity extends Activity implements OnClickListener { private EditText etSearch; private ImageView ivDeleteSearch; private TextView tvSearch; private TextView tvHistory; private CustomerListView lvHistory; private Button btnClearHistory; private static final String SEARCH_HISTORY = "search_history"; private SharedPreferences sp; private SearchHistoryAdapter mAdapter; private List<SearchData> lstHistory; private List<SearchData> lstAllHistory; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_search); initViews(); initData(); setListener(); } private void initViews() { tvSearch = (TextView) findViewById(R.id.tv_search); ivDeleteSearch = (ImageView) findViewById(R.id.iv_delete_search); etSearch = (EditText) findViewById(R.id.et_search); tvHistory = (TextView) findViewById(R.id.tv_history); lvHistory = (CustomerListView) findViewById(R.id.lv_history); btnClearHistory = (Button) findViewById(R.id.btn_clear_history); } private void initData() { sp = getSharedPreferences(SEARCH_HISTORY, 0); lstAllHistory = new ArrayList<SearchData>(); lstHistory = new ArrayList<SearchData>(); readSearchHistory(); lstHistory.addAll(lstAllHistory); mAdapter = new SearchHistoryAdapter(this, lstHistory); lvHistory.setAdapter(mAdapter); Log.i("TEST", "长度---" + lstHistory.size()); if (lstHistory.size() < 1) { lvHistory.setVisibility(View.GONE); btnClearHistory.setVisibility(View.GONE); } } private void setListener() { ivDeleteSearch.setOnClickListener(this); tvSearch.setOnClickListener(this); btnClearHistory.setOnClickListener(this); lvHistory.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View view, int position, long arg3) { SearchData data = (SearchData) mAdapter.getItem(position); etSearch.setText(data.getContent()); tvSearch.performClick(); } }); etSearch.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { performFiltering(s); if (s.length() > 0) { ivDeleteSearch.setVisibility(View.VISIBLE); tvSearch.setText("搜索"); tvHistory.setText("猜你想搜"); } else { ivDeleteSearch.setVisibility(View.INVISIBLE); tvSearch.setText("取消"); tvHistory.setText("历史记录"); } } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { } }); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.iv_delete_search:// 清空搜索内容 etSearch.setText(""); ivDeleteSearch.setVisibility(View.INVISIBLE); break; case R.id.tv_search:// 搜索按钮 String searchContent = etSearch.getText().toString().trim(); if (TextUtils.isEmpty(searchContent)) { SearchShopActivity.this.finish(); } else { saveSearchHistory(searchContent); readSearchHistory(); if (lstHistory.size() > 0) { lstHistory.clear(); } lstHistory.addAll(lstAllHistory); mAdapter.notifyDataSetChanged(); lvHistory.setVisibility(View.VISIBLE); btnClearHistory.setVisibility(View.VISIBLE); Toast.makeText(SearchShopActivity.this, searchContent, Toast.LENGTH_SHORT).show(); } break; case R.id.btn_clear_history:// 清除历史记录 clearAllSearchHistory(); readSearchHistory(); if (lstHistory.size() > 0) { lstHistory.clear(); } lstHistory.addAll(lstAllHistory); mAdapter.notifyDataSetChanged(); lvHistory.setVisibility(View.GONE); btnClearHistory.setVisibility(View.GONE); Toast.makeText(SearchShopActivity.this, "清空历史记录成功", Toast.LENGTH_SHORT).show(); break; default: break; } } /** * 匹配过滤搜索内容 * * @param prefix * 输入框中输入的内容 */ public void performFiltering(CharSequence inputContent) { if (TextUtils.isEmpty(inputContent)) {// 搜索框内容为空的时候显示所有历史记录 if (lstHistory.size() > 0) { lstHistory.clear(); } lstHistory.addAll(lstAllHistory); } else { String inputContentString = inputContent.toString().toLowerCase(); int count = lstAllHistory.size(); List<SearchData> lstFilterHistory = new ArrayList<SearchData>(count); for (int i = 0; i < count; i++) { String value = lstAllHistory.get(i).getContent(); String valueText = value.toLowerCase(); if (valueText.contains(inputContentString)) {// 包含输入的内容 // 这个和下面的判断根据设计来做选择 } if (valueText.startsWith(inputContentString)) {// 以输入的内容开头 lstFilterHistory.add(new SearchData().setContent(valueText)); } else {// 判断有空格的搜索内容中某一段是否包含输入内容开头的 String[] words = valueText.split(" "); int wordCount = words.length; for (int k = 0; k < wordCount; k++) { if (words[k].startsWith(inputContentString)) { lstFilterHistory.add(new SearchData().setContent(value)); break; } } } // if (mMaxMatch > 0) {// 过滤后的历史记录只显示mMaxMatch条 // if (newValues.size() > mMaxMatch - 1) { // break; // } // } } if (lstHistory.size() > 0) { lstHistory.clear(); } lstHistory.addAll(lstFilterHistory); } mAdapter.notifyDataSetChanged(); if (lstHistory.size() < 1) { lvHistory.setVisibility(View.GONE); btnClearHistory.setVisibility(View.GONE); }else{ lvHistory.setVisibility(View.VISIBLE); btnClearHistory.setVisibility(View.VISIBLE); } } /* * 保存搜索记录 */ private void saveSearchHistory(String searchContent) { String longhistory = sp.getString(SEARCH_HISTORY, ""); String[] tmpHistory = longhistory.split(","); List<String> lstHistory = new ArrayList<String>(Arrays.asList(tmpHistory)); if (lstHistory.size() > 0) {// 移除历史,添加新数据 for (int i = 0; i < lstHistory.size(); i++) { if (searchContent.equals(lstHistory.get(i))) { lstHistory.remove(i); break; } } lstHistory.add(0, searchContent); } if (lstHistory.size() > 0) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < lstHistory.size(); i++) { sb.append(lstHistory.get(i) + ","); } sp.edit().putString(SEARCH_HISTORY, sb.toString()).commit(); } else {// 添加新数据 sp.edit().putString(SEARCH_HISTORY, searchContent + ",").commit(); } } /** * 读取历史搜索记录 */ public void readSearchHistory() { if (lstAllHistory.size() > 0) { lstAllHistory.clear(); } String longhistory = sp.getString(SEARCH_HISTORY, ""); if (TextUtils.isEmpty(longhistory)) { return; } String[] hisArrays = longhistory.split(","); for (int i = 0; i < hisArrays.length; i++) { lstAllHistory.add(new SearchData().setContent(hisArrays[i])); } } /** * 清空全部历史搜索记录 */ public void clearAllSearchHistory() { Editor editor = sp.edit(); editor.clear(); editor.commit(); } }
历史记录适配器的代码:SearchHistoryAdapter.java
package com.xiaoxi.search.adapter; import java.util.List; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; import com.xiaoxi.search.R; import com.xiaoxi.search.bean.SearchData; public class SearchHistoryAdapter extends BaseAdapter { private Context mContext; private List<SearchData> lstHistory;// 所有历史记录 public SearchHistoryAdapter(Context context, List<SearchData> lstHistory) { this.mContext = context; this.lstHistory = lstHistory; } @Override public int getCount() { return lstHistory == null ? 0 : lstHistory.size(); } @Override public Object getItem(int position) { return lstHistory == null ? 0 : lstHistory.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { holder = new ViewHolder(); convertView = LayoutInflater.from(mContext).inflate(R.layout.item_search_history, parent, false); holder.tvContent = (TextView) convertView.findViewById(R.id.tv_content); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } SearchData data = lstHistory.get(position); holder.tvContent.setText(data.getContent()); return convertView; } private class ViewHolder { TextView tvContent; } }
搜索数据对象:SearchData.java
package com.xiaoxi.search.bean; public class SearchData { private String id; private String content; public String getId() { return id; } public SearchData setId(String id) { this.id = id; return this; } public String getContent() { return content; } public SearchData setContent(String content) { this.content = content; return this; } }
自定义ListView:CustomerListView.java
package com.xiaoxi.search.widget; import android.content.Context; import android.util.AttributeSet; import android.widget.ListView; /** * 自定义ListView * @author 周辉 * @version 1.4 * @created 2014-9-1 */ public class CustomerListView extends ListView { public CustomerListView(Context context) { super(context); } public CustomerListView(Context context, AttributeSet attrs) { super(context, attrs); } public CustomerListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } }
自定义ScrollView:CustomerScrollView.java
package com.xiaoxi.search.widget; import android.content.Context; import android.os.Handler; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.ScrollView; /** * 增加ScrollView控件的弹簧效果 * */ public class CustomerScrollView extends ScrollView { Context mContext; private View mView; private float touchY; private int scrollY = 0; private boolean handleStop = false; private int eachStep = 0; private static final int MAX_SCROLL_HEIGHT = 300;// 最大滑动距离 private static final float SCROLL_RATIO = 0.4f;// 阻尼系数,越小阻力就越大 public CustomerScrollView(Context context) { super(context); this.mContext = context; } public CustomerScrollView(Context context, AttributeSet attrs) { super(context, attrs); this.mContext = context; } public CustomerScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); this.mContext = context; } @Override protected void onFinishInflate() { if (getChildCount() > 0) { this.mView = getChildAt(0); } } @Override public boolean onInterceptTouchEvent(MotionEvent arg0) { if (arg0.getAction() == MotionEvent.ACTION_DOWN) { touchY = arg0.getY(); } return super.onInterceptTouchEvent(arg0); } @Override public boolean onTouchEvent(MotionEvent ev) { if (mView == null) { return super.onTouchEvent(ev); } else { commonOnTouchEvent(ev); } return super.onTouchEvent(ev); } private void commonOnTouchEvent(MotionEvent ev) { int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_UP: if (mView.getScrollY() != 0) { handleStop = true; animation(); } break; case MotionEvent.ACTION_MOVE: float nowY = ev.getY(); int deltaY = (int) (touchY - nowY); touchY = nowY; if (isNeedMove()) { int offset = mView.getScrollY(); if (offset < MAX_SCROLL_HEIGHT && offset > -MAX_SCROLL_HEIGHT) { mView.scrollBy(0, (int) (deltaY * SCROLL_RATIO)); handleStop = false; } } break; default: break; } } private boolean isNeedMove() { int viewHight = mView.getMeasuredHeight(); int srollHight = getHeight(); int offset = viewHight - srollHight; int scrollY = getScrollY(); if (scrollY == 0 || scrollY == offset) { return true; } return false; } private void animation() { scrollY = mView.getScrollY(); eachStep = scrollY / 10; resetPositionHandler.sendEmptyMessage(0); } Handler resetPositionHandler = new Handler() { public void handleMessage(android.os.Message msg) { if (scrollY != 0 && handleStop) { scrollY -= eachStep; if ((eachStep < 0 && scrollY > 0) || (eachStep > 0 && scrollY < 0)) { scrollY = 0; } mView.scrollTo(0, scrollY); this.sendEmptyMessageDelayed(0, 5); } }; }; }
那么整个的就结束了,还有几个图片资源,就不贴了,源码下载:【源码】。
相关文章推荐
- IntentService与Service用法区别
- android kl文件
- Android性能优化典范 - 第2季
- Android ANR产生原因和解决办法
- android中的任务栈和启动模式的总结
- Android之GridView横向左右滚动
- Android EventBus现实 听说你out该
- Android性能优化典范
- android服务的总结
- android DDMS 导出files文件夹下的容时报错
- 一个Android Socket的例子
- GitHub 优秀的 Android 开源项目
- Android之线程池深度剖析
- Android消息推送
- Android学习笔记之下拉更新控件
- TortoiseSVN新建和合并分支图文教程
- Android控件详解之惰性装载控件
- Android学习笔记之SlidingMenu
- android之类似卫星菜单,来自定义ViewGroup。。。。。
- 心得分享:关于对Android的理解,知识点梳理