├── .gitignore ├── .idea ├── caches │ └── build_file_checksums.ser ├── codeStyles │ └── Project.xml ├── gradle.xml ├── misc.xml └── runConfigurations.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── tangguna │ │ └── searchbox │ │ └── searchbox │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── tangguna │ │ │ └── searchbox │ │ │ └── searchbox │ │ │ ├── MainActivity.java │ │ │ └── SearchActivity.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ └── search.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── tangguna │ └── searchbox │ └── searchbox │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── img ├── Screenshot_1536113887.png ├── Screenshot_1536113915.png ├── Screenshot_1536113922.png ├── Screenshot_1536113962.png └── Screenshot_1536126402.png ├── library ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── tangguna │ │ └── searchbox │ │ └── library │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── tangguna │ │ │ └── searchbox │ │ │ └── library │ │ │ ├── adapter │ │ │ ├── HistoryDataAdapter.java │ │ │ └── HistoryDataListViewAdapter.java │ │ │ ├── cache │ │ │ └── HistoryCache.java │ │ │ ├── callback │ │ │ └── onSearchCallBackListener.java │ │ │ └── widget │ │ │ ├── FlowLayout.java │ │ │ ├── SearchLayout.java │ │ │ ├── SearchListLayout.java │ │ │ ├── SelfSearchGridView.java │ │ │ └── SelfSearchListView.java │ └── res │ │ ├── drawable-xhdpi │ │ ├── explore_search_close.png │ │ ├── explore_search_close_nt.png │ │ ├── explore_search_icon.png │ │ ├── explore_search_icon_nt.png │ │ ├── icon_clear_data.png │ │ └── icon_close.png │ │ ├── drawable │ │ ├── bg_shadow_set_bottom.xml │ │ ├── ll_clear_bg.xml │ │ ├── search.xml │ │ ├── search_baground_shap.xml │ │ ├── search_title_baground_shap.xml │ │ ├── search_title_baground_shap2.xml │ │ ├── sousuo_back_or_search_button_shap.xml │ │ ├── sousuo_bg_gray_1.xml │ │ ├── sousuo_bg_gray_2.xml │ │ ├── sousuo_bg_gray_3.xml │ │ ├── sousuo_bg_gray_4.xml │ │ ├── sousuo_bg_gray_5.xml │ │ ├── sousuo_bg_gray_state_pressed_shap.xml │ │ ├── sousuo_bg_level.xml │ │ └── sousuo_clearolddata_shap.xml │ │ ├── layout │ │ ├── list_item.xml │ │ ├── msearch_top.xml │ │ ├── msearchlayout.xml │ │ ├── search_olddata_item.xml │ │ ├── searchlistlayout.xml │ │ └── suosou_item.xml │ │ └── values │ │ ├── color.xml │ │ ├── strings.xml │ │ └── style.xml │ └── test │ └── java │ └── com │ └── tangguna │ └── searchbox │ └── library │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SearchBox 2 | android 自定义选择框 3 | ##### gridView布局 4 | ![图片加载失败](https://github.com/tangguna/SearchBox/blob/master/img/Screenshot_1536113915.png) 5 | ##### ListView布局 6 | ![图片加载失败](https://github.com/tangguna/SearchBox/blob/master/img/Screenshot_1536126402.png) 7 | ### 功能 8 | 1.包含历史搜索,热门搜索; 9 | 2.可以从服务器或者本地加载数据; 10 | 3.历史记录缓存,动态添加记录 11 | 4 listView,gridView两种显示方式 12 | ### 配置 13 | #### 在根build.gradle中添加 14 | allprojects { 15 | repositories { 16 | ... 17 | maven { url 'https://jitpack.io' } 18 | } 19 | } 20 | #### 添加依赖 21 | dependencies { 22 | implementation 'com.github.tangguna:SearchBox:1.0.1' 23 | } 24 | 25 | ###使用方式 26 | #### 实现接口onSearchCallBackListener 27 | public abstract void Search(String str):搜索关键字 28 | public abstract void Back():返回 29 | public abstract void ClearOldData():清空历史记录 30 | public abstract void SaveOldData(ArrayList AlloldDataList):保存搜索记录 31 | #### 调用 GridView 32 | 在XML中添加布局 33 | 39 | 40 | #### 实现initData(@Nullable List olddatalist,@Nullable List hotdata, onSearchCallBackListener sCb,int styleId) 41 | /** 42 | * 43 | * @param olddatalist 历史搜索数据集合 44 | * @param hotdata 热门搜索数据集合 45 | * @param sCb 事件处理监听 46 | * @param styleId 热门搜索样式(值在1到5之间) 可以在drawable下修改、添加 sousuo_bg_gray_X等样式背景 47 | */ 48 | public void initData(@Nullable List olddatalist,@Nullable List hotdata, onSearchCallBackListener sCb,int styleId){ 49 | } 50 | #### 在activity中添加: 51 | List skills = HistoryCache.toArray(getApplicationContext()); 52 | String shareHotData ="C++,C,PHP,React"; 53 | List skillHots = Arrays.asList(shareHotData.split(",")); 54 | searchLayout.initData(skills, skillHots, new onSearchCallBackListener() { 55 | @Override 56 | public void Search(String str) { 57 | //进行或联网搜索 58 | Log.e("点击",str.toString()); 59 | Bundle bundle = new Bundle(); 60 | bundle.putString("data",str); 61 | startActivity(SearchActivity.class,bundle);//此方法为跳转传值,详情见代码 62 | } 63 | @Override 64 | public void Back() { 65 | finish(); 66 | } 67 | 68 | @Override 69 | public void ClearOldData() { 70 | //清除历史搜索记录 更新记录原始数据 71 | HistoryCache.clear(getApplicationContext()); 72 | Log.e("点击","清除数据"); 73 | } 74 | @Override 75 | public void SaveOldData(ArrayList AlloldDataList) { 76 | //保存所有的搜索记录 77 | HistoryCache.saveHistory(getApplicationContext(),HistoryCache.toJsonArray(AlloldDataList)); 78 | Log.e("点击","保存数据"); 79 | } 80 | },1); 81 | 82 | #### HistoryCache 83 | HistoryCache为历史记录缓存类,包含方法如下: 84 | public static void clear(Context context):清除所有数据 85 | public static void saveHistory(Context context, String result):保存记录 86 | public static String getHistory(Context context):获取保存记录 87 | public static List toArray(Context context):把json数据转换成list 88 | public static String toJsonArray(List historyList):ArrayList 转换成JsonArray 89 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | defaultConfig { 6 | applicationId "com.tangguna.searchbox.searchbox" 7 | minSdkVersion 19 8 | targetSdkVersion 27 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(include: ['*.jar'], dir: 'libs') 23 | implementation 'com.android.support:appcompat-v7:27.0.2' 24 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 25 | testImplementation 'junit:junit:4.12' 26 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 28 | implementation project(':library') 29 | } 30 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/tangguna/searchbox/searchbox/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.tangguna.searchbox.searchbox; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.tangguna.searchbox.searchbox", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/tangguna/searchbox/searchbox/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.tangguna.searchbox.searchbox; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | 8 | import com.tangguna.searchbox.library.cache.HistoryCache; 9 | import com.tangguna.searchbox.library.callback.onSearchCallBackListener; 10 | import com.tangguna.searchbox.library.widget.SearchListLayout; 11 | import com.tangguna.searchbox.library.widget.SelfSearchListView; 12 | 13 | import java.util.ArrayList; 14 | import java.util.Arrays; 15 | import java.util.List; 16 | 17 | public class MainActivity extends AppCompatActivity { 18 | 19 | private SearchListLayout searchLayout; 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_main); 25 | searchLayout = (SearchListLayout)findViewById(R.id.searchlayout); 26 | initData(); 27 | } 28 | 29 | private void initData() { 30 | List skills = HistoryCache.toArray(getApplicationContext()); 31 | String shareHotData ="C++,C,PHP,React"; 32 | List skillHots = Arrays.asList(shareHotData.split(",")); 33 | searchLayout.initData(skills, skillHots, new onSearchCallBackListener() { 34 | @Override 35 | public void Search(String str) { 36 | //进行或联网搜索 37 | Log.e("点击",str.toString()); 38 | Bundle bundle = new Bundle(); 39 | bundle.putString("data",str); 40 | startActivity(SearchActivity.class,bundle); 41 | } 42 | @Override 43 | public void Back() { 44 | finish(); 45 | } 46 | 47 | @Override 48 | public void ClearOldData() { 49 | //清除历史搜索记录 更新记录原始数据 50 | HistoryCache.clear(getApplicationContext()); 51 | Log.e("点击","清除数据"); 52 | } 53 | @Override 54 | public void SaveOldData(ArrayList AlloldDataList) { 55 | //保存所有的搜索记录 56 | HistoryCache.saveHistory(getApplicationContext(),HistoryCache.toJsonArray(AlloldDataList)); 57 | Log.e("点击","保存数据"); 58 | } 59 | },1); 60 | } 61 | 62 | 63 | 64 | public void startActivity(Class openClass, Bundle bundle) { 65 | Intent intent = new Intent(this,openClass); 66 | if (null != bundle) 67 | intent.putExtras(bundle); 68 | startActivity(intent); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/com/tangguna/searchbox/searchbox/SearchActivity.java: -------------------------------------------------------------------------------- 1 | package com.tangguna.searchbox.searchbox; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.widget.TextView; 7 | 8 | public class SearchActivity extends AppCompatActivity { 9 | @Override 10 | protected void onCreate(@Nullable Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | setContentView(R.layout.search); 13 | TextView textView =(TextView) findViewById(R.id.tv_show); 14 | 15 | Bundle bundle = getIntent().getExtras(); 16 | if (bundle != null){ 17 | textView.setText(bundle.getString("data")); 18 | } 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/search.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SearchBox 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/tangguna/searchbox/searchbox/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.tangguna.searchbox.searchbox; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.1.2' 11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Sep 04 14:15:51 CST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /img/Screenshot_1536113887.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/img/Screenshot_1536113887.png -------------------------------------------------------------------------------- /img/Screenshot_1536113915.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/img/Screenshot_1536113915.png -------------------------------------------------------------------------------- /img/Screenshot_1536113922.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/img/Screenshot_1536113922.png -------------------------------------------------------------------------------- /img/Screenshot_1536113962.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/img/Screenshot_1536113962.png -------------------------------------------------------------------------------- /img/Screenshot_1536126402.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/img/Screenshot_1536126402.png -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | group='com.github.tangguna' 4 | android { 5 | compileSdkVersion 27 6 | 7 | 8 | 9 | defaultConfig { 10 | minSdkVersion 19 11 | targetSdkVersion 27 12 | versionCode 1 13 | versionName "1.0" 14 | 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | 26 | } 27 | 28 | dependencies { 29 | implementation fileTree(dir: 'libs', include: ['*.jar']) 30 | 31 | implementation 'com.android.support:appcompat-v7:27.0.2' 32 | testImplementation 'junit:junit:4.12' 33 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 34 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 35 | api 'com.google.code.gson:gson:2.7' 36 | } 37 | -------------------------------------------------------------------------------- /library/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/tangguna/searchbox/library/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.tangguna.searchbox.library; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.tangguna.searchbox.library.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /library/src/main/java/com/tangguna/searchbox/library/adapter/HistoryDataAdapter.java: -------------------------------------------------------------------------------- 1 | package com.tangguna.searchbox.library.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.BaseAdapter; 7 | import android.widget.TextView; 8 | 9 | import com.tangguna.searchbox.library.R; 10 | 11 | import java.util.ArrayList; 12 | 13 | /** 14 | * GridView列表展示历史记录数据 15 | */ 16 | public class HistoryDataAdapter extends BaseAdapter { 17 | 18 | private Context context; 19 | private ArrayList list = new ArrayList(); 20 | 21 | 22 | public HistoryDataAdapter(Context context, ArrayList list) { 23 | this.context = context; 24 | this.list = list; 25 | } 26 | 27 | 28 | @Override 29 | public int getCount() { 30 | return list.size(); 31 | } 32 | 33 | @Override 34 | public Object getItem(int i) { 35 | return list.get(i); 36 | } 37 | 38 | @Override 39 | public long getItemId(int i) { 40 | return i; 41 | } 42 | 43 | @Override 44 | public View getView(int i, View view, ViewGroup viewGroup) { 45 | ViewHolder holder = null; 46 | if (view == null){ 47 | holder = new ViewHolder(); 48 | view = View.inflate(context,R.layout.search_olddata_item,null); 49 | holder.tv = (TextView) view.findViewById(R.id.text); 50 | view.setTag(holder); 51 | }else { 52 | holder = (ViewHolder) view.getTag(); 53 | } 54 | holder.tv.setText(list.get(i)); 55 | return view; 56 | } 57 | 58 | public class ViewHolder{ 59 | TextView tv; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /library/src/main/java/com/tangguna/searchbox/library/adapter/HistoryDataListViewAdapter.java: -------------------------------------------------------------------------------- 1 | package com.tangguna.searchbox.library.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.BaseAdapter; 7 | import android.widget.ImageView; 8 | import android.widget.TextView; 9 | 10 | import com.tangguna.searchbox.library.R; 11 | 12 | import java.util.ArrayList; 13 | 14 | /** 15 | * ListView列表展示历史记录 16 | */ 17 | public class HistoryDataListViewAdapter extends BaseAdapter { 18 | 19 | private Context context; 20 | private ArrayList list = new ArrayList(); 21 | 22 | public HistoryDataListViewAdapter(Context context, ArrayList list) { 23 | this.context = context; 24 | this.list = list; 25 | } 26 | 27 | 28 | @Override 29 | public int getCount() { 30 | return list.size(); 31 | } 32 | 33 | @Override 34 | public Object getItem(int i) { 35 | return list.get(i); 36 | } 37 | 38 | @Override 39 | public long getItemId(int i) { 40 | return i; 41 | } 42 | 43 | @Override 44 | public View getView(int i, View view, ViewGroup viewGroup) { 45 | ViewHolder holder = null; 46 | if (view == null){ 47 | holder = new ViewHolder(); 48 | view = View.inflate(context,R.layout.list_item,null); 49 | holder.tv_info = (TextView)view.findViewById(R.id.tv_info); 50 | view.setTag(holder); 51 | }else { 52 | holder = (ViewHolder) view.getTag(); 53 | } 54 | holder.tv_info.setText(list.get(i)); 55 | return view; 56 | } 57 | 58 | class ViewHolder{ 59 | TextView tv_info; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /library/src/main/java/com/tangguna/searchbox/library/cache/HistoryCache.java: -------------------------------------------------------------------------------- 1 | package com.tangguna.searchbox.library.cache; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import com.google.gson.Gson; 6 | import com.google.gson.reflect.TypeToken; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * 历史缓存工具类 12 | */ 13 | public class HistoryCache { 14 | 15 | /** 16 | * 清除所有数据 17 | * @param context 18 | */ 19 | public static void clear(Context context) { 20 | SharedPreferences sp = context.getSharedPreferences("record", Context.MODE_PRIVATE); 21 | sp.edit().clear().commit(); 22 | } 23 | 24 | /** 25 | * 保存历史记录到SharedPreferences中 26 | * 27 | * @param result 28 | */ 29 | public static void saveHistory(Context context, String result) { 30 | SharedPreferences sp = context.getSharedPreferences("record", Context.MODE_PRIVATE); 31 | SharedPreferences.Editor editor = sp.edit(); 32 | editor.putString("record_key", result); 33 | editor.commit(); 34 | } 35 | 36 | /** 37 | * 获取保存在SharedPreferences中的历史记录 38 | * 39 | * @return 40 | */ 41 | public static String getHistory(Context context) { 42 | SharedPreferences sp = context.getSharedPreferences("record", Context.MODE_PRIVATE); 43 | String result = sp.getString("record_key", null); 44 | return result; 45 | } 46 | 47 | /** 48 | * 把json数据转换成list 49 | * @param context 50 | * @return 51 | */ 52 | public static List toArray(Context context) { 53 | 54 | String history = getHistory(context); 55 | Gson gson = new Gson(); 56 | List retList = gson.fromJson(history, new TypeToken>() { 57 | }.getType()); 58 | return retList; 59 | } 60 | 61 | /** 62 | * ArrayList 转换成JsonArray 63 | * @param historyList 64 | * @return 65 | */ 66 | public static String toJsonArray(List historyList) { 67 | 68 | Gson gson = new Gson(); 69 | return gson.toJson(historyList); // 将JSONArray转换得到String 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /library/src/main/java/com/tangguna/searchbox/library/callback/onSearchCallBackListener.java: -------------------------------------------------------------------------------- 1 | package com.tangguna.searchbox.library.callback; 2 | 3 | import android.os.Bundle; 4 | 5 | import java.util.ArrayList; 6 | 7 | public interface onSearchCallBackListener { 8 | /** 9 | * @param str 搜索关键字 10 | */ 11 | public abstract void Search(String str); 12 | /** 13 | * 后退 14 | */ 15 | public abstract void Back(); 16 | /** 17 | * 清除历史搜索记录 18 | */ 19 | public abstract void ClearOldData(); 20 | 21 | /** 22 | * 保存搜索记录 23 | */ 24 | public abstract void SaveOldData(ArrayList AlloldDataList); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /library/src/main/java/com/tangguna/searchbox/library/widget/FlowLayout.java: -------------------------------------------------------------------------------- 1 | package com.tangguna.searchbox.library.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * 流布局 13 | */ 14 | public class FlowLayout extends ViewGroup { 15 | 16 | public FlowLayout(Context context) { 17 | super(context); 18 | } 19 | 20 | public FlowLayout(Context context, AttributeSet attrs) { 21 | super(context, attrs); 22 | } 23 | 24 | public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) { 25 | super(context, attrs, defStyleAttr); 26 | } 27 | 28 | 29 | 30 | @Override 31 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 32 | 33 | int widthsize = MeasureSpec.getSize(widthMeasureSpec); 34 | int heightsize = MeasureSpec.getSize(heightMeasureSpec); 35 | 36 | int widthmode = MeasureSpec.getMode(widthMeasureSpec); 37 | int heightmode = MeasureSpec.getMode(heightMeasureSpec); 38 | 39 | // wrap_content 40 | int width = 0; 41 | int height = 0; 42 | 43 | // 记录每一行的宽度与高度 44 | int lineWidth = 0; 45 | int lineHeight = 0; 46 | 47 | // 得到内部元素的个数 48 | int cCount = getChildCount(); 49 | 50 | 51 | for(int i=0;i widthsize - (getPaddingLeft() + getPaddingRight())) { 70 | // 对比得到最大的宽度 71 | width = Math.max(width, lineWidth); 72 | // 重置lineWidth 73 | lineWidth = childWidth; 74 | 75 | // 记录行高 76 | height += lineHeight; 77 | lineHeight = childHeight; 78 | } 79 | else 80 | { 81 | //未换行 82 | // 叠加行宽 83 | lineWidth += childWidth; 84 | // 得到当前行最大的高度 85 | lineHeight = Math.max(lineHeight, childHeight); 86 | } 87 | 88 | // 最后一个控件 89 | if (i == cCount - 1) 90 | { 91 | width = Math.max(lineWidth, width); 92 | height += lineHeight; 93 | } 94 | } 95 | 96 | 97 | setMeasuredDimension( 98 | widthmode == MeasureSpec.EXACTLY ? widthsize : width + getPaddingLeft() + getPaddingRight(), 99 | heightmode == MeasureSpec.EXACTLY ? heightsize : height + getPaddingTop() + getPaddingBottom()// 100 | ); 101 | } 102 | 103 | 104 | /** 105 | * 存储所有的View 106 | */ 107 | private List> mAllViews = new ArrayList>(); 108 | /** 109 | * 每一行的高度 110 | */ 111 | private List mLineHeight = new ArrayList(); 112 | 113 | @Override 114 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 115 | mAllViews.clear(); 116 | mLineHeight.clear(); 117 | // 当前ViewGroup的宽度 118 | int width = getWidth(); 119 | int lineWidth = 0; 120 | int lineHeight = 0; 121 | List lineViews = new ArrayList(); 122 | int cCount = getChildCount(); 123 | for (int i = 0; i < cCount; i++) 124 | { 125 | View child = getChildAt(i); 126 | MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 127 | int childWidth = child.getMeasuredWidth(); 128 | int childHeight = child.getMeasuredHeight(); 129 | // 如果需要换行 130 | if (childWidth + lineWidth + lp.leftMargin + lp.rightMargin > width - getPaddingLeft() - getPaddingRight()) { 131 | // 记录LineHeight 132 | mLineHeight.add(lineHeight); 133 | // 记录当前行的Views 134 | mAllViews.add(lineViews); 135 | // 重置我们的行宽和行高 136 | lineWidth = 0; 137 | lineHeight = childHeight + lp.topMargin + lp.bottomMargin; 138 | // 重置我们的View集合 139 | lineViews = new ArrayList(); 140 | } 141 | 142 | lineWidth += childWidth + lp.leftMargin + lp.rightMargin; 143 | lineHeight = Math.max(lineHeight, childHeight + lp.topMargin + lp.bottomMargin); 144 | lineViews.add(child); 145 | } 146 | // for end 147 | // 处理最后一行 148 | mLineHeight.add(lineHeight); 149 | mAllViews.add(lineViews); 150 | 151 | // 设置子View的位置 152 | int left = getPaddingLeft(); 153 | int top = getPaddingTop(); 154 | // 行数 155 | int lineNum = mAllViews.size(); 156 | for (int i = 0; i < lineNum; i++) 157 | { 158 | // 当前行的所有的View 159 | lineViews = mAllViews.get(i); 160 | lineHeight = mLineHeight.get(i);//相对开始的高度 161 | for (int j = 0; j < lineViews.size(); j++) 162 | { 163 | View child = lineViews.get(j); 164 | // 判断child的状态 165 | if (child.getVisibility() == View.GONE) 166 | { 167 | continue; 168 | } 169 | 170 | MarginLayoutParams lp = (MarginLayoutParams) child 171 | .getLayoutParams(); 172 | 173 | int lc = left + lp.leftMargin; 174 | int tc = top + lp.topMargin; 175 | int rc = lc + child.getMeasuredWidth(); 176 | int bc = tc + child.getMeasuredHeight(); 177 | 178 | // 为子View进行布局 179 | child.layout(lc, tc, rc, bc); 180 | 181 | left += child.getMeasuredWidth() + lp.leftMargin 182 | + lp.rightMargin; 183 | } 184 | 185 | left = getPaddingLeft() ; 186 | top += lineHeight ; 187 | } 188 | } 189 | 190 | /** 191 | * 与当前ViewGroup对应的LayoutParams 192 | */ 193 | @Override 194 | public LayoutParams generateLayoutParams(AttributeSet attrs) 195 | { 196 | return new MarginLayoutParams(getContext(), attrs); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /library/src/main/java/com/tangguna/searchbox/library/widget/SearchLayout.java: -------------------------------------------------------------------------------- 1 | package com.tangguna.searchbox.library.widget; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Color; 6 | import android.graphics.drawable.ColorDrawable; 7 | import android.support.annotation.Nullable; 8 | import android.text.Editable; 9 | import android.text.TextWatcher; 10 | import android.util.AttributeSet; 11 | import android.util.Log; 12 | import android.view.KeyEvent; 13 | import android.view.LayoutInflater; 14 | import android.view.View; 15 | import android.view.inputmethod.EditorInfo; 16 | import android.widget.AdapterView; 17 | import android.widget.Button; 18 | import android.widget.EditText; 19 | import android.widget.ImageView; 20 | import android.widget.LinearLayout; 21 | import android.widget.TextView; 22 | 23 | import com.tangguna.searchbox.library.R; 24 | import com.tangguna.searchbox.library.adapter.HistoryDataAdapter; 25 | import com.tangguna.searchbox.library.callback.onSearchCallBackListener; 26 | 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | import java.util.Random; 30 | 31 | /** 32 | * 搜索框 GridView 33 | */ 34 | public class SearchLayout extends LinearLayout { 35 | 36 | private String msearch_hint; 37 | private int msearch_baground; 38 | private Context context; 39 | private ImageView ib_searchtext_delete; 40 | private EditText et_searchtext_search; 41 | private LinearLayout searchview; 42 | private Button bt_text_search_back; 43 | private TextView tvclearolddata; 44 | //历史搜索 45 | private SelfSearchGridView gridViewData; 46 | private HistoryDataAdapter historyDataAdapter; 47 | private ArrayList historyList = new ArrayList<>(); 48 | //热门搜索 49 | FlowLayout hotflowLayout; 50 | 51 | private String backtitle="取消",searchtitle="搜索"; 52 | private OnClickListener TextViewItemListener; 53 | private int countOldDataSize=15;//默认搜索记录的条数, 正确的是传入进来的条数 54 | 55 | public SearchLayout(Context context) { 56 | super(context); 57 | this.context = context; 58 | initView(); 59 | } 60 | 61 | public SearchLayout(Context context, @Nullable AttributeSet attrs) { 62 | super(context, attrs); 63 | this.context = context; 64 | 65 | TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.searchmlist); 66 | msearch_hint = array.getString(R.styleable.searchmlist_search_hint); 67 | msearch_baground = array.getResourceId(R.styleable.searchmlist_search_baground,R.drawable.search_baground_shap); 68 | array.recycle(); 69 | initView(); 70 | } 71 | 72 | public SearchLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 73 | super(context, attrs, defStyleAttr); 74 | this.context = context; 75 | initView(); 76 | } 77 | 78 | private void initView() { 79 | 80 | backtitle=getResources().getString(R.string.search_cancel); 81 | searchtitle=getResources().getString(R.string.search_verify); 82 | searchview =(LinearLayout) LayoutInflater.from(context).inflate(R.layout.msearchlayout, null); 83 | //把获得的view加载到这个控件中 84 | addView(searchview); 85 | //把两个按钮从布局文件中找到 86 | ib_searchtext_delete = (ImageView) searchview.findViewById(R.id.ib_searchtext_delete); 87 | et_searchtext_search = (EditText) searchview.findViewById(R.id.et_searchtext_search); 88 | et_searchtext_search.setBackgroundResource(msearch_baground); 89 | et_searchtext_search.setHint(msearch_hint); 90 | //搜索返回时一个按钮 91 | bt_text_search_back = (Button) searchview.findViewById(R.id.buttonback); 92 | //清除历史记录 93 | tvclearolddata = (TextView) searchview.findViewById(R.id.tvclearolddata); 94 | 95 | gridViewData= (SelfSearchGridView)searchview.findViewById(R.id.gridviewolddata); 96 | gridViewData.setSelector(new ColorDrawable(Color.TRANSPARENT));//去除背景点击效果 97 | 98 | hotflowLayout = (FlowLayout)searchview.findViewById(R.id.id_flowlayouthot); 99 | setLinstener(); 100 | } 101 | 102 | //文本观察者 103 | private class MyTextWatcher implements TextWatcher { 104 | 105 | @Override 106 | public void afterTextChanged(Editable s) { 107 | 108 | } 109 | 110 | @Override 111 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 112 | et_searchtext_search.setCursorVisible(true); 113 | et_searchtext_search.setSelection(et_searchtext_search.getText().toString().length()); 114 | } 115 | //当文本改变时候的操作 116 | @Override 117 | public void onTextChanged(CharSequence s, int start, int before, int count) { 118 | //如果编辑框中文本的长度大于0就显示删除按钮否则不显示 119 | if(s.length() > 0){ 120 | ib_searchtext_delete.setVisibility(View.VISIBLE); 121 | bt_text_search_back.setText(searchtitle); 122 | }else{ 123 | ib_searchtext_delete.setVisibility(View.GONE); 124 | bt_text_search_back.setText(backtitle); 125 | } 126 | } 127 | 128 | } 129 | 130 | private void setLinstener() { 131 | //给删除按钮添加点击事件 132 | ib_searchtext_delete.setOnClickListener(new OnClickListener() { 133 | @Override 134 | public void onClick(View v) { 135 | // TODO Auto-generated method stub 136 | et_searchtext_search.setText(""); 137 | } 138 | }); 139 | //给编辑框添加文本改变事件 140 | et_searchtext_search.addTextChangedListener(new MyTextWatcher()); 141 | 142 | et_searchtext_search.setOnEditorActionListener(new TextView.OnEditorActionListener() { 143 | public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 144 | if (actionId == EditorInfo.IME_ACTION_SEARCH || (event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) { 145 | 146 | String searchtext = et_searchtext_search.getText().toString().trim(); 147 | //dosoming 148 | // Toast.makeText(context, "搜索" +searchtext, Toast.LENGTH_SHORT).show(); 149 | executeSearch_and_NotifyDataSetChanged(searchtext); 150 | return true; 151 | } 152 | return false; 153 | } 154 | }); 155 | 156 | 157 | 158 | bt_text_search_back.setOnClickListener(new OnClickListener() { 159 | @Override 160 | public void onClick(View v) { 161 | String searchtext = et_searchtext_search.getText().toString().trim(); 162 | if (bt_text_search_back.getText().toString().equals(searchtitle)) { 163 | // Toast.makeText(context, "点击button搜索" + searchtext, Toast.LENGTH_SHORT).show(); 164 | executeSearch_and_NotifyDataSetChanged(searchtext); 165 | } else { 166 | // Toast.makeText(context, "点击button 返回", Toast.LENGTH_SHORT).show(); 167 | if (sCBlistener != null) 168 | sCBlistener.Back(); 169 | } 170 | } 171 | }); 172 | 173 | 174 | 175 | tvclearolddata.setOnClickListener(new OnClickListener() { 176 | @Override 177 | public void onClick(View v) { 178 | if(sCBlistener!=null) { 179 | historyList.clear(); 180 | historyDataAdapter.notifyDataSetChanged(); 181 | sCBlistener.ClearOldData(); 182 | } 183 | } 184 | }); 185 | 186 | 187 | TextViewItemListener = new OnClickListener(){ 188 | @Override 189 | public void onClick(View v) { 190 | String string = ((TextView)v).getText().toString(); 191 | // Toast.makeText(context, "Item点击"+string, Toast.LENGTH_SHORT).show(); 192 | executeSearch_and_NotifyDataSetChanged(string); 193 | 194 | } 195 | }; 196 | 197 | gridViewData.setOnItemClickListener(new AdapterView.OnItemClickListener() { 198 | @Override 199 | public void onItemClick(AdapterView parent, View view, int position, long id) { 200 | if(sCBlistener!=null){ 201 | sCBlistener.Search(historyList.get(position).trim()); 202 | et_searchtext_search.setText(historyList.get(position).trim()); 203 | et_searchtext_search.setCursorVisible(false); 204 | et_searchtext_search.setSelection(et_searchtext_search.getText().toString().length()); 205 | //点击项移动到首部位 206 | swap(historyList,position,0); 207 | historyDataAdapter.notifyDataSetChanged(); 208 | } 209 | } 210 | }); 211 | 212 | } 213 | 214 | 215 | /** 216 | * 217 | * @param olddatalist 历史搜索数据集合 218 | * @param hotdata 热门搜索数据集合 219 | * @param sCb 事件处理监听 220 | * @param styleId 热门搜索样式(值在1到5之间) 可以在drawable下修改、添加 sousuo_bg_gray_X等样式背景 221 | */ 222 | public void initData(@Nullable List olddatalist,@Nullable List hotdata, onSearchCallBackListener sCb,int styleId){ 223 | SetCallBackListener(sCb); 224 | hotflowLayout.removeAllViews(); 225 | historyList.clear(); 226 | if(olddatalist!=null) 227 | historyList.addAll(olddatalist); 228 | historyDataAdapter = new HistoryDataAdapter(context,historyList); 229 | gridViewData.setAdapter(historyDataAdapter); 230 | 231 | 232 | LayoutInflater mInflater = LayoutInflater.from(context); 233 | if (hotdata != null){ 234 | for (int i = 0; i < hotdata.size(); i++) 235 | { 236 | TextView tv = (TextView) mInflater.inflate(R.layout.suosou_item,hotflowLayout, false); 237 | tv.setText(hotdata.get(i)); 238 | tv.setOnClickListener(TextViewItemListener); 239 | tv.getBackground().setLevel(MyRandom(1,styleId)); 240 | hotflowLayout.addView(tv); 241 | } 242 | }else { 243 | return; 244 | } 245 | } 246 | 247 | 248 | 249 | private void executeSearch_and_NotifyDataSetChanged(String str){ 250 | if(sCBlistener!=null&&(!str.equals(""))){ 251 | if (historyList.size() > 0 && historyList.get(0).equals(str)) { 252 | } 253 | else 254 | { 255 | if (historyList.size() == countOldDataSize && historyList.size()>0) 256 | historyList.remove(historyList.size() - 1); 257 | //判断字符串是否在历史记录中 如果在就不添加,反之则添加 258 | if (historyList != null && !historyList.contains(str)) 259 | { 260 | historyList.add(0, str);//把最新的添加到前面 261 | historyDataAdapter.notifyDataSetChanged(); 262 | sCBlistener.SaveOldData(historyList); 263 | }else { 264 | historyList.remove(str); 265 | historyList.add(0, str);//把最新的添加到前面 266 | historyDataAdapter.notifyDataSetChanged(); 267 | sCBlistener.SaveOldData(historyList); 268 | } 269 | } 270 | et_searchtext_search.setText(str); 271 | sCBlistener.Search(str); 272 | et_searchtext_search.setCursorVisible(false); 273 | et_searchtext_search.setSelection(et_searchtext_search.getText().toString().length()); 274 | } 275 | } 276 | 277 | 278 | 279 | 280 | /** 281 | * 生成随机数 282 | * @param max 最大值 283 | * @param min 最小值 284 | * @return 285 | */ 286 | public int MyRandom(int min,int max){ 287 | Random random = new Random(); 288 | int s = random.nextInt(max)%(max-min+1) + min; 289 | return s; 290 | } 291 | 292 | private onSearchCallBackListener sCBlistener; 293 | /** 294 | * 设置接口回调 295 | * @param onSearchCallBackListener setCallBackListener接口 296 | */ 297 | private void SetCallBackListener(onSearchCallBackListener onSearchCallBackListener){ 298 | sCBlistener=onSearchCallBackListener; 299 | } 300 | 301 | 302 | /** 303 | * 调换集合中两个指定位置的元素, 若两个元素位置中间还有其他元素,需要实现中间元素的前移或后移的操作。 304 | * @param list 集合 305 | * @param oldPosition 需要调换的元素 306 | * @param newPosition 被调换的元素 307 | * @param 308 | */ 309 | public static void swap(List list, int oldPosition, int newPosition){ 310 | if(null == list){ 311 | throw new IllegalStateException("集合不能为空"); 312 | } 313 | T tempElement = list.get(oldPosition); 314 | 315 | // 向前移动,前面的元素需要向后移动 316 | if(oldPosition < newPosition){ 317 | for(int i = oldPosition; i < newPosition; i++){ 318 | list.set(i, list.get(i + 1)); 319 | } 320 | list.set(newPosition, tempElement); 321 | } 322 | // 向后移动,后面的元素需要向前移动 323 | if(oldPosition > newPosition){ 324 | for(int i = oldPosition; i > newPosition; i--){ 325 | list.set(i, list.get(i - 1)); 326 | } 327 | list.set(newPosition, tempElement); 328 | } 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /library/src/main/java/com/tangguna/searchbox/library/widget/SearchListLayout.java: -------------------------------------------------------------------------------- 1 | package com.tangguna.searchbox.library.widget; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Color; 6 | import android.graphics.drawable.ColorDrawable; 7 | import android.support.annotation.Nullable; 8 | import android.text.Editable; 9 | import android.text.TextWatcher; 10 | import android.util.AttributeSet; 11 | import android.view.KeyEvent; 12 | import android.view.LayoutInflater; 13 | import android.view.View; 14 | import android.view.inputmethod.EditorInfo; 15 | import android.widget.AdapterView; 16 | import android.widget.Button; 17 | import android.widget.EditText; 18 | import android.widget.ImageView; 19 | import android.widget.LinearLayout; 20 | import android.widget.TextView; 21 | 22 | import com.tangguna.searchbox.library.R; 23 | import com.tangguna.searchbox.library.adapter.HistoryDataAdapter; 24 | import com.tangguna.searchbox.library.adapter.HistoryDataListViewAdapter; 25 | import com.tangguna.searchbox.library.cache.HistoryCache; 26 | import com.tangguna.searchbox.library.callback.onSearchCallBackListener; 27 | 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | import java.util.Random; 31 | 32 | /** 33 | * 搜索框ListView 34 | */ 35 | public class SearchListLayout extends LinearLayout { 36 | private String msearch_hint; 37 | private int msearch_baground; 38 | private Context context; 39 | private ImageView ib_searchtext_delete; 40 | private EditText et_searchtext_search; 41 | private LinearLayout searchview; 42 | private LinearLayout ll_clear; 43 | private Button bt_text_search_back; 44 | private TextView tvclearolddata; 45 | //历史搜索 46 | private SelfSearchListView gridViewData; 47 | private HistoryDataListViewAdapter historyDataAdapter; 48 | private ArrayList historyList = new ArrayList<>(); 49 | //热门搜索 50 | FlowLayout hotflowLayout; 51 | 52 | private String backtitle="取消",searchtitle="搜索"; 53 | private View.OnClickListener TextViewItemListener; 54 | private int countOldDataSize=15;//默认搜索记录的条数, 正确的是传入进来的条数 55 | 56 | public SearchListLayout(Context context) { 57 | super(context); 58 | this.context = context; 59 | initView(); 60 | } 61 | 62 | public SearchListLayout(Context context, @Nullable AttributeSet attrs) { 63 | super(context, attrs); 64 | this.context = context; 65 | 66 | TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.searchmlist); 67 | msearch_hint = array.getString(R.styleable.searchmlist_search_hint); 68 | msearch_baground = array.getResourceId(R.styleable.searchmlist_search_baground,R.drawable.search_baground_shap); 69 | array.recycle(); 70 | initView(); 71 | } 72 | 73 | public SearchListLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 74 | super(context, attrs, defStyleAttr); 75 | this.context = context; 76 | initView(); 77 | } 78 | 79 | private void initView() { 80 | backtitle=getResources().getString(R.string.search_cancel); 81 | searchtitle=getResources().getString(R.string.search_verify); 82 | searchview =(LinearLayout) LayoutInflater.from(context).inflate(R.layout.searchlistlayout, null); 83 | //把获得的view加载到这个控件中 84 | addView(searchview); 85 | //把两个按钮从布局文件中找到 86 | ib_searchtext_delete = (ImageView) searchview.findViewById(R.id.ib_searchtext_delete); 87 | et_searchtext_search = (EditText) searchview.findViewById(R.id.et_searchtext_search); 88 | et_searchtext_search.setBackgroundResource(msearch_baground); 89 | et_searchtext_search.setHint(msearch_hint); 90 | //搜索返回时一个按钮 91 | bt_text_search_back = (Button) searchview.findViewById(R.id.buttonback); 92 | //清除历史记录 93 | tvclearolddata = (TextView) searchview.findViewById(R.id.tvclearolddata); 94 | //清空搜索记录 95 | ll_clear = (LinearLayout) findViewById(R.id.ll_clear); 96 | gridViewData= (SelfSearchListView) searchview.findViewById(R.id.gridviewolddata); 97 | gridViewData.setSelector(new ColorDrawable(Color.TRANSPARENT));//去除背景点击效果 98 | 99 | hotflowLayout = (FlowLayout)searchview.findViewById(R.id.id_flowlayouthot); 100 | setLinstener(); 101 | } 102 | 103 | //文本观察者 104 | private class MyTextWatcher implements TextWatcher { 105 | 106 | @Override 107 | public void afterTextChanged(Editable s) { 108 | 109 | } 110 | 111 | @Override 112 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 113 | et_searchtext_search.setCursorVisible(true); 114 | et_searchtext_search.setSelection(et_searchtext_search.getText().toString().length()); 115 | } 116 | //当文本改变时候的操作 117 | @Override 118 | public void onTextChanged(CharSequence s, int start, int before, int count) { 119 | //如果编辑框中文本的长度大于0就显示删除按钮否则不显示 120 | if(s.length() > 0){ 121 | ib_searchtext_delete.setVisibility(View.VISIBLE); 122 | bt_text_search_back.setText(searchtitle); 123 | }else{ 124 | ib_searchtext_delete.setVisibility(View.GONE); 125 | bt_text_search_back.setText(backtitle); 126 | } 127 | } 128 | 129 | } 130 | 131 | private void setLinstener() { 132 | /** 133 | * 清空历史记录 134 | */ 135 | ll_clear.setOnClickListener(new OnClickListener() { 136 | @Override 137 | public void onClick(View view) { 138 | if(sCBlistener!=null) { 139 | historyList.clear(); 140 | historyDataAdapter.notifyDataSetChanged(); 141 | sCBlistener.ClearOldData(); 142 | } 143 | ll_clear.setVisibility(GONE); 144 | } 145 | }); 146 | //给删除按钮添加点击事件 147 | ib_searchtext_delete.setOnClickListener(new View.OnClickListener() { 148 | @Override 149 | public void onClick(View v) { 150 | // TODO Auto-generated method stub 151 | et_searchtext_search.setText(""); 152 | } 153 | }); 154 | //给编辑框添加文本改变事件 155 | et_searchtext_search.addTextChangedListener(new MyTextWatcher()); 156 | 157 | et_searchtext_search.setOnEditorActionListener(new TextView.OnEditorActionListener() { 158 | public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 159 | if (actionId == EditorInfo.IME_ACTION_SEARCH || (event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) { 160 | 161 | String searchtext = et_searchtext_search.getText().toString().trim(); 162 | //dosoming 163 | // Toast.makeText(context, "搜索" +searchtext, Toast.LENGTH_SHORT).show(); 164 | executeSearch_and_NotifyDataSetChanged(searchtext); 165 | return true; 166 | } 167 | return false; 168 | } 169 | }); 170 | 171 | 172 | 173 | bt_text_search_back.setOnClickListener(new View.OnClickListener() { 174 | @Override 175 | public void onClick(View v) { 176 | String searchtext = et_searchtext_search.getText().toString().trim(); 177 | if (bt_text_search_back.getText().toString().equals(searchtitle)) { 178 | // Toast.makeText(context, "点击button搜索" + searchtext, Toast.LENGTH_SHORT).show(); 179 | executeSearch_and_NotifyDataSetChanged(searchtext); 180 | } else { 181 | // Toast.makeText(context, "点击button 返回", Toast.LENGTH_SHORT).show(); 182 | if (sCBlistener != null) 183 | sCBlistener.Back(); 184 | } 185 | } 186 | }); 187 | 188 | 189 | 190 | tvclearolddata.setOnClickListener(new View.OnClickListener() { 191 | @Override 192 | public void onClick(View v) { 193 | if(sCBlistener!=null) { 194 | historyList.clear(); 195 | historyDataAdapter.notifyDataSetChanged(); 196 | sCBlistener.ClearOldData(); 197 | } 198 | } 199 | }); 200 | 201 | 202 | TextViewItemListener = new View.OnClickListener(){ 203 | @Override 204 | public void onClick(View v) { 205 | String string = ((TextView)v).getText().toString(); 206 | // Toast.makeText(context, "Item点击"+string, Toast.LENGTH_SHORT).show(); 207 | executeSearch_and_NotifyDataSetChanged(string); 208 | 209 | } 210 | }; 211 | 212 | gridViewData.setOnItemClickListener(new AdapterView.OnItemClickListener() { 213 | @Override 214 | public void onItemClick(AdapterView parent, View view, int position, long id) { 215 | if(sCBlistener!=null){ 216 | sCBlistener.Search(historyList.get(position).trim()); 217 | et_searchtext_search.setText(historyList.get(position).trim()); 218 | et_searchtext_search.setCursorVisible(false); 219 | et_searchtext_search.setSelection(et_searchtext_search.getText().toString().length()); 220 | //点击项移动到首部位 221 | swap(historyList,position,0); 222 | historyDataAdapter.notifyDataSetChanged(); 223 | } 224 | } 225 | }); 226 | 227 | } 228 | 229 | 230 | /** 231 | * 232 | * @param olddatalist 历史搜索数据集合 233 | * @param hotdata 热门搜索数据集合 234 | * @param sCb 事件处理监听 235 | * @param styleId 热门搜索样式(值在1到5之间) 可以在drawable下修改、添加 sousuo_bg_gray_X等样式背景 236 | */ 237 | public void initData(@Nullable List olddatalist, @Nullable List hotdata, onSearchCallBackListener sCb, int styleId){ 238 | SetCallBackListener(sCb); 239 | hotflowLayout.removeAllViews(); 240 | historyList.clear(); 241 | if(olddatalist!=null) 242 | historyList.addAll(olddatalist); 243 | ll_clear.setVisibility(VISIBLE); 244 | historyDataAdapter = new HistoryDataListViewAdapter(context,historyList); 245 | gridViewData.setAdapter(historyDataAdapter); 246 | 247 | 248 | LayoutInflater mInflater = LayoutInflater.from(context); 249 | if (hotdata != null){ 250 | for (int i = 0; i < hotdata.size(); i++) 251 | { 252 | TextView tv = (TextView) mInflater.inflate(R.layout.suosou_item,hotflowLayout, false); 253 | tv.setText(hotdata.get(i)); 254 | tv.setOnClickListener(TextViewItemListener); 255 | tv.getBackground().setLevel(MyRandom(1,styleId)); 256 | hotflowLayout.addView(tv); 257 | } 258 | }else { 259 | return; 260 | } 261 | } 262 | 263 | 264 | 265 | private void executeSearch_and_NotifyDataSetChanged(String str){ 266 | if(sCBlistener!=null&&(!str.equals(""))){ 267 | if (historyList.size() > 0 && historyList.get(0).equals(str)) { 268 | ll_clear.setVisibility(VISIBLE); 269 | } 270 | else 271 | { 272 | if (historyList.size() == countOldDataSize && historyList.size()>0) 273 | historyList.remove(historyList.size() - 1); 274 | //判断字符串是否在历史记录中 如果在就不添加,反之则添加 275 | if (historyList != null && !historyList.contains(str)) 276 | { 277 | historyList.add(0, str);//把最新的添加到前面 278 | historyDataAdapter.notifyDataSetChanged(); 279 | sCBlistener.SaveOldData(historyList); 280 | }else { 281 | historyList.remove(str); 282 | historyList.add(0, str);//把最新的添加到前面 283 | historyDataAdapter.notifyDataSetChanged(); 284 | sCBlistener.SaveOldData(historyList); 285 | } 286 | } 287 | ll_clear.setVisibility(VISIBLE); 288 | et_searchtext_search.setText(str); 289 | sCBlistener.Search(str); 290 | et_searchtext_search.setCursorVisible(false); 291 | et_searchtext_search.setSelection(et_searchtext_search.getText().toString().length()); 292 | } 293 | } 294 | 295 | 296 | 297 | 298 | /** 299 | * 生成随机数 300 | * @param max 最大值 301 | * @param min 最小值 302 | * @return 303 | */ 304 | public int MyRandom(int min,int max){ 305 | Random random = new Random(); 306 | int s = random.nextInt(max)%(max-min+1) + min; 307 | return s; 308 | } 309 | 310 | private onSearchCallBackListener sCBlistener; 311 | /** 312 | * 设置接口回调 313 | * @param onSearchCallBackListener setCallBackListener接口 314 | */ 315 | private void SetCallBackListener(onSearchCallBackListener onSearchCallBackListener){ 316 | sCBlistener=onSearchCallBackListener; 317 | } 318 | 319 | 320 | /** 321 | * 调换集合中两个指定位置的元素, 若两个元素位置中间还有其他元素,需要实现中间元素的前移或后移的操作。 322 | * @param list 集合 323 | * @param oldPosition 需要调换的元素 324 | * @param newPosition 被调换的元素 325 | * @param 326 | */ 327 | public static void swap(List list, int oldPosition, int newPosition){ 328 | if(null == list){ 329 | throw new IllegalStateException("集合不能为空"); 330 | } 331 | T tempElement = list.get(oldPosition); 332 | 333 | // 向前移动,前面的元素需要向后移动 334 | if(oldPosition < newPosition){ 335 | for(int i = oldPosition; i < newPosition; i++){ 336 | list.set(i, list.get(i + 1)); 337 | } 338 | list.set(newPosition, tempElement); 339 | } 340 | // 向后移动,后面的元素需要向前移动 341 | if(oldPosition > newPosition){ 342 | for(int i = oldPosition; i > newPosition; i--){ 343 | list.set(i, list.get(i - 1)); 344 | } 345 | list.set(newPosition, tempElement); 346 | } 347 | } 348 | } 349 | -------------------------------------------------------------------------------- /library/src/main/java/com/tangguna/searchbox/library/widget/SelfSearchGridView.java: -------------------------------------------------------------------------------- 1 | package com.tangguna.searchbox.library.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.ViewGroup; 6 | import android.widget.GridView; 7 | 8 | public class SelfSearchGridView extends GridView { 9 | 10 | public SelfSearchGridView(Context context) { 11 | super(context); 12 | } 13 | 14 | public SelfSearchGridView(Context context, AttributeSet attrs) { 15 | super(context, attrs); 16 | } 17 | 18 | 19 | public SelfSearchGridView(Context context, AttributeSet attrs, int defStyleAttr) { 20 | super(context, attrs, defStyleAttr); 21 | } 22 | 23 | @Override 24 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 25 | 26 | int heightSpec; 27 | if(getLayoutParams().height== ViewGroup.LayoutParams.WRAP_CONTENT) 28 | { 29 | heightSpec = MeasureSpec.makeMeasureSpec( 30 | Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); 31 | //Integer.MAX_VALUE >> 2 == 2的31次方-1 表示的int的最大值 32 | } 33 | else { 34 | // Any other height should be respected as is. 35 | heightSpec = heightMeasureSpec; 36 | } 37 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /library/src/main/java/com/tangguna/searchbox/library/widget/SelfSearchListView.java: -------------------------------------------------------------------------------- 1 | package com.tangguna.searchbox.library.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.ViewGroup; 6 | import android.widget.ListView; 7 | 8 | public class SelfSearchListView extends ListView { 9 | 10 | public SelfSearchListView(Context context) { 11 | super(context); 12 | } 13 | 14 | public SelfSearchListView(Context context, AttributeSet attrs) { 15 | super(context, attrs); 16 | } 17 | 18 | public SelfSearchListView(Context context, AttributeSet attrs, int defStyleAttr) { 19 | super(context, attrs, defStyleAttr); 20 | } 21 | 22 | @Override 23 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 24 | 25 | int heightSpec; 26 | if(getLayoutParams().height== ViewGroup.LayoutParams.WRAP_CONTENT) 27 | { 28 | heightSpec = MeasureSpec.makeMeasureSpec( 29 | Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); 30 | //Integer.MAX_VALUE >> 2 == 2的31次方-1 表示的int的最大值 31 | } 32 | else { 33 | // Any other height should be respected as is. 34 | heightSpec = heightMeasureSpec; 35 | } 36 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/explore_search_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/library/src/main/res/drawable-xhdpi/explore_search_close.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/explore_search_close_nt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/library/src/main/res/drawable-xhdpi/explore_search_close_nt.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/explore_search_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/library/src/main/res/drawable-xhdpi/explore_search_icon.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/explore_search_icon_nt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/library/src/main/res/drawable-xhdpi/explore_search_icon_nt.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/icon_clear_data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/library/src/main/res/drawable-xhdpi/icon_clear_data.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/icon_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangguna/SearchBox/42d5a5a93115841e7f9f79e78ba0f6b5bba283e9/library/src/main/res/drawable-xhdpi/icon_close.png -------------------------------------------------------------------------------- /library/src/main/res/drawable/bg_shadow_set_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/ll_clear_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/search.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | 11 | 12 | 16 | 17 | 18 | 19 | 20 | 21 | 23 | 24 | 25 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/search_baground_shap.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/search_title_baground_shap.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 25 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/search_title_baground_shap2.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 19 | 20 | 21 | 22 | 23 | 34 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/sousuo_back_or_search_button_shap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | 11 | 12 | 16 | 17 | 18 | 19 | 20 | 21 | 23 | 24 | 25 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/sousuo_bg_gray_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/sousuo_bg_gray_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/sousuo_bg_gray_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/sousuo_bg_gray_4.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/sousuo_bg_gray_5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/sousuo_bg_gray_state_pressed_shap.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/sousuo_bg_level.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/sousuo_clearolddata_shap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /library/src/main/res/layout/list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 18 | 19 | -------------------------------------------------------------------------------- /library/src/main/res/layout/msearch_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 15 | 16 | 28 | 38 | 39 | 40 |