├── .gitignore ├── .idea ├── caches │ └── build_file_checksums.ser ├── codeStyles │ └── Project.xml ├── gradle.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── libs │ └── pinyin4j-2.5.0.jar ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── city_db_53.db │ ├── java │ │ └── com │ │ │ └── address │ │ │ └── xiaolei │ │ │ └── addressselect │ │ │ ├── MainActivity.java │ │ │ ├── adapter │ │ │ └── PickCityAdapter.java │ │ │ ├── sqlutil │ │ │ └── DBManager.java │ │ │ ├── utils │ │ │ ├── FileUtil.java │ │ │ ├── PinyinUtil.java │ │ │ ├── RxUtils.java │ │ │ └── UiUtils.java │ │ │ ├── view │ │ │ ├── FloatingItemDecoration.java │ │ │ └── SlideBar.java │ │ │ └── vo │ │ │ └── CityVo.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable-xhdpi │ │ ├── ic_clear_gray.png │ │ └── ic_search_small_2.png │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── item_city.xml │ │ └── layout_city_header.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 │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── strings.xml │ │ ├── styles.xml │ │ └── values.xml │ └── test │ └── java │ └── com │ └── address │ └── xiaolei │ └── addressselect │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── img ├── GIF.gif ├── device-2019-01-17-185749.png ├── device-2019-01-17-185822.png └── sql.png └── 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 | 11 | .externalNativeBuild 12 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/.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 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AddressSelect 2 | 在一些项目中经常会用到把一个列表的数据源按照一定的顺序分组排序例如联系人,日期,地址选择等。 3 | 效果如下图所示 4 | 5 | # Example 6 | 9 | 10 | # Uasage 11 | 12 | ## 从数据库读取数据源 13 | ``` 14 | Observable.just(dbManager).map(new Function() { 15 | @Override 16 | public Object apply(DBManager dbManager) throws Exception { 17 | sqLiteDatabase = dbManager.initDataBase(getPackageName()); 18 | String[] columns = new String[]//列属性 19 | {"cityType", "cityID", "cityName", "parentId"}; 20 | String selection = "cityType=?"; 21 | String[] selectionArgs = new String[]{"3"};//type为3时查询的是市 22 | List cityVoList = dbManager.query(sqLiteDatabase, columns, selection, selectionArgs); 23 | sqLiteDatabase.close(); 24 | return cityVoList; 25 | } 26 | }).compose(RxUtils.schedulersTransformer()).subscribe(new Consumer>() { 27 | @Override 28 | public void accept(List cityVoList) throws Exception { 29 | doData(cityVoList); 30 | } 31 | }); 32 | ``` 33 | ## 把数据源进行排序 34 | ``` 35 | Observable.fromIterable(cityVoList). 36 | toSortedList(new Comparator() { 37 | @Override 38 | public int compare(CityVo o1, CityVo o2) { 39 | //a-z排序 40 | String a = o1.getPinYin(); 41 | String b = o2.getPinYin(); 42 | return a.compareTo(b); 43 | } 44 | }).subscribe(new Consumer>() { 45 | @Override 46 | public void accept(List cityVoList) throws Exception { 47 | pickCityAdapter.addHeaderView(headerView); 48 | pickCityAdapter.setNewData(cityVoList); 49 | //添加了头部,所以keys要从1开始 50 | keys.put(1, "A"); 51 | letterIndexes.put("#", 0); 52 | letterIndexes.put("A", 1); 53 | for (int i = 0; i < cityVoList.size(); i++) { 54 | if (i < cityVoList.size() - 1) { 55 | //首字母不同,设为ky 56 | String pre = cityVoList.get(i).getPinYin().substring(0, 1).toUpperCase(); 57 | String next = cityVoList.get(i + 1).getPinYin().substring(0, 1).toUpperCase(); 58 | if (!pre.equals(next)) { 59 | keys.put(i + 2, next); 60 | letterIndexes.put(next, i + 2); 61 | } 62 | } 63 | } 64 | floatingItemDecoration.setKeys(keys); 65 | } 66 | }); 67 | ``` 68 | ## 项目数据源为本地数据库读取,数据库结构如下(也可从网络动态获取) 69 | 71 | 72 | 73 | ### 备注: 这里用到了一个把汉字转拼音的jar,pinyin4j-2.5.0.jar https://github.com/sky8650/AddressSelect/blob/master/app/libs/pinyin4j-2.5.0.jar 74 | 75 | #### 如果您感觉此项目对您有帮助,请点个star哦… 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.address.xiaolei.addressselect" 7 | minSdkVersion 15 8 | targetSdkVersion 28 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:28.0.0' 24 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 25 | implementation 'io.reactivex.rxjava2:rxjava:2.1.0' 26 | implementation 'io.reactivex.rxjava2:rxandroid:2.0.1' 27 | implementation 'com.android.support:recyclerview-v7:28.0.0' 28 | implementation 'com.trello.rxlifecycle2:rxlifecycle-components:2.0.1' 29 | implementation 'com.jakewharton:butterknife:8.5.1' 30 | api 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30' 31 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1' 32 | implementation 'com.google.code.gson:gson:2.8.5' 33 | } 34 | -------------------------------------------------------------------------------- /app/libs/pinyin4j-2.5.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/app/libs/pinyin4j-2.5.0.jar -------------------------------------------------------------------------------- /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/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/assets/city_db_53.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/app/src/main/assets/city_db_53.db -------------------------------------------------------------------------------- /app/src/main/java/com/address/xiaolei/addressselect/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.address.xiaolei.addressselect; 2 | 3 | import android.database.sqlite.SQLiteDatabase; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.support.v7.widget.GridLayoutManager; 7 | import android.support.v7.widget.LinearLayoutManager; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.support.v7.widget.SearchView; 10 | import android.util.Log; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.RelativeLayout; 14 | import android.widget.Toast; 15 | 16 | import com.address.xiaolei.addressselect.adapter.PickCityAdapter; 17 | import com.address.xiaolei.addressselect.sqlutil.DBManager; 18 | import com.address.xiaolei.addressselect.utils.PinyinUtil; 19 | import com.address.xiaolei.addressselect.utils.RxUtils; 20 | import com.address.xiaolei.addressselect.utils.UiUtils; 21 | import com.address.xiaolei.addressselect.view.FloatingItemDecoration; 22 | import com.address.xiaolei.addressselect.view.SlideBar; 23 | import com.address.xiaolei.addressselect.vo.CityVo; 24 | import com.chad.library.adapter.base.BaseQuickAdapter; 25 | import com.google.gson.Gson; 26 | import com.google.gson.reflect.TypeToken; 27 | 28 | import java.lang.reflect.Type; 29 | import java.util.Comparator; 30 | import java.util.HashMap; 31 | import java.util.List; 32 | 33 | import butterknife.BindView; 34 | import butterknife.ButterKnife; 35 | import io.reactivex.Observable; 36 | import io.reactivex.functions.Consumer; 37 | import io.reactivex.functions.Function; 38 | import io.reactivex.functions.Predicate; 39 | 40 | /** 41 | * xiaolei 42 | */ 43 | public class MainActivity extends AppCompatActivity { 44 | DBManager dbManager; 45 | SQLiteDatabase sqLiteDatabase; 46 | @BindView(R.id.et_city_name) 47 | SearchView etCityName; 48 | @BindView(R.id.ll_search) 49 | RelativeLayout llSearch; 50 | @BindView(R.id.rv_city) 51 | RecyclerView rvCity; 52 | @BindView(R.id.slideBar) 53 | SlideBar slideBar; 54 | 55 | private List cityVoList; 56 | private View headerView; 57 | private PickCityAdapter pickCityAdapter; 58 | //悬浮itemDecoration 59 | private FloatingItemDecoration floatingItemDecoration; 60 | private HashMap keys; 61 | private HashMap letterIndexes = new HashMap<>(); 62 | private LinearLayoutManager llm; 63 | 64 | @Override 65 | protected void onCreate(Bundle savedInstanceState) { 66 | super.onCreate(savedInstanceState); 67 | setContentView(R.layout.activity_main); 68 | ButterKnife.bind(this); 69 | initView(); 70 | initSearch(); 71 | initDb(); 72 | } 73 | 74 | 75 | /** 76 | * 初始化视图 77 | */ 78 | private void initView() { 79 | pickCityAdapter = new PickCityAdapter(); 80 | rvCity.setLayoutManager(new LinearLayoutManager(this)); 81 | rvCity.setAdapter(pickCityAdapter); 82 | //头部视图 83 | headerView = getLayoutInflater().inflate(R.layout.layout_city_header, (ViewGroup) rvCity.getParent(), 84 | false); 85 | RecyclerView rvHotCity = (RecyclerView) headerView.findViewById(R.id.rv_hot_city); 86 | rvHotCity.setLayoutManager(new GridLayoutManager(this, 3)); 87 | PickCityAdapter hotCityAdapter = new PickCityAdapter(); 88 | hotCityAdapter.setNewData(generateHotCity()); 89 | rvHotCity.setAdapter(hotCityAdapter); 90 | //分割线 91 | floatingItemDecoration = new FloatingItemDecoration(this, 92 | this.getResources().getColor(R.color.divider_normal), 100, 1); 93 | floatingItemDecoration.setmTitleHeight(UiUtils.dp2px(this, 27)); 94 | floatingItemDecoration.setShowFloatingHeaderOnScrolling(true);//悬浮 95 | rvCity.addItemDecoration(floatingItemDecoration); 96 | llm = new LinearLayoutManager(this); 97 | rvCity.setLayoutManager(llm); 98 | 99 | //右侧滑动选择 100 | slideBar.setOnTouchingLetterChangedListener(new SlideBar.OnTouchingLetterChangedListener() { 101 | @Override 102 | public void onTouchingLetterChanged(String s, int offset) { 103 | int position = letterIndexes.get(s) == null ? -1 : letterIndexes.get(s); 104 | llm.scrollToPositionWithOffset(position, offset); 105 | } 106 | }); 107 | //列点击事件 108 | pickCityAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() { 109 | @Override 110 | public void onItemClick(BaseQuickAdapter adapter, View view, int position) { 111 | Toast.makeText(MainActivity.this,pickCityAdapter.getItem(position).getCityName(), 112 | Toast.LENGTH_LONG).show(); 113 | } 114 | }); 115 | } 116 | 117 | 118 | /** 119 | * 初始化搜索 120 | */ 121 | 122 | private void initSearch(){ 123 | etCityName.setIconified(false);//设置searchView处于展开状态 124 | etCityName.onActionViewExpanded();// 当展开无输入内容的时候,没有关闭的图标 125 | etCityName.setOnQueryTextListener(new SearchView.OnQueryTextListener() { 126 | @Override 127 | public boolean onQueryTextSubmit(String s) { 128 | searchData(s); 129 | return false; 130 | } 131 | 132 | @Override 133 | public boolean onQueryTextChange(String s) { 134 | slideBar.setVisibility(s.length() > 0 ? View.GONE : View.VISIBLE); 135 | if (s.length() > 0) { 136 | searchData(s); 137 | } else { 138 | doData(cityVoList); 139 | } 140 | return true; 141 | } 142 | }); 143 | 144 | } 145 | 146 | 147 | 148 | /** 149 | * 初始化数据库 150 | */ 151 | private void initDb() { 152 | dbManager = new DBManager(MainActivity.this); 153 | Observable.just(dbManager).map(new Function() { 154 | @Override 155 | public Object apply(DBManager dbManager) throws Exception { 156 | sqLiteDatabase = dbManager.initDataBase(getPackageName()); 157 | String[] columns = new String[]//列属性 158 | {"cityType", "cityID", "cityName", "parentId"}; 159 | String selection = "cityType=?"; 160 | String[] selectionArgs = new String[]{"3"};//type为3时查询的是市 161 | cityVoList = dbManager.query(sqLiteDatabase, columns, selection, selectionArgs); 162 | sqLiteDatabase.close(); 163 | return cityVoList; 164 | } 165 | }).compose(RxUtils.schedulersTransformer()).subscribe(new Consumer>() { 166 | @Override 167 | public void accept(List cityVoList) throws Exception { 168 | doData(cityVoList); 169 | } 170 | }); 171 | } 172 | 173 | 174 | /** 175 | *数据排序 176 | */ 177 | private void doData(ListcityVoList){ 178 | Observable.just(cityVoList).doOnNext(new Consumer>() { 179 | @Override 180 | public void accept(List cityVoList) throws Exception { 181 | setPingyin(cityVoList); 182 | } 183 | }).doOnNext(new Consumer>() { 184 | @Override 185 | public void accept(List cityVoList) throws Exception { 186 | sortData(cityVoList); 187 | } 188 | }).subscribe(new Consumer>() { 189 | @Override 190 | public void accept(List cityVoList) throws Exception { 191 | Log.d("aaaaa","aaaa"); 192 | } 193 | }); 194 | 195 | } 196 | 197 | 198 | /** 199 | * 处理拼音 200 | */ 201 | private List setPingyin(final ListcityVoList){ 202 | for (CityVo cityVo :cityVoList){ 203 | cityVo.setPinYin(PinyinUtil.getPingYin(cityVo.getCityName())); 204 | } 205 | return cityVoList; 206 | } 207 | 208 | /** 209 | * 排序 210 | */ 211 | private void sortData(final ListcityVoList){ 212 | keys = new HashMap<>(); 213 | Observable.fromIterable(cityVoList). 214 | toSortedList(new Comparator() { 215 | @Override 216 | public int compare(CityVo o1, CityVo o2) { 217 | //a-z排序 218 | String a = o1.getPinYin(); 219 | String b = o2.getPinYin(); 220 | return a.compareTo(b); 221 | } 222 | }).subscribe(new Consumer>() { 223 | @Override 224 | public void accept(List cityVoList) throws Exception { 225 | pickCityAdapter.addHeaderView(headerView); 226 | pickCityAdapter.setNewData(cityVoList); 227 | //添加了头部,所以keys要从1开始 228 | keys.put(1, "A"); 229 | letterIndexes.put("#", 0); 230 | letterIndexes.put("A", 1); 231 | for (int i = 0; i < cityVoList.size(); i++) { 232 | if (i < cityVoList.size() - 1) { 233 | //首字母不同,设为ky 234 | String pre = cityVoList.get(i).getPinYin().substring(0, 1).toUpperCase(); 235 | String next = cityVoList.get(i + 1).getPinYin().substring(0, 1).toUpperCase(); 236 | if (!pre.equals(next)) { 237 | keys.put(i + 2, next); 238 | letterIndexes.put(next, i + 2); 239 | } 240 | } 241 | } 242 | floatingItemDecoration.setKeys(keys); 243 | } 244 | }); 245 | } 246 | 247 | /** 248 | * 搜索数据 249 | */ 250 | private void searchData(final String searchKey){ 251 | Observable.fromIterable(cityVoList).filter(new Predicate() { 252 | @Override 253 | public boolean test(CityVo cityVo) throws Exception { 254 | if (cityVo.getPinYin().contains(searchKey.trim())|| 255 | cityVo.getCityName().contains(searchKey.trim())){ 256 | return true; 257 | } 258 | return false; 259 | } 260 | }).compose(RxUtils.schedulersTransformer()). 261 | toList(). 262 | subscribe(new Consumer>() { 263 | @Override 264 | public void accept(List cityVoList) throws Exception { 265 | keys = new HashMap(); 266 | keys.put(0, searchKey.trim().toUpperCase()); 267 | pickCityAdapter.removeAllHeaderView(); 268 | pickCityAdapter.setNewData(cityVoList); 269 | floatingItemDecoration.setKeys(keys); 270 | 271 | } 272 | }); 273 | } 274 | 275 | 276 | 277 | /** 278 | * 热门城市 279 | * 280 | * @return 281 | */ 282 | private List generateHotCity() { 283 | Gson gson = new Gson(); 284 | Type type = new TypeToken>(){}.getType(); 285 | String jsonString=this.getResources().getString(R.string.hot_city_json); 286 | ListcityVoList = gson.fromJson(jsonString,type); 287 | return cityVoList; 288 | } 289 | 290 | 291 | } 292 | -------------------------------------------------------------------------------- /app/src/main/java/com/address/xiaolei/addressselect/adapter/PickCityAdapter.java: -------------------------------------------------------------------------------- 1 | package com.address.xiaolei.addressselect.adapter; 2 | 3 | import android.view.View; 4 | 5 | import com.address.xiaolei.addressselect.R; 6 | import com.address.xiaolei.addressselect.vo.CityVo; 7 | import com.chad.library.adapter.base.BaseQuickAdapter; 8 | import com.chad.library.adapter.base.BaseViewHolder; 9 | 10 | 11 | /** 12 | * Created by Cicinnus on 2017/6/8. 13 | */ 14 | 15 | public class PickCityAdapter extends BaseQuickAdapter { 16 | 17 | 18 | private OnCityClickListener onCityClickListener; 19 | 20 | public PickCityAdapter() { 21 | super(R.layout.item_city); 22 | } 23 | @Override 24 | protected void convert(BaseViewHolder helper, final CityVo item) { 25 | helper.setText(R.id.tv_city_name, item.getCityName()); 26 | } 27 | 28 | 29 | public interface OnCityClickListener{ 30 | void onClick(CityVo item); 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/address/xiaolei/addressselect/sqlutil/DBManager.java: -------------------------------------------------------------------------------- 1 | package com.address.xiaolei.addressselect.sqlutil; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | import android.database.sqlite.SQLiteDatabase; 6 | import android.util.Log; 7 | 8 | import com.address.xiaolei.addressselect.utils.FileUtil; 9 | import com.address.xiaolei.addressselect.vo.CityVo; 10 | 11 | import java.io.File; 12 | import java.io.FileOutputStream; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | /** 19 | * xiaolei 20 | */ 21 | public class DBManager { 22 | private String DB_NAME = "city_db_53.db"; 23 | private Context mContext; 24 | 25 | public DBManager(Context mContext) { 26 | this.mContext = mContext; 27 | } 28 | //把assets目录下的db文件复制到dbpath下 29 | public SQLiteDatabase initDataBase(String packName) { 30 | String dbPath = FileUtil.getCachePath(mContext)+ "/"+packName 31 | + "/databases/" + DB_NAME; 32 | File file=new File(dbPath); 33 | if (!file.exists()) { 34 | try { 35 | // 先得到文件的上级目录,并创建上级目录,在创建文件 36 | boolean falg1= file.getParentFile().mkdirs(); 37 | boolean falg2= file.createNewFile(); 38 | Log.d("*************",falg1+"&&&"+falg2); 39 | FileOutputStream out = new FileOutputStream(file); 40 | InputStream in = mContext.getAssets().open(DB_NAME); 41 | byte[] buffer = new byte[1024]; 42 | int readBytes = 0; 43 | while ((readBytes = in.read(buffer)) != -1) 44 | out.write(buffer, 0, readBytes); 45 | in.close(); 46 | out.close(); 47 | } catch (IOException e) { 48 | e.printStackTrace(); 49 | } 50 | } 51 | return SQLiteDatabase.openOrCreateDatabase(dbPath, null); 52 | } 53 | //查询 54 | public List query(SQLiteDatabase sqliteDB, String[] columns, String selection, String[] selectionArgs) { 55 | CityVo city = null; 56 | ListcityVoList=new ArrayList<>(); 57 | try { 58 | String table = "cityTable";//数据库表名 59 | Cursor cursor = sqliteDB.query(table, columns, selection, selectionArgs, 60 | null, null, null); 61 | while (cursor.moveToNext()) { 62 | String cityType = cursor.getString(cursor.getColumnIndex("cityType")); 63 | String cityId = cursor.getString(cursor.getColumnIndex("cityID")); 64 | String cityName = cursor.getString(cursor.getColumnIndex("cityName")); 65 | String parentId = cursor.getString(cursor.getColumnIndex("parentId")); 66 | city = new CityVo(cityId,cityName, parentId,cityType); 67 | cityVoList.add(city); 68 | } 69 | cursor.close(); 70 | } catch (Exception e) { 71 | e.printStackTrace(); 72 | } 73 | return cityVoList; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/address/xiaolei/addressselect/utils/FileUtil.java: -------------------------------------------------------------------------------- 1 | package com.address.xiaolei.addressselect.utils; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | import android.util.Log; 6 | 7 | import java.io.File; 8 | import java.io.FileInputStream; 9 | import java.text.DecimalFormat; 10 | 11 | 12 | public class FileUtil { 13 | /** 14 | * 获取缓存目录 15 | * @param context 16 | * @return 17 | */ 18 | public static String getCachePath(Context context ){ 19 | String cachePath ; 20 | if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) 21 | || !Environment.isExternalStorageRemovable()) { 22 | //外部存储可用 23 | cachePath = context.getExternalCacheDir().getPath() ; 24 | 25 | }else { 26 | //外部存储不可用 27 | cachePath = context.getCacheDir().getPath() ; 28 | } 29 | return cachePath ; 30 | } 31 | 32 | /** 33 | * 根据文件路径获取文件 34 | * 35 | * @param filePath 文件路径 36 | * @return 文件 37 | */ 38 | public static File getFileByPath(String filePath) { 39 | return isSpace(filePath) ? null : new File(filePath); 40 | } 41 | 42 | 43 | 44 | /** 45 | * 删除目录 46 | * 47 | * @param dir 目录 48 | * @return {@code true}: 删除成功
{@code false}: 删除失败 49 | */ 50 | public static boolean deleteDir(File dir) { 51 | if (dir == null) return false; 52 | // 目录不存在返回true 53 | if (!dir.exists()) return true; 54 | // 不是目录返回false 55 | if (!dir.isDirectory()) return false; 56 | // 现在文件存在且是文件夹 57 | File[] files = dir.listFiles(); 58 | if (files != null && files.length != 0) { 59 | for (File file : files) { 60 | if (file.isFile()) { 61 | if (!deleteFile(file)) return false; 62 | } else if (file.isDirectory()) { 63 | if (!deleteDir(file)) return false; 64 | } 65 | } 66 | } 67 | return dir.delete(); 68 | } 69 | 70 | /** 71 | * 删除文件 72 | * 73 | * @param srcFilePath 文件路径 74 | * @return {@code true}: 删除成功
{@code false}: 删除失败 75 | */ 76 | public static boolean deleteFile(String srcFilePath) { 77 | return deleteFile(getFileByPath(srcFilePath)); 78 | } 79 | 80 | 81 | 82 | /** 83 | * 删除文件 84 | * 85 | * @param file 文件 86 | * @return {@code true}: 删除成功
{@code false}: 删除失败 87 | */ 88 | public static boolean deleteFile(File file) { 89 | return file != null && (!file.exists() || file.isFile() && file.delete()); 90 | } 91 | 92 | 93 | /** 94 | * 获取指定文件大小 95 | * @param 96 | * @return 97 | * @throws Exception    98 | */ 99 | public static long getFileSize(File file) throws Exception { 100 | long size = 0; 101 | if (file.exists()) { 102 | FileInputStream fis = null; 103 | fis = new FileInputStream(file); 104 | size = fis.available(); 105 | } else { 106 | file.createNewFile(); 107 | Log.e("获取文件大小", "文件不存在!"); 108 | } 109 | return size; 110 | } 111 | 112 | /** 113 | * 获取指定文件夹 114 | * @param f 115 | * @return 116 | * @throws Exception 117 | * 118 | */ 119 | public static long getFileSizes(File f) throws Exception { 120 | long size = 0; 121 | File flist[] = f.listFiles(); 122 | for (int i = 0; i < flist.length; i++) { 123 | if (flist[i].isDirectory()) { 124 | size = size + getFileSizes(flist[i]); 125 | } else { 126 | size = size + getFileSize(flist[i]); 127 | } 128 | } 129 | return size; 130 | } 131 | 132 | public static String FormatFileSize(long fileS) { 133 | DecimalFormat df = new DecimalFormat("#.00"); 134 | String fileSizeString = ""; 135 | String wrongSize = "0B"; 136 | if (fileS == 0) { 137 | return wrongSize; 138 | } 139 | if (fileS < 1024) { 140 | fileSizeString = df.format((double) fileS) + "B"; 141 | } else if (fileS < 1048576) { 142 | fileSizeString = df.format((double) fileS / 1024) + "KB"; 143 | } else if (fileS < 1073741824) { 144 | fileSizeString = df.format((double) fileS / 1048576) + "MB"; 145 | } else { 146 | fileSizeString = df.format((double) fileS / 1073741824) + "GB"; 147 | } 148 | return fileSizeString; 149 | } 150 | 151 | private static boolean isSpace(String s) { 152 | if (s == null) return true; 153 | for (int i = 0, len = s.length(); i < len; ++i) { 154 | if (!Character.isWhitespace(s.charAt(i))) { 155 | return false; 156 | } 157 | } 158 | return true; 159 | } 160 | 161 | } 162 | -------------------------------------------------------------------------------- /app/src/main/java/com/address/xiaolei/addressselect/utils/PinyinUtil.java: -------------------------------------------------------------------------------- 1 | package com.address.xiaolei.addressselect.utils; 2 | 3 | import net.sourceforge.pinyin4j.PinyinHelper; 4 | import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType; 5 | import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat; 6 | import net.sourceforge.pinyin4j.format.HanyuPinyinToneType; 7 | import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType; 8 | import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination; 9 | 10 | /** 11 | * author : XiaoLei 12 | * date : 2019/1/161337 13 | * desc : 14 | */ 15 | public class PinyinUtil { 16 | /** 17 | * 将字符串中的中文转化为拼音,其他字符不变 18 | * 19 | * @param inputString 20 | * @return 21 | */ 22 | public static String getPingYin(String inputString) { 23 | HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat(); 24 | format.setCaseType(HanyuPinyinCaseType.LOWERCASE); 25 | format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); 26 | format.setVCharType(HanyuPinyinVCharType.WITH_V); 27 | 28 | char[] input = inputString.trim().toCharArray(); 29 | String output = ""; 30 | 31 | try { 32 | for (int i = 0; i < input.length; i++) { 33 | if (java.lang.Character.toString(input[i]).matches("[\\u4E00-\\u9FA5]+")) { 34 | String[] temp = PinyinHelper.toHanyuPinyinStringArray(input[i], format); 35 | output += temp[0]; 36 | } else 37 | output += java.lang.Character.toString(input[i]); 38 | } 39 | } catch (BadHanyuPinyinOutputFormatCombination e) { 40 | e.printStackTrace(); 41 | } 42 | return output; 43 | } 44 | /** 45 | * 获取汉字串拼音首字母,英文字符不变 46 | * @param chinese 汉字串 47 | * @return 汉语拼音首字母 48 | */ 49 | public static String getFirstSpell(String chinese) { 50 | StringBuffer pybf = new StringBuffer(); 51 | char[] arr = chinese.toCharArray(); 52 | HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat(); 53 | defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE); 54 | defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE); 55 | for (int i = 0; i < arr.length; i++) { 56 | if (arr[i] > 128) { 57 | try { 58 | String[] temp = PinyinHelper.toHanyuPinyinStringArray(arr[i], defaultFormat); 59 | if (temp != null) { 60 | pybf.append(temp[0].charAt(0)); 61 | } 62 | } catch (BadHanyuPinyinOutputFormatCombination e) { 63 | e.printStackTrace(); 64 | } 65 | } else { 66 | pybf.append(arr[i]); 67 | } 68 | } 69 | return pybf.toString().replaceAll("\\W", "").trim(); 70 | } 71 | /** 72 | * 获取汉字串拼音,英文字符不变 73 | * @param chinese 汉字串 74 | * @return 汉语拼音 75 | */ 76 | public static String getFullSpell(String chinese) { 77 | StringBuffer pybf = new StringBuffer(); 78 | char[] arr = chinese.toCharArray(); 79 | HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat(); 80 | defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE); 81 | defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE); 82 | for (int i = 0; i < arr.length; i++) { 83 | if (arr[i] > 128) { 84 | try { 85 | pybf.append(PinyinHelper.toHanyuPinyinStringArray(arr[i], defaultFormat)[0]); 86 | } catch (BadHanyuPinyinOutputFormatCombination e) { 87 | e.printStackTrace(); 88 | } 89 | } else { 90 | pybf.append(arr[i]); 91 | } 92 | } 93 | return pybf.toString(); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /app/src/main/java/com/address/xiaolei/addressselect/utils/RxUtils.java: -------------------------------------------------------------------------------- 1 | package com.address.xiaolei.addressselect.utils; 2 | 3 | import android.content.Context; 4 | import android.support.v4.app.Fragment; 5 | 6 | import com.trello.rxlifecycle2.LifecycleProvider; 7 | import com.trello.rxlifecycle2.LifecycleTransformer; 8 | 9 | import io.reactivex.Observable; 10 | import io.reactivex.ObservableSource; 11 | import io.reactivex.ObservableTransformer; 12 | import io.reactivex.android.schedulers.AndroidSchedulers; 13 | import io.reactivex.annotations.NonNull; 14 | import io.reactivex.functions.Function; 15 | import io.reactivex.schedulers.Schedulers; 16 | 17 | /** 18 | * author : XiaoLei 19 | * date : 2019/1/1113:33 20 | * desc :有关Rx的工具类 21 | */ 22 | public class RxUtils { 23 | /** 24 | * 生命周期绑定 25 | * 26 | * @param lifecycle Activity 27 | */ 28 | public static LifecycleTransformer bindToLifecycle(@NonNull Context lifecycle) { 29 | if (lifecycle instanceof LifecycleProvider) { 30 | return ((LifecycleProvider) lifecycle).bindToLifecycle(); 31 | } else { 32 | throw new IllegalArgumentException("context not the LifecycleProvider type"); 33 | } 34 | } 35 | 36 | /** 37 | * 生命周期绑定 38 | * 39 | * @param lifecycle Fragment 40 | */ 41 | public static LifecycleTransformer bindToLifecycle(@NonNull Fragment lifecycle) { 42 | if (lifecycle instanceof LifecycleProvider) { 43 | return ((LifecycleProvider) lifecycle).bindToLifecycle(); 44 | } else { 45 | throw new IllegalArgumentException("fragment not the LifecycleProvider type"); 46 | } 47 | } 48 | 49 | /** 50 | * 生命周期绑定 51 | * 52 | * @param lifecycle Fragment 53 | */ 54 | public static LifecycleTransformer bindToLifecycle(@NonNull LifecycleProvider lifecycle) { 55 | return lifecycle.bindToLifecycle(); 56 | } 57 | 58 | /** 59 | * 线程调度器 60 | */ 61 | public static ObservableTransformer schedulersTransformer() { 62 | return new ObservableTransformer() { 63 | @Override 64 | public ObservableSource apply(Observable upstream) { 65 | return upstream.subscribeOn(Schedulers.io()) 66 | .observeOn(AndroidSchedulers.mainThread()); 67 | } 68 | }; 69 | } 70 | 71 | 72 | 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/com/address/xiaolei/addressselect/utils/UiUtils.java: -------------------------------------------------------------------------------- 1 | package com.address.xiaolei.addressselect.utils; 2 | 3 | import android.content.Context; 4 | import android.util.DisplayMetrics; 5 | 6 | /** 7 | * Created by Administrator on 2017/1/19. 8 | */ 9 | 10 | public class UiUtils { 11 | 12 | 13 | /** 14 | * 动态获取屏幕的宽度 15 | * @param context context 16 | * @return 17 | */ 18 | public static int getDeviceWidth(Context context){ 19 | DisplayMetrics metrics = context.getResources().getDisplayMetrics(); 20 | return metrics.widthPixels; 21 | } 22 | 23 | /** 24 | * 动态获取屏幕的高度 25 | * @param context context 26 | * @return 27 | */ 28 | public static int getDeviceHeight(Context context){ 29 | DisplayMetrics metrics = context.getResources().getDisplayMetrics(); 30 | return metrics.heightPixels; 31 | } 32 | 33 | /** 34 | * 将dp转为px 35 | * @param context context 36 | * @param dpValue dp值 37 | * @return 38 | */ 39 | public static int dp2px(Context context, float dpValue){ 40 | float scale = context.getResources().getDisplayMetrics().density; 41 | return (int)(dpValue * scale +0.5f); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/address/xiaolei/addressselect/view/FloatingItemDecoration.java: -------------------------------------------------------------------------------- 1 | package com.address.xiaolei.addressselect.view; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.Paint; 8 | import android.graphics.Rect; 9 | import android.graphics.drawable.ColorDrawable; 10 | import android.graphics.drawable.Drawable; 11 | import android.support.annotation.ColorInt; 12 | import android.support.annotation.Dimension; 13 | import android.support.annotation.DrawableRes; 14 | import android.support.v4.content.ContextCompat; 15 | import android.support.v7.widget.LinearLayoutManager; 16 | import android.support.v7.widget.RecyclerView; 17 | import android.text.TextUtils; 18 | import android.util.TypedValue; 19 | import android.view.View; 20 | 21 | import com.address.xiaolei.addressselect.R; 22 | 23 | import java.util.HashMap; 24 | import java.util.Map; 25 | 26 | /** 27 | * 自定义分割线 28 | */ 29 | 30 | public class FloatingItemDecoration extends RecyclerView.ItemDecoration { 31 | private static final String TAG = "FloatingItemDecoration"; 32 | private static final int[] ATTRS = new int[]{android.R.attr.listDivider}; 33 | private Drawable mDivider; 34 | private int dividerHeight; 35 | private int dividerWidth; 36 | private Map keys=new HashMap<>(); 37 | private int mTitleHeight; 38 | private Paint mTextPaint; 39 | private Paint mBackgroundPaint; 40 | private float mTextHeight; 41 | private float mTextBaselineOffset; 42 | private Context mContext; 43 | /** 44 | * 滚动列表的时候是否一直显示悬浮头部 45 | */ 46 | private boolean showFloatingHeaderOnScrolling=true; 47 | 48 | public FloatingItemDecoration(Context context) { 49 | final TypedArray a = context.obtainStyledAttributes(ATTRS); 50 | mDivider = a.getDrawable(0); 51 | a.recycle(); 52 | this.dividerHeight=mDivider.getIntrinsicHeight(); 53 | this.dividerWidth=mDivider.getIntrinsicWidth(); 54 | init(context); 55 | } 56 | 57 | /** 58 | * 自定义分割线 59 | * 60 | * @param context 61 | * @param drawableId 分割线图片 62 | */ 63 | public FloatingItemDecoration(Context context, @DrawableRes int drawableId) { 64 | mDivider = ContextCompat.getDrawable(context, drawableId); 65 | this.dividerHeight=mDivider.getIntrinsicHeight(); 66 | this.dividerWidth=mDivider.getIntrinsicWidth(); 67 | init(context); 68 | } 69 | 70 | /** 71 | * 自定义分割线 72 | * 也可以使用{@link Canvas#drawRect(float, float, float, float, Paint)}或者{@link Canvas#drawText(String, float, float, Paint)}等等 73 | * 结合{@link Paint}去绘制各式各样的分割线 74 | * @param context 75 | * @param color 整型颜色值,非资源id 76 | * @param dividerWidth 单位为dp 77 | * @param dividerHeight 单位为dp 78 | */ 79 | public FloatingItemDecoration(Context context, @ColorInt int color, @Dimension float dividerWidth, @Dimension float dividerHeight) { 80 | mDivider = new ColorDrawable(color); 81 | this.dividerWidth= (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dividerWidth,context.getResources().getDisplayMetrics()); 82 | this.dividerHeight= (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dividerHeight,context.getResources().getDisplayMetrics()); 83 | init(context); 84 | } 85 | 86 | private void init(Context mContext){ 87 | this.mContext=mContext; 88 | mTextPaint=new Paint(); 89 | mTextPaint.setAntiAlias(true); 90 | mTextPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,12,mContext.getResources().getDisplayMetrics())); 91 | mTextPaint.setColor(Color.BLACK); 92 | Paint.FontMetrics fm=mTextPaint.getFontMetrics(); 93 | mTextHeight=fm.bottom-fm.top;//计算文字高度 94 | mTextBaselineOffset=fm.bottom; 95 | 96 | mBackgroundPaint=new Paint(); 97 | mBackgroundPaint.setAntiAlias(true); 98 | mBackgroundPaint.setColor(mContext.getResources().getColor(R.color.divider_normal)); 99 | } 100 | 101 | @Override 102 | public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { 103 | super.onDraw(c, parent, state); 104 | drawVertical(c,parent); 105 | } 106 | 107 | @Override 108 | public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { 109 | super.onDrawOver(c, parent, state); 110 | if(!showFloatingHeaderOnScrolling){ 111 | return; 112 | } 113 | int firstVisiblePos=((LinearLayoutManager)parent.getLayoutManager()).findFirstVisibleItemPosition(); 114 | if(firstVisiblePos== RecyclerView.NO_POSITION){ 115 | return; 116 | } 117 | String title=getTitle(firstVisiblePos); 118 | if(TextUtils.isEmpty(title)){ 119 | return; 120 | } 121 | boolean flag=false; 122 | if(getTitle(firstVisiblePos+1)!=null&&!title.equals(getTitle(firstVisiblePos+1))){ 123 | //说明是当前组最后一个元素,但不一定碰撞了 124 | // Log.e(TAG, "onDrawOver: "+"==============" +firstVisiblePos); 125 | View child=parent.findViewHolderForAdapterPosition(firstVisiblePos).itemView; 126 | if(child.getTop()+child.getMeasuredHeight()= 0) { 166 | if (keys.containsKey(position)) { 167 | return keys.get(position); 168 | } 169 | position--; 170 | } 171 | return null; 172 | } 173 | 174 | private void drawVertical(Canvas c, RecyclerView parent){ 175 | int left = parent.getPaddingLeft(); 176 | int right = parent.getWidth() - parent.getPaddingRight(); 177 | int top=0; 178 | int bottom=0; 179 | for (int i = 0; i < parent.getChildCount(); i++) { 180 | View child=parent.getChildAt(i); 181 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); 182 | if(!keys.containsKey(params.getViewAdapterPosition())){ 183 | //画普通分割线 184 | top=child.getTop()-params.topMargin-dividerHeight; 185 | bottom=top+dividerHeight; 186 | mDivider.setBounds(left, top, right, bottom); 187 | mDivider.draw(c); 188 | }else{ 189 | //画分组 190 | top=child.getTop()-params.topMargin-mTitleHeight; 191 | bottom=top+mTitleHeight; 192 | c.drawRect(left,top,right,bottom,mBackgroundPaint); 193 | // float x=child.getPaddingLeft()+params.leftMargin; 194 | float x= TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,10,mContext.getResources().getDisplayMetrics()); 195 | float y=bottom - (mTitleHeight - mTextHeight) / 2 - mTextBaselineOffset;//计算文字baseLine 196 | // Log.e(TAG, "drawVertical: "+bottom ); 197 | c.drawText(keys.get(params.getViewLayoutPosition()),x,y,mTextPaint); 198 | } 199 | } 200 | } 201 | 202 | public void setShowFloatingHeaderOnScrolling(boolean showFloatingHeaderOnScrolling) { 203 | this.showFloatingHeaderOnScrolling = showFloatingHeaderOnScrolling; 204 | } 205 | 206 | public void setKeys(Map keys){ 207 | this.keys.clear(); 208 | this.keys.putAll(keys); 209 | } 210 | 211 | public void setmTitleHeight(int titleHeight){ 212 | this.mTitleHeight=titleHeight; 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /app/src/main/java/com/address/xiaolei/addressselect/view/SlideBar.java: -------------------------------------------------------------------------------- 1 | package com.address.xiaolei.addressselect.view; 2 | 3 | /** 4 | * Created by xiaolei on 2017/6/8. 5 | */ 6 | 7 | import android.content.Context; 8 | import android.graphics.Canvas; 9 | import android.graphics.Color; 10 | import android.graphics.Paint; 11 | import android.graphics.RectF; 12 | import android.graphics.Typeface; 13 | import android.support.v4.view.MotionEventCompat; 14 | import android.util.AttributeSet; 15 | import android.view.MotionEvent; 16 | import android.view.View; 17 | import android.view.ViewConfiguration; 18 | 19 | 20 | import com.address.xiaolei.addressselect.R; 21 | 22 | import static android.support.v4.widget.ViewDragHelper.INVALID_POINTER; 23 | 24 | /** 25 | * 根据该项目进行了小修改,可以在滑动的同时进行监听 26 | * https://github.com/kongnanlive/SideBar 27 | */ 28 | 29 | public class SlideBar extends View { 30 | private static final String TAG = SlideBar.class.getSimpleName(); 31 | 32 | public interface OnTouchingLetterChangedListener { 33 | void onTouchingLetterChanged(String s, int offset); 34 | } 35 | 36 | private OnTouchingLetterChangedListener mOnTouchingLetterChangedListener; 37 | 38 | private String[] mLetters = null; 39 | private Paint mPaint; 40 | 41 | private int mChoose = -1; 42 | 43 | private final float mDensity; 44 | private float mY; 45 | private float mHalfWidth, mHalfHeight; 46 | private float mLetterHeight; 47 | private float mAnimStep; 48 | 49 | private int mTouchSlop; 50 | private float mInitialDownY; 51 | private boolean mIsBeingDragged, mStartEndAnim; 52 | private int mActivePointerId = INVALID_POINTER; 53 | 54 | private RectF mIsDownRect = new RectF(); 55 | 56 | public SlideBar(Context context) { 57 | this(context, null); 58 | } 59 | 60 | public SlideBar(Context context, AttributeSet attrs) { 61 | this(context, attrs, 0); 62 | } 63 | 64 | public SlideBar(Context context, AttributeSet attrs, int defStyleAttr) { 65 | super(context, attrs, defStyleAttr); 66 | this.mPaint = new Paint(); 67 | int mTextColor = Color.GRAY; 68 | this.mPaint.setAntiAlias(true); 69 | this.mPaint.setTextAlign(Paint.Align.CENTER); 70 | this.mPaint.setColor(mTextColor); 71 | 72 | int mResArrayId = R.array.letter_list; 73 | this.mLetters = context.getResources().getStringArray(mResArrayId); 74 | 75 | mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 76 | mDensity = getContext().getResources().getDisplayMetrics().density; 77 | 78 | setPadding(0, 0, 0, dip2px(20)); 79 | } 80 | 81 | public void setOnTouchingLetterChangedListener(OnTouchingLetterChangedListener listener) { 82 | this.mOnTouchingLetterChangedListener = listener; 83 | } 84 | 85 | private int getLettersSize() { 86 | return mLetters.length; 87 | } 88 | 89 | @Override 90 | public boolean onTouchEvent(MotionEvent ev) { 91 | final int action = MotionEventCompat.getActionMasked(ev); 92 | switch (action) { 93 | case MotionEvent.ACTION_DOWN: 94 | mActivePointerId = MotionEventCompat.getPointerId(ev, 0); 95 | mIsBeingDragged = false; 96 | final float initialDownY = getMotionEventY(ev, mActivePointerId); 97 | if (initialDownY == -1) { 98 | return false; 99 | } 100 | if (!mIsDownRect.contains(ev.getX(), ev.getY())) { 101 | return false; 102 | } 103 | mInitialDownY = initialDownY; 104 | break; 105 | case MotionEvent.ACTION_MOVE: 106 | if (mActivePointerId == INVALID_POINTER) { 107 | return false; 108 | } 109 | 110 | final float y = getMotionEventY(ev, mActivePointerId); 111 | if (y == -1) { 112 | return false; 113 | } 114 | final float yDiff = Math.abs(y - mInitialDownY); 115 | if (yDiff > mTouchSlop && !mIsBeingDragged) { 116 | mIsBeingDragged = true; 117 | } 118 | if (mIsBeingDragged) { 119 | mY = y; 120 | final float moveY = y - getPaddingTop() - mLetterHeight / 1.64f; 121 | final int characterIndex = (int) (moveY / mHalfHeight * mLetters.length); 122 | if (mChoose != characterIndex) { 123 | if (characterIndex >= 0 && characterIndex < mLetters.length) { 124 | mChoose = characterIndex; 125 | } 126 | mOnTouchingLetterChangedListener.onTouchingLetterChanged(mLetters[mChoose],(int)moveY); 127 | } 128 | 129 | invalidate(); 130 | } 131 | break; 132 | case MotionEventCompat.ACTION_POINTER_UP: 133 | onSecondaryPointerUp(ev); 134 | break; 135 | case MotionEvent.ACTION_UP: 136 | case MotionEvent.ACTION_CANCEL: 137 | if (mOnTouchingLetterChangedListener != null) { 138 | if (mIsBeingDragged) { 139 | mOnTouchingLetterChangedListener.onTouchingLetterChanged(mLetters[mChoose],0); 140 | } else { 141 | float downY = ev.getY() - getPaddingTop(); 142 | final int characterIndex = (int) (downY / mHalfHeight * mLetters.length); 143 | if (characterIndex >= 0 && characterIndex < mLetters.length) { 144 | mOnTouchingLetterChangedListener.onTouchingLetterChanged(mLetters[characterIndex],0); 145 | } 146 | } 147 | } 148 | mStartEndAnim = mIsBeingDragged; 149 | mIsBeingDragged = false; 150 | mActivePointerId = INVALID_POINTER; 151 | 152 | mChoose = -1; 153 | mAnimStep = 0f; 154 | invalidate(); 155 | return false; 156 | } 157 | return true; 158 | } 159 | 160 | @Override 161 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 162 | super.onSizeChanged(w, h, oldw, oldh); 163 | mHalfWidth = w - dip2px(16); 164 | mHalfHeight = h - getPaddingTop() - getPaddingBottom(); 165 | 166 | float lettersLen = getLettersSize(); 167 | 168 | mLetterHeight = mHalfHeight / lettersLen; 169 | int textSize = (int) (mHalfHeight * 0.7f / lettersLen); 170 | this.mPaint.setTextSize(textSize); 171 | 172 | mIsDownRect.set(w - dip2px(16 * 2), 0, w, h); 173 | } 174 | 175 | @Override 176 | protected void onDraw(Canvas canvas) { 177 | super.onDraw(canvas); 178 | for (int i = 0; i < getLettersSize(); i++) { 179 | float letterPosY = mLetterHeight * (i + 1) + getPaddingTop(); 180 | float diff, diffY, diffX; 181 | if (mChoose == i && i != 0 && i != getLettersSize() - 1) { 182 | diffX = 0f; 183 | diffY = 0f; 184 | diff = 2.16f; 185 | } else { 186 | float maxPos = Math.abs((mY - letterPosY) / mHalfHeight * 7f); 187 | diff = Math.max(1f, 2.2f - maxPos); 188 | if (mStartEndAnim && diff != 1f) { 189 | diff -= mAnimStep; 190 | if (diff <= 1f) { 191 | diff = 1f; 192 | } 193 | } else if (!mIsBeingDragged) { 194 | diff = 1f; 195 | } 196 | diffY = maxPos * 50f * (letterPosY >= mY ? -1 : 1); 197 | diffX = maxPos * 100f; 198 | } 199 | canvas.save(); 200 | canvas.scale(diff, diff, mHalfWidth * 1.20f + diffX, letterPosY + diffY); 201 | if (diff == 1f) { 202 | this.mPaint.setAlpha(255); 203 | this.mPaint.setTypeface(Typeface.DEFAULT); 204 | } else { 205 | int alpha = (int) (255 * (1 - Math.min(0.9, diff - 1))); 206 | if (mChoose == i) 207 | alpha = 255; 208 | this.mPaint.setAlpha(alpha); 209 | this.mPaint.setTypeface(Typeface.DEFAULT_BOLD); 210 | } 211 | canvas.drawText(mLetters[i], mHalfWidth, letterPosY, this.mPaint); 212 | canvas.restore(); 213 | } 214 | if (mChoose == -1 && mStartEndAnim && mAnimStep <= 0.6f) { 215 | mAnimStep += 0.6f; 216 | postInvalidateDelayed(25); 217 | } else { 218 | mAnimStep = 0f; 219 | mStartEndAnim = false; 220 | } 221 | } 222 | 223 | private int dip2px(int dipValue) { 224 | return (int) (dipValue * mDensity + 0.5f); 225 | } 226 | 227 | private float getMotionEventY(MotionEvent ev, int activePointerId) { 228 | final int index = MotionEventCompat.findPointerIndex(ev, activePointerId); 229 | if (index < 0) { 230 | return -1; 231 | } 232 | return MotionEventCompat.getY(ev, index); 233 | } 234 | 235 | private void onSecondaryPointerUp(MotionEvent ev) { 236 | final int pointerIndex = MotionEventCompat.getActionIndex(ev); 237 | final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); 238 | if (pointerId == mActivePointerId) { 239 | final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 240 | mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); 241 | } 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /app/src/main/java/com/address/xiaolei/addressselect/vo/CityVo.java: -------------------------------------------------------------------------------- 1 | package com.address.xiaolei.addressselect.vo; 2 | 3 | public class CityVo { 4 | private String cityId; 5 | private String cityName; 6 | private String parentId; 7 | private String cityType; 8 | private String pinYin; 9 | 10 | public String getPinYin() { 11 | return pinYin; 12 | } 13 | 14 | public void setPinYin(String pinYin) { 15 | this.pinYin = pinYin; 16 | } 17 | 18 | public CityVo(){} 19 | 20 | public CityVo(String cityId, String cityName, String parentId, String cityType) { 21 | this.cityId = cityId; 22 | this.cityName = cityName; 23 | this.parentId = parentId; 24 | this.cityType = cityType; 25 | } 26 | 27 | public String getCityId() { 28 | return cityId; 29 | } 30 | 31 | public void setCityId(String cityId) { 32 | this.cityId = cityId; 33 | } 34 | 35 | public String getCityName() { 36 | return cityName; 37 | } 38 | 39 | public void setCityName(String cityName) { 40 | this.cityName = cityName; 41 | } 42 | 43 | public String getParentId() { 44 | return parentId; 45 | } 46 | 47 | public void setParentId(String parentId) { 48 | this.parentId = parentId; 49 | } 50 | 51 | public String getCityType() { 52 | return cityType; 53 | } 54 | 55 | public void setCityType(String cityType) { 56 | this.cityType = cityType; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /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-xhdpi/ic_clear_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/app/src/main/res/drawable-xhdpi/ic_clear_gray.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_search_small_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/app/src/main/res/drawable-xhdpi/ic_search_small_2.png -------------------------------------------------------------------------------- /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 | 9 | 10 | 11 | 17 | 18 | 19 | 29 | 30 | 31 | 32 | 33 | 38 | 39 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_city.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_city_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 24 | 25 | 33 | 34 | 35 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /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/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # 5 | A 6 | B 7 | C 8 | D 9 | E 10 | F 11 | G 12 | H 13 | I 14 | J 15 | K 16 | L 17 | M 18 | N 19 | O 20 | P 21 | Q 22 | R 23 | S 24 | T 25 | U 26 | V 27 | W 28 | X 29 | Y 30 | Z 31 | 32 | 33 | 34 | 35 | 36 | [ 37 | { 38 | "citycityId": 10, 39 | "cityName": "上海", 40 | "py": "shanghai" 41 | }, 42 | { 43 | "cityId": 1, 44 | "cityName": "北京", 45 | "py": "beijing" 46 | }, 47 | { 48 | "cityId": 20, 49 | "cityName": "广州", 50 | "py": "guangzhou" 51 | }, 52 | { 53 | "cityId": 30, 54 | "cityName": "深圳", 55 | "py": "shenzhen" 56 | }, 57 | { 58 | "cityId": 57, 59 | "cityName": "武汉", 60 | "py": "wuhan" 61 | }, 62 | { 63 | "cityId": 40, 64 | "cityName": "天津", 65 | "py": "tianjin" 66 | }, 67 | { 68 | "cityId": 42, 69 | "cityName": "西安", 70 | "py": "xian" 71 | }, 72 | { 73 | "cityId": 55, 74 | "cityName": "南京", 75 | "py": "nanjing" 76 | }, 77 | { 78 | "cityId": 50, 79 | "cityName": "杭州", 80 | "py": "hangzhou" 81 | }, 82 | { 83 | "cityId": 59, 84 | "cityName": "成都", 85 | "py": "chengdu" 86 | }, 87 | { 88 | "cityId": 45, 89 | "cityName": "重庆", 90 | "py": "chongqing" 91 | } 92 | ] 93 | 94 | 95 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | 8 | #2c2c2c 9 | 10 | 11 | #333333 12 | #12a2f1 13 | #ffb400 14 | #999999 15 | #666666 16 | #c8c8c8 17 | 18 | #f0f0f0 19 | #f5f5f5 20 | 21 | #ffffff 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AddressSelect 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/values.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 50dp 4 | 10dp 5 | 4dp 6 | 40dp 7 | 72dp 8 | 12dp 9 | 12sp 10 | 14sp 11 | 16sp 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/test/java/com/address/xiaolei/addressselect/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.address.xiaolei.addressselect; 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 | //jCenter 12 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' 13 | //Maven 14 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 15 | 16 | // NOTE: Do not place your application dependencies here; they belong 17 | // in the individual module build.gradle files 18 | } 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | google() 24 | jcenter() 25 | maven { url "https://jitpack.io" } 26 | } 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /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=-Xmx1537m 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/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jan 10 19:12:25 CST 2019 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 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /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 | 113 | 114 | # For Cygwin, switch paths to Windows format before running java 115 | if $cygwin ; then 116 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 117 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 118 | JAVACMD=`cygpath --unix "$JAVACMD"` 119 | 120 | # We build the pattern for arguments to be converted via cygpath 121 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 122 | SEP="" 123 | for dir in $ROOTDIRSRAW ; do 124 | ROOTDIRS="$ROOTDIRS$SEP$dir" 125 | SEP="|" 126 | done 127 | OURCYGPATTERN="(^($ROOTDIRS))" 128 | # Add a user-defined pattern to the cygpath arguments 129 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 130 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 131 | fi 132 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 133 | i=0 134 | for arg in "$@" ; do 135 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 136 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 137 | 138 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 139 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 140 | else 141 | eval `echo args$i`="\"$arg\"" 142 | fi 143 | i=$((i+1)) 144 | done 145 | case $i in 146 | (0) set -- ;; 147 | (1) set -- "$args0" ;; 148 | (2) set -- "$args0" "$args1" ;; 149 | (3) set -- "$args0" "$args1" "$args2" ;; 150 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 151 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 152 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 153 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 154 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 155 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 156 | esac 157 | fi 158 | 159 | # Escape application args 160 | save () { 161 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 162 | echo " " 163 | } 164 | APP_ARGS=$(save "$@") 165 | 166 | # Collect all arguments for the java command, following the shell quoting and substitution rules 167 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 168 | 169 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 170 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 171 | cd "$(dirname "$0")" 172 | fi 173 | 174 | exec "$JAVACMD" "$@" 175 | -------------------------------------------------------------------------------- /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 | 12 | set DIRNAME=%~dp0 13 | if "%DIRNAME%" == "" set DIRNAME=. 14 | set APP_BASE_NAME=%~n0 15 | set APP_HOME=%DIRNAME% 16 | 17 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 18 | set DEFAULT_JVM_OPTS= 19 | 20 | @rem Find java.exe 21 | if defined JAVA_HOME goto findJavaFromJavaHome 22 | 23 | set JAVA_EXE=java.exe 24 | %JAVA_EXE% -version >NUL 2>&1 25 | if "%ERRORLEVEL%" == "0" goto init 26 | 27 | echo. 28 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 29 | echo. 30 | echo Please set the JAVA_HOME variable in your environment to match the 31 | echo location of your Java installation. 32 | 33 | goto fail 34 | 35 | :findJavaFromJavaHome 36 | set JAVA_HOME=%JAVA_HOME:"=% 37 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 38 | 39 | if exist "%JAVA_EXE%" goto init 40 | 41 | echo. 42 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 43 | echo. 44 | echo Please set the JAVA_HOME variable in your environment to match the 45 | echo location of your Java installation. 46 | 47 | goto fail 48 | 49 | :init 50 | @rem Get command-line arguments, handling Windows variants 51 | 52 | if not "%OS%" == "Windows_NT" goto win9xME_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | 64 | :execute 65 | @rem Setup the command line 66 | 67 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 68 | 69 | @rem Execute Gradle 70 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 71 | 72 | :end 73 | @rem End local scope for the variables with windows NT shell 74 | if "%ERRORLEVEL%"=="0" goto mainEnd 75 | 76 | :fail 77 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 78 | rem the _cmd.exe /c_ return code! 79 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 80 | exit /b 1 81 | 82 | :mainEnd 83 | if "%OS%"=="Windows_NT" endlocal 84 | 85 | :omega 86 | -------------------------------------------------------------------------------- /img/GIF.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/img/GIF.gif -------------------------------------------------------------------------------- /img/device-2019-01-17-185749.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/img/device-2019-01-17-185749.png -------------------------------------------------------------------------------- /img/device-2019-01-17-185822.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/img/device-2019-01-17-185822.png -------------------------------------------------------------------------------- /img/sql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sky8650/AddressSelect/8540208b47703b69f83d4893dcd837b715d5014a/img/sql.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | 4 | 5 | 6 | 7 | --------------------------------------------------------------------------------