Repository: YoKeyword/IndexableStickyListView
Branch: master
Commit: b3c54d6caddd
Files: 69
Total size: 154.5 KB
Directory structure:
gitextract_u96ej4no/
├── .gitignore
├── LICENSE
├── README.md
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── indexablerecyclerview/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── me/
│ │ └── yokeyword/
│ │ └── indexablerv/
│ │ ├── AbstractHeaderFooterAdapter.java
│ │ ├── EntityWrapper.java
│ │ ├── IndexBar.java
│ │ ├── IndexableAdapter.java
│ │ ├── IndexableEntity.java
│ │ ├── IndexableFooterAdapter.java
│ │ ├── IndexableHeaderAdapter.java
│ │ ├── IndexableLayout.java
│ │ ├── InitialComparator.java
│ │ ├── PinyinComparator.java
│ │ ├── PinyinUtil.java
│ │ ├── RealAdapter.java
│ │ ├── SimpleFooterAdapter.java
│ │ ├── SimpleHeaderAdapter.java
│ │ └── database/
│ │ ├── DataObservable.java
│ │ ├── DataObserver.java
│ │ ├── HeaderFooterDataObservable.java
│ │ ├── HeaderFooterDataObserver.java
│ │ ├── IndexBarDataObservable.java
│ │ └── IndexBarDataObserver.java
│ └── res/
│ ├── drawable/
│ │ └── indexable_bg_center_overlay.xml
│ └── values/
│ ├── indexable_attrs.xml
│ ├── indexable_default.xml
│ └── indexable_strings.xml
├── sample/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── me/
│ │ └── yokeyword/
│ │ └── sample/
│ │ ├── MainActivity.java
│ │ ├── ToastUtil.java
│ │ ├── city/
│ │ │ ├── CityAdapter.java
│ │ │ ├── CityEntity.java
│ │ │ ├── PickCityActivity.java
│ │ │ └── SearchFragment.java
│ │ └── contact/
│ │ ├── ContactAdapter.java
│ │ ├── MenuEntity.java
│ │ ├── PickContactActivity.java
│ │ └── UserEntity.java
│ └── res/
│ ├── layout/
│ │ ├── activity_main.xml
│ │ ├── activity_pick_city.xml
│ │ ├── activity_pick_contact.xml
│ │ ├── fragment_search_city.xml
│ │ ├── header_contact_banner.xml
│ │ ├── header_contact_menu.xml
│ │ ├── item_city.xml
│ │ ├── item_contact.xml
│ │ ├── item_index_city.xml
│ │ └── item_index_contact.xml
│ ├── values/
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── string_city.xml
│ │ ├── string_contact.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── values-w820dp/
│ └── dimens.xml
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.idea/
.DS_Store
/build
/captures
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright YoKey
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# IndexableRecyclerView
A RecyclerView with indexable, sticky and many other features.
**轻松实现:选择城市,选择联系人等需要索引的功能**
> 替代之前的IndexableStickyListView(移至该[分支](https://github.com/YoKeyword/IndexableRecyclerView/tree/listview)),进行大幅度重构,性能优化,更易使用的API,更易拓展的HeaderView/FooterView等等!
重构历程可以看这篇文章:[[设计模式]记一次开源库的重构历程](http://www.jianshu.com/p/2ee8706c346b)
# Demo演示
<img src="/gif/demo_city.gif" width="320px"/>
<img src="/gif/demo_contact.gif" width="320px"/>
# 特性
1、根据数据源,自动**排序生成**字母索引Bar(非字母开头,索引为"#",另可自由定制),以及HeaderTitle
2、非常自由的 添加各种HeaderView/FooterView,包括自定义索引,HeaderTitle,各种View等等
3、HeaderTitle是粘性的(Sticky)
4、UI自由定制、拓展;提供2种悬浮提示View,常规居中 以及 MD风格的右侧气泡
5、绑定数据源,通过单线程的线程池优化,不怕重复绑定数据
6、使用[TinyPinyin](https://github.com/promeG/TinyPinyin)代替Pinyin4j.jar库,体积更小,拼音转化速度提升4倍!
# 更新日志
1.3.0
* 多音字借助TinyPinyin处理
* 可以自定义排序方式
1.2.4
* Fix 数据变动时,StickTitle不及时更新问题;增加2处安全校验
1.2.0
* 支持GridLayoutManager! (感谢[guodongAndroid](https://github.com/guodongAndroid))
1.0.7
* 默认不再显示左侧的悬浮气泡
* 默认排序方式改为快速排序,提供一个MODE_NONE的排序方式
1.0.5
为HeaderView/FooterView添加:
* `indexableLayout.removeHeaderAdapter();`
* `headerAdapter.addData()`
* `headerAdapter.removeData()`
# 如何使用
### gradle
项目下app的build.gradle中依赖:
````xml
compile 'me.yokeyword:indexablerecyclerview:1.3.0'
compile 'com.android.support:appcompat-v7:你使用的版本号'
compile 'com.android.support:recyclerview-v7:你使用的版本号'
````
### Xml
````xml
<me.yokeyword.indexablerv.IndexableLayout
...
app:indexBar_layout_width="24dp" // IndexBar:width
app:indexBar_background="#08000000" // IndexBar:background
app:indexBar_textColor="#000000" // IndexBar:textColor
app:indexBar_selectedTextColor="#f33737" // IndexBar:isSelected textColor
app:indexBar_textSize="14sp" // IndexBar:textSize
app:indexBar_textSpace="6dp" /> // IndexBar:text lineSpace
````
### 3步集成
**1、实体类实现IndexableEntity**
````java
public class CityEntity implements IndexableEntity {
...
private String name;
private String pinyin;
@Override
public String getFieldIndexBy() {
return name; // return 你需要根据该属性排序的field
}
@Override
public void setFieldIndexBy(String indexByField) {
this.name = indexByField; // 同上
}
@Override
public void setFieldPinyinIndexBy(String pinyin) {
this.pinyin = pinyin; // 保存排序field的拼音,在执行比如搜索等功能时有用 (若不需要,空实现该方法即可)
}
}
````
**2、继承IndexAdapter**
````java
public class CityAdapter extends IndexableAdapter<CityEntity> {
@Override
public RecyclerView.ViewHolder onCreateTitleViewHolder(ViewGroup parent) {
// 创建 TitleItem 布局
}
@Override
public RecyclerView.ViewHolder onCreateContentViewHolder(ViewGroup parent) {
// 创建 内容Item 布局
}
@Override
public void onBindTitleViewHolder(RecyclerView.ViewHolder holder, String indexTitle) {
// 填充 TitleItem 布局
}
@Override
public void onBindContentViewHolder(RecyclerView.ViewHolder holder, CityEntity entity) {
// 填充 内容Item 布局
}
}
````
**3、绑定视图和数据**
````java
// 支持LinearLayoutManager, GridLayoutManager
indexableLayout.setLayoutManager(LayoutManager);
CityAdapter adapter = new CityAdapter(this);
indexableLayout.setAdapter(adapter);
// 排序过程是异步的 另有setDatas(mDatas,IndexCallback callback) // callback在datas异步排序结束后回调
adapter.setDatas(mDatas);
// 另有setOnItemTitleClickListener(listener),点击TitleItem点击事件以及LongClick
adapter.setOnItemContentClickListener(listener);
````
# 拓展
### 1、设置 索引悬浮提示框 风格
````java
// 前者Material Design风格右侧气泡 , 后者 居中 IOS风格气泡
indexableLayout.setOverlayStyle_MaterialDesign(int Color) & setOverlayStyle_Center()
````
### 2、多音字处理
多音字处理得益于使用了[TinyPinyin](https://github.com/promeG/TinyPinyin),可以如下设置:
````java
// 添加中文城市词典
Pinyin.init(Pinyin.newConfig().with(CnCityDict.getInstance());
// 添加自定义词典
Pinyin.init(Pinyin.newConfig()
.with(new PinyinMapDict() {
@Override
public Map<String, String[]> mapping() {
HashMap<String, String[]> map = new HashMap<String, String[]>();
map.put("重庆", new String[]{"CHONG", "QING"});
return map;
}
}));
````
### 3、添加自定义HeaderView,FooterView
````java
indexableLayout.addHeaderAdapter(IndexableHeaderAdapter adapter) // 添加HeaderView
indexableLayout.addFooterAdapter(IndexableFooterAdapter adapter) // 添加FooterView
// 3个参数分别对应:IndexBar的索引,HeaderTitle,传入的Header数据源,此处泛型T可以是任何实体类,不需要和主Adapter类型一致
// 如果不想显示某块视图,则传入null即可: 比如不想显示 HeaderTitle, 则indexTitle传入null;
IndexableHeaderAdapter<T>(String index, String indexTitle, List<T> datas){
// 需要实现3个方法:
public abstract int getItemViewType(); // 每个HeaderView的type应当不同
public abstract RecyclerView.ViewHolder onCreateContentViewHolder(ViewGroup parent);
public abstract void onBindContentViewHolder(RecyclerView.ViewHolder holder, T entity);
}
// 如果想添加的HeaderView,和主Adapter的布局完全一致,则可以使用:
new SimpleHeaderAdapter(IndexableAdapter<T> adapter, String index, String indexTitle, List<T> datas);
````
### 4、更改排序规则
默认根据**全拼音排序**,可根据需求更改为**按首字母排序**:
````java
// 设置排序规则:
// MODE_FAST:按首字母排序(默认);MODE_ALL_LETTERS:全字母比较,效率较低; MODE_NONE:字母模块内不排序,效率最高
indexableLayout.setCompareMode(@CompareMode int mode);
// 自定义排序规则
indexableLayout.setComparator(yourComparator);
````
> 更多细节使用,参见Demo
## 致谢
[TinyPinyin](https://github.com/promeG/TinyPinyin)
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Wed Jul 05 23:28:02 CST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
================================================
FILE: gradle.properties
================================================
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: indexablerecyclerview/.gitignore
================================================
/build
================================================
FILE: indexablerecyclerview/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
minSdkVersion 14
targetSdkVersion 24
versionCode 1
versionName "1.0"
}
}
dependencies {
compile 'com.github.promeg:tinypinyin:2.0.3'
compile 'com.github.promeg:tinypinyin-lexicons-android-cncity:2.0.3'
provided 'com.android.support:appcompat-v7:25.3.1'
provided 'com.android.support:recyclerview-v7:25.3.1'
}
================================================
FILE: indexablerecyclerview/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/YoKeyword/Documents/android-sdk-macosx/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: indexablerecyclerview/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="me.yokeyword.indexablerecyclerview">
<uses-sdk
tools:overrideLibrary="com.github.promeg.tinypinyin.lexicons.android.cncity,com.github.promeg.tinypinyin.android.asset.lexicons"/>
<application/>
</manifest>
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/AbstractHeaderFooterAdapter.java
================================================
package me.yokeyword.indexablerv;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
import me.yokeyword.indexablerv.database.HeaderFooterDataObservable;
import me.yokeyword.indexablerv.database.HeaderFooterDataObserver;
import me.yokeyword.indexablerv.database.IndexBarDataObservable;
import me.yokeyword.indexablerv.database.IndexBarDataObserver;
/**
* Created by YoKey on 16/10/16.
*/
abstract class AbstractHeaderFooterAdapter<T> {
private final HeaderFooterDataObservable mDataSetObservable = new HeaderFooterDataObservable();
private final IndexBarDataObservable mIndexBarDataSetObservable = new IndexBarDataObservable();
ArrayList<EntityWrapper<T>> mEntityWrapperList = new ArrayList<>();
protected OnItemClickListener<T> mListener;
protected OnItemLongClickListener<T> mLongListener;
private String mIndex, mIndexTitle;
/**
* 不想显示哪个就传null
*
* @param index IndexBar的字母索引
* @param indexTitle IndexTitle
* @param datas 数据源
*/
public AbstractHeaderFooterAdapter(String index, String indexTitle, List<T> datas) {
this.mIndex = index;
this.mIndexTitle = indexTitle;
if (indexTitle != null) {
EntityWrapper<T> wrapper = wrapEntity();
wrapper.setItemType(EntityWrapper.TYPE_TITLE);
}
for (int i = 0; i < datas.size(); i++) {
EntityWrapper<T> wrapper = wrapEntity();
wrapper.setData(datas.get(i));
}
}
private EntityWrapper<T> wrapEntity() {
EntityWrapper<T> wrapper = new EntityWrapper<>();
wrapper.setIndex(mIndex);
wrapper.setIndexTitle(mIndexTitle);
wrapper.setHeaderFooterType(getHeaderFooterType());
mEntityWrapperList.add(wrapper);
return wrapper;
}
private EntityWrapper<T> wrapEntity(int pos) {
EntityWrapper<T> wrapper = new EntityWrapper<>();
wrapper.setIndex(mIndex);
wrapper.setIndexTitle(mIndexTitle);
wrapper.setHeaderFooterType(getHeaderFooterType());
mEntityWrapperList.add(pos, wrapper);
return wrapper;
}
public abstract int getItemViewType();
public abstract RecyclerView.ViewHolder onCreateContentViewHolder(ViewGroup parent);
public abstract void onBindContentViewHolder(RecyclerView.ViewHolder holder, T entity);
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
*/
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
public void addData(T data) {
int size = mEntityWrapperList.size();
EntityWrapper<T> wrapper = wrapEntity();
wrapper.setItemType(getItemViewType());
wrapper.setData(data);
if (size > 0) {
mDataSetObservable.notifyAdd(getHeaderFooterType() == EntityWrapper.TYPE_HEADER, mEntityWrapperList.get(size - 1), wrapper);
mIndexBarDataSetObservable.notifyChanged();
}
}
public void removeData(T data) {
for (EntityWrapper wrapper : mEntityWrapperList) {
if (wrapper.getData() == data) {
mEntityWrapperList.remove(wrapper);
mDataSetObservable.notifyRemove(getHeaderFooterType() == EntityWrapper.TYPE_HEADER, wrapper);
mIndexBarDataSetObservable.notifyChanged();
return;
}
}
}
int getHeaderFooterType() {
return EntityWrapper.TYPE_HEADER;
}
public void addData(int position, T data) {
int size = mEntityWrapperList.size();
if (position >= size) {
return;
}
EntityWrapper<T> wrapper = wrapEntity(position + 1);
wrapper.setItemType(getItemViewType());
wrapper.setData(data);
if (size > 0) {
mDataSetObservable.notifyAdd(getHeaderFooterType() == EntityWrapper.TYPE_HEADER, mEntityWrapperList.get(position), wrapper);
mIndexBarDataSetObservable.notifyChanged();
}
}
public void addDatas(List<T> datas) {
for (int i = 0; i < datas.size(); i++) {
addData(datas.get(i));
}
}
public void addDatas(int position, List<T> datas) {
int size = mEntityWrapperList.size();
if (position >= size) {
return;
}
for (int i = datas.size() - 1; i >= 0; i--) {
addData(position, datas.get(i));
}
}
// public void removeAll(List<T> datas) {
// // TODO: 16/10/27
// }
OnItemClickListener<T> getOnItemClickListener() {
return mListener;
}
OnItemLongClickListener getOnItemLongClickListener() {
return mLongListener;
}
ArrayList<EntityWrapper<T>> getDatas() {
for (EntityWrapper<T> wrapper : mEntityWrapperList) {
if (wrapper.getItemType() == EntityWrapper.TYPE_CONTENT) {
wrapper.setItemType(getItemViewType());
}
}
return mEntityWrapperList;
}
void registerDataSetObserver(HeaderFooterDataObserver observer) {
mDataSetObservable.registerObserver(observer);
}
void unregisterDataSetObserver(HeaderFooterDataObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
void registerIndexBarDataSetObserver(IndexBarDataObserver observer) {
mIndexBarDataSetObservable.registerObserver(observer);
}
void unregisterIndexBarDataSetObserver(IndexBarDataObserver observer) {
mIndexBarDataSetObservable.unregisterObserver(observer);
}
interface OnItemClickListener<T> {
void onItemClick(View v, int currentPosition, T entity);
}
interface OnItemLongClickListener<T> {
boolean onItemLongClick(View v, int currentPosition, T entity);
}
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/EntityWrapper.java
================================================
package me.yokeyword.indexablerv;
/**
* Created by YoKey on 16/10/6.
*/
public class EntityWrapper<T> {
static final int TYPE_TITLE = Integer.MAX_VALUE - 1;
static final int TYPE_CONTENT = Integer.MAX_VALUE;
static final int TYPE_HEADER = 1;
static final int TYPE_FOOTER = 2;
private String index;
private String indexTitle;
private String pinyin;
private String indexByField;
private T data;
private int originalPosition = -1;
private int itemType = TYPE_CONTENT;
private int headerFooterType;
EntityWrapper() {
}
EntityWrapper(String index, int itemType) {
this.index = index;
this.indexTitle = index;
this.pinyin = index;
this.itemType = itemType;
}
public String getIndex() {
return index;
}
void setIndex(String index) {
this.index = index;
}
public String getIndexTitle() {
return indexTitle;
}
void setIndexTitle(String indexTitle) {
this.indexTitle = indexTitle;
}
public String getPinyin() {
return pinyin;
}
void setPinyin(String pinyin) {
this.pinyin = pinyin;
}
public String getIndexByField() {
return indexByField;
}
void setIndexByField(String indexByField) {
this.indexByField = indexByField;
}
public T getData() {
return data;
}
void setData(T data) {
this.data = data;
}
public int getOriginalPosition() {
return originalPosition;
}
void setOriginalPosition(int originalPosition) {
this.originalPosition = originalPosition;
}
int getItemType() {
return itemType;
}
void setItemType(int itemType) {
this.itemType = itemType;
}
int getHeaderFooterType() {
return headerFooterType;
}
void setHeaderFooterType(int headerFooterType) {
this.headerFooterType = headerFooterType;
}
public boolean isTitle(){
return itemType == TYPE_TITLE;
}
public boolean isContent(){
return itemType == TYPE_CONTENT;
}
public boolean isHeader(){
return headerFooterType == TYPE_HEADER;
}
public boolean isFooter(){
return headerFooterType == TYPE_FOOTER;
}
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/IndexBar.java
================================================
package me.yokeyword.indexablerv;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.View;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import me.yokeyword.indexablerecyclerview.R;
/**
* Created by YoKey on 16/10/6.
*/
class IndexBar extends View {
private int mTotalHeight;
private float mTextSpace;
private List<String> mIndexList = new ArrayList<>();
// 首字母 到 mIndexList 的映射
private HashMap<String, Integer> mMapping = new HashMap<>();
private ArrayList<EntityWrapper> mDatas;
private int mSelectionPosition;
private float mIndexHeight;
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Paint mFocusPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
public IndexBar(Context context) {
super(context);
}
void init(Drawable barBg, int barTextColor, int barFocusTextColor, float barTextSize, float textSpace) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
setBackground(barBg);
} else {
setBackgroundDrawable(barBg);
}
this.mTextSpace = textSpace;
mPaint.setColor(barTextColor);
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setTextSize(barTextSize);
mFocusPaint.setTextAlign(Paint.Align.CENTER);
mFocusPaint.setTextSize(barTextSize + (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics()));
mFocusPaint.setColor(barFocusTextColor);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int mode = MeasureSpec.getMode(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (mIndexList.size() > 0) {
mTotalHeight = (int) (((mIndexList.size() - 1) * mPaint.getTextSize()
+ mFocusPaint.getTextSize())
+ (mIndexList.size() + 1) * mTextSpace);
}
if (mTotalHeight > height) {
mTotalHeight = height;
}
// // TODO: 16/10/8 Measure AT_MOST
// if (mode == MeasureSpec.AT_MOST) {
// int maxWidth = (int) getResources().getDimension(R.dimen.default_indexBar_layout_width);
// super.onMeasure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(mTotalHeight, MeasureSpec.EXACTLY));
// return;
// }
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(mTotalHeight, MeasureSpec.EXACTLY));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mIndexList.size() == 0) return;
mIndexHeight = ((float) getHeight()) / mIndexList.size();
for (int i = 0; i < mIndexList.size(); i++) {
if (mSelectionPosition == i) {
canvas.drawText(mIndexList.get(i), getWidth() / 2, mIndexHeight * 0.85f + mIndexHeight * i, mFocusPaint);
} else {
canvas.drawText(mIndexList.get(i), getWidth() / 2, mIndexHeight * 0.85f + mIndexHeight * i, mPaint);
}
}
}
int getPositionForPointY(float y) {
if (mIndexList.size() <= 0) return -1;
int position = (int) (y / mIndexHeight);
if (position < 0) {
position = 0;
} else if (position > mIndexList.size() - 1) {
position = mIndexList.size() - 1;
}
return position;
}
int getSelectionPosition() {
return mSelectionPosition;
}
void setSelectionPosition(int position) {
this.mSelectionPosition = position;
invalidate();
}
int getFirstRecyclerViewPositionBySelection() {
String index = mIndexList.get(mSelectionPosition);
if (mMapping.containsKey(index)) {
return mMapping.get(index);
}
return -1;
}
List<String> getIndexList() {
return mIndexList;
}
void setDatas(boolean showAllLetter, ArrayList<EntityWrapper> datas) {
this.mDatas = datas;
this.mIndexList.clear();
this.mMapping.clear();
ArrayList<String> tempHeaderList = null;
if (showAllLetter) {
mIndexList = Arrays.asList(getResources().getStringArray(R.array.indexable_letter));
mIndexList = new ArrayList<>(mIndexList);
tempHeaderList = new ArrayList<>();
}
for (int i = 0; i < datas.size(); i++) {
EntityWrapper wrapper = datas.get(i);
if (wrapper.getItemType() == EntityWrapper.TYPE_TITLE || wrapper.getIndexTitle() == null) {
String index = wrapper.getIndex();
if (!TextUtils.isEmpty(index)) {
if (!showAllLetter) {
mIndexList.add(index);
} else {
if (IndexableLayout.INDEX_SIGN.equals(index)) {
mIndexList.add(IndexableLayout.INDEX_SIGN);
} else if (mIndexList.indexOf(index) < 0) {
if (wrapper.getHeaderFooterType() == EntityWrapper.TYPE_HEADER && tempHeaderList.indexOf(index) < 0) {
tempHeaderList.add(index);
} else if (wrapper.getHeaderFooterType() == EntityWrapper.TYPE_FOOTER) {
mIndexList.add(index);
}
}
}
if (!mMapping.containsKey(index)) {
mMapping.put(index, i);
}
}
}
}
if (showAllLetter) {
mIndexList.addAll(0, tempHeaderList);
}
requestLayout();
}
void setSelection(int firstVisibleItemPosition) {
if (mDatas == null || mDatas.size() <= firstVisibleItemPosition || firstVisibleItemPosition < 0)
return;
EntityWrapper wrapper = mDatas.get(firstVisibleItemPosition);
int position = mIndexList.indexOf(wrapper.getIndex());
if (mSelectionPosition != position && position >= 0) {
mSelectionPosition = position;
invalidate();
}
}
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/IndexableAdapter.java
================================================
package me.yokeyword.indexablerv;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
import me.yokeyword.indexablerv.database.DataObservable;
import me.yokeyword.indexablerv.database.DataObserver;
/**
* Created by YoKey on 16/10/6.
*/
public abstract class IndexableAdapter<T extends IndexableEntity> {
static final int TYPE_ALL = 0;
static final int TYPE_CLICK_TITLE = 1;
static final int TYPE_CLICK_CONTENT = 2;
static final int TYPE_LONG_CLICK_TITLE = 3;
static final int TYPE_LONG_CLICK_CONTENT = 4;
private final DataObservable mDataSetObservable = new DataObservable();
private List<T> mDatas;
private IndexCallback<T> mCallback;
private OnItemTitleClickListener mTitleClickListener;
private OnItemContentClickListener mContentClickListener;
private OnItemTitleLongClickListener mTitleLongClickListener;
private OnItemContentLongClickListener mContentLongClickListener;
public abstract RecyclerView.ViewHolder onCreateTitleViewHolder(ViewGroup parent);
public abstract RecyclerView.ViewHolder onCreateContentViewHolder(ViewGroup parent);
public abstract void onBindTitleViewHolder(RecyclerView.ViewHolder holder, String indexTitle);
public abstract void onBindContentViewHolder(RecyclerView.ViewHolder holder, T entity);
public void setDatas(List<T> datas) {
setDatas(datas, null);
}
/**
* @param callback Register a callback to be invoked when this datas is processed.
*/
public void setDatas(List<T> datas, IndexCallback<T> callback) {
this.mCallback = callback;
mDatas = datas;
notifyInited();
}
/**
* set Index-ItemView click listener
*/
public void setOnItemTitleClickListener(OnItemTitleClickListener listener) {
this.mTitleClickListener = listener;
notifySetListener(TYPE_CLICK_TITLE);
}
/**
* set Content-ItemView click listener
*/
public void setOnItemContentClickListener(OnItemContentClickListener<T> listener) {
this.mContentClickListener = listener;
notifySetListener(TYPE_CLICK_CONTENT);
}
/**
* set Index-ItemView longClick listener
*/
public void setOnItemTitleLongClickListener(OnItemTitleLongClickListener listener) {
this.mTitleLongClickListener = listener;
notifySetListener(TYPE_LONG_CLICK_TITLE);
}
/**
* set Content-ItemView longClick listener
*/
public void setOnItemContentLongClickListener(OnItemContentLongClickListener<T> listener) {
this.mContentLongClickListener = listener;
notifySetListener(TYPE_LONG_CLICK_CONTENT);
}
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
*/
public void notifyDataSetChanged() {
mDataSetObservable.notifyInited();
// mDataSetObservable.notifyChanged();
}
private void notifyInited() {
mDataSetObservable.notifyInited();
}
private void notifySetListener(int type) {
mDataSetObservable.notifySetListener(type);
}
public List<T> getItems() {
return mDatas;
}
IndexCallback<T> getIndexCallback() {
return mCallback;
}
OnItemTitleClickListener getOnItemTitleClickListener() {
return mTitleClickListener;
}
OnItemTitleLongClickListener getOnItemTitleLongClickListener() {
return mTitleLongClickListener;
}
OnItemContentClickListener getOnItemContentClickListener() {
return mContentClickListener;
}
OnItemContentLongClickListener getOnItemContentLongClickListener() {
return mContentLongClickListener;
}
void registerDataSetObserver(DataObserver observer) {
mDataSetObservable.registerObserver(observer);
}
void unregisterDataSetObserver(DataObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
public interface IndexCallback<T> {
void onFinished(List<EntityWrapper<T>> datas);
}
public interface OnItemTitleClickListener {
void onItemClick(View v, int currentPosition, String indexTitle);
}
public interface OnItemContentClickListener<T> {
void onItemClick(View v, int originalPosition, int currentPosition, T entity);
}
public interface OnItemTitleLongClickListener {
boolean onItemLongClick(View v, int currentPosition, String indexTitle);
}
public interface OnItemContentLongClickListener<T> {
boolean onItemLongClick(View v, int originalPosition, int currentPosition, T entity);
}
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/IndexableEntity.java
================================================
package me.yokeyword.indexablerv;
/**
* Created by YoKey on 16/10/9.
*/
public interface IndexableEntity {
String getFieldIndexBy();
void setFieldIndexBy(String indexField);
void setFieldPinyinIndexBy(String pinyin);
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/IndexableFooterAdapter.java
================================================
package me.yokeyword.indexablerv;
import java.util.List;
/**
* FooterView Adapter
* Created by YoKey on 16/10/14.
*/
public abstract class IndexableFooterAdapter<T> extends AbstractHeaderFooterAdapter<T> {
public IndexableFooterAdapter(String index, String indexTitle, List<T> datas) {
super(index, indexTitle, datas);
}
@Override
int getHeaderFooterType() {
return EntityWrapper.TYPE_FOOTER;
}
/**
* set Content-ItemView click listener
*/
public void setOnItemFooterClickListener(OnItemFooterClickListener<T> listener) {
this.mListener = listener;
}
/**
* set Content-ItemView longClick listener
*/
public void setOnItemFooterLongClickListener(OnItemFooterLongClickListener<T> listener) {
this.mLongListener = listener;
}
public interface OnItemFooterClickListener<T> extends OnItemClickListener<T>{
}
public interface OnItemFooterLongClickListener<T> extends OnItemLongClickListener<T>{
}
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/IndexableHeaderAdapter.java
================================================
package me.yokeyword.indexablerv;
import java.util.List;
/**
* HeaderView Adapter
* Created by YoKey on 16/10/8.
*/
public abstract class IndexableHeaderAdapter<T> extends AbstractHeaderFooterAdapter<T>{
public IndexableHeaderAdapter(String index, String indexTitle, List<T> datas) {
super(index, indexTitle, datas);
}
@Override
int getHeaderFooterType() {
return EntityWrapper.TYPE_HEADER;
}
/**
* set Content-ItemView click listener
*/
public void setOnItemHeaderClickListener(OnItemHeaderClickListener<T> listener) {
this.mListener = listener;
}
/**
* set Content-ItemView longClick listener
*/
public void setOnItemHeaderLongClickListener(OnItemHeaderLongClickListener<T> listener) {
this.mLongListener = listener;
}
public interface OnItemHeaderClickListener<T> extends OnItemClickListener<T>{
}
public interface OnItemHeaderLongClickListener<T> extends OnItemLongClickListener<T>{
}
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/IndexableLayout.java
================================================
package me.yokeyword.indexablerv;
import android.app.Activity;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.IntDef;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.AppCompatTextView;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.TextView;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.TreeMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import me.yokeyword.indexablerecyclerview.R;
import me.yokeyword.indexablerv.database.DataObserver;
import me.yokeyword.indexablerv.database.HeaderFooterDataObserver;
import me.yokeyword.indexablerv.database.IndexBarDataObserver;
/**
* RecyclerView + IndexBar
* Created by YoKey on 16/10/6.
*/
@SuppressWarnings("unchecked")
public class IndexableLayout extends FrameLayout {
// 快速排序,只比对首字母(默认)
public static final int MODE_FAST = 0;
// 全字母比较排序, 效率最低
public static final int MODE_ALL_LETTERS = 1;
// 每个字母模块内:无需排序,效率最高
public static final int MODE_NONE = 2;
private static int PADDING_RIGHT_OVERLAY;
static final String INDEX_SIGN = "#";
private Context mContext;
private boolean mShowAllLetter = true;
private ExecutorService mExecutorService;
private Future mFuture;
private RecyclerView mRecy;
private IndexBar mIndexBar;
/**
* 保存正在Invisible的ItemView
* <p>
* 使用mLastInvisibleRecyclerViewItemView来保存当前Invisible的ItemView,
* 每次有新的ItemView需要Invisible的时候,把旧的Invisible的ItemView设为Visible。
* 这样就修复了View复用导致的Invisible状态传递的问题。
*/
private View mLastInvisibleRecyclerViewItemView;
private boolean mSticyEnable = true;
private RecyclerView.ViewHolder mStickyViewHolder;
private String mStickyTitle;
private RealAdapter mRealAdapter;
private RecyclerView.LayoutManager mLayoutManager;
private IndexableAdapter mIndexableAdapter;
private TextView mCenterOverlay, mMDOverlay;
private int mBarTextColor, mBarFocusTextColor;
private float mBarTextSize, mBarTextSpace, mBarWidth;
private Drawable mBarBg;
private DataObserver mDataSetObserver;
private int mCompareMode = MODE_FAST;
private Comparator mComparator;
private Handler mHandler;
private HeaderFooterDataObserver<EntityWrapper> mHeaderFooterDataSetObserver = new HeaderFooterDataObserver<EntityWrapper>() {
@Override
public void onChanged() {
if (mRealAdapter == null) return;
mRealAdapter.notifyDataSetChanged();
}
@Override
public void onAdd(boolean header, EntityWrapper preData, EntityWrapper data) {
if (mRealAdapter == null) return;
mRealAdapter.addHeaderFooterData(header, preData, data);
}
@Override
public void onRemove(boolean header, EntityWrapper data) {
if (mRealAdapter == null) return;
mRealAdapter.removeHeaderFooterData(header, data);
}
};
private IndexBarDataObserver mIndexBarDataSetObserver = new IndexBarDataObserver() {
@Override
public void onChanged() {
mIndexBar.setDatas(mShowAllLetter, mRealAdapter.getItems());
}
};
public IndexableLayout(Context context) {
this(context, null);
}
public IndexableLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public IndexableLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
/**
* set RealAdapter
* {@link #setLayoutManager(RecyclerView.LayoutManager)}
*/
public <T extends IndexableEntity> void setAdapter(final IndexableAdapter<T> adapter) {
if (mLayoutManager == null) {
throw new NullPointerException("You must set the LayoutManager first");
}
this.mIndexableAdapter = adapter;
if (mDataSetObserver != null) {
adapter.unregisterDataSetObserver(mDataSetObserver);
}
mDataSetObserver = new DataObserver() {
@Override
public void onInited() {
onSetListener(IndexableAdapter.TYPE_ALL);
onDataChanged();
}
@Override
public void onChanged() {
if (mRealAdapter != null) {
mRealAdapter.notifyDataSetChanged();
}
}
@Override
public void onSetListener(int type) {
// set listeners
if ((type == IndexableAdapter.TYPE_CLICK_TITLE || type == IndexableAdapter.TYPE_ALL) && adapter.getOnItemTitleClickListener() != null) {
mRealAdapter.setOnItemTitleClickListener(adapter.getOnItemTitleClickListener());
}
if ((type == IndexableAdapter.TYPE_LONG_CLICK_TITLE || type == IndexableAdapter.TYPE_ALL) && adapter.getOnItemTitleLongClickListener() != null) {
mRealAdapter.setOnItemTitleLongClickListener(adapter.getOnItemTitleLongClickListener());
}
if ((type == IndexableAdapter.TYPE_CLICK_CONTENT || type == IndexableAdapter.TYPE_ALL) && adapter.getOnItemContentClickListener() != null) {
mRealAdapter.setOnItemContentClickListener(adapter.getOnItemContentClickListener());
}
if ((type == IndexableAdapter.TYPE_LONG_CLICK_CONTENT || type == IndexableAdapter.TYPE_ALL) && adapter.getOnItemContentLongClickListener() != null) {
mRealAdapter.setOnItemContentLongClickListener(adapter.getOnItemContentLongClickListener());
}
}
};
adapter.registerDataSetObserver(mDataSetObserver);
mRealAdapter.setIndexableAdapter(adapter);
if (mSticyEnable) {
initStickyView(adapter);
}
}
/**
* add HeaderView Adapter
*/
public <T> void addHeaderAdapter(IndexableHeaderAdapter<T> adapter) {
adapter.registerDataSetObserver(mHeaderFooterDataSetObserver);
adapter.registerIndexBarDataSetObserver(mIndexBarDataSetObserver);
mRealAdapter.addIndexableHeaderAdapter(adapter);
}
/**
* removeData HeaderView Adapter
*/
public <T> void removeHeaderAdapter(IndexableHeaderAdapter<T> adapter) {
try {
adapter.unregisterDataSetObserver(mHeaderFooterDataSetObserver);
adapter.unregisterIndexBarDataSetObserver(mIndexBarDataSetObserver);
mRealAdapter.removeIndexableHeaderAdapter(adapter);
} catch (Exception ignored) {
}
}
/**
* add FooterView Adapter
*/
public <T> void addFooterAdapter(IndexableFooterAdapter<T> adapter) {
adapter.registerDataSetObserver(mHeaderFooterDataSetObserver);
adapter.registerIndexBarDataSetObserver(mIndexBarDataSetObserver);
mRealAdapter.addIndexableFooterAdapter(adapter);
}
/**
* removeData FooterView Adapter
*/
public <T> void removeFooterAdapter(IndexableFooterAdapter<T> adapter) {
try {
adapter.unregisterDataSetObserver(mHeaderFooterDataSetObserver);
adapter.unregisterIndexBarDataSetObserver(mIndexBarDataSetObserver);
mRealAdapter.removeIndexableFooterAdapter(adapter);
} catch (Exception ignored) {
}
}
/**
* set sort-mode
* Deprecated {@link #setCompareMode(int)}
*/
@Deprecated
public void setFastCompare(boolean fastCompare) {
setCompareMode(fastCompare ? MODE_FAST : MODE_ALL_LETTERS);
}
@IntDef({MODE_FAST, MODE_ALL_LETTERS, MODE_NONE})
@Retention(RetentionPolicy.SOURCE)
@interface CompareMode {
}
/**
* set sort-mode
*/
public void setCompareMode(@CompareMode int mode) {
this.mCompareMode = mode;
}
/**
* set sort-mode
*/
public <T extends IndexableEntity> void setComparator(Comparator<EntityWrapper<T>> comparator) {
this.mComparator = comparator;
}
/**
* set Sticky Enable
*/
public void setStickyEnable(boolean enable) {
this.mSticyEnable = enable;
}
/**
* display all letter-index
*/
public void showAllLetter(boolean show) {
mShowAllLetter = show;
}
/**
* display Material Design OverlayView
*/
public void setOverlayStyle_MaterialDesign(int color) {
if (mMDOverlay == null) {
initMDOverlay(color);
} else {
ViewCompat.setBackgroundTintList(mMDOverlay, ColorStateList.valueOf(color));
}
mCenterOverlay = null;
}
/**
* display Center OverlayView
*/
public void setOverlayStyle_Center() {
if (mCenterOverlay == null) {
initCenterOverlay();
}
mMDOverlay = null;
}
/**
* get OverlayView
*/
public TextView getOverlayView() {
return mMDOverlay != null ? mMDOverlay : mCenterOverlay;
}
/**
* get RecyclerView
*/
public RecyclerView getRecyclerView() {
return mRecy;
}
/**
* Set the enabled state of this IndexBar.
*/
public void setIndexBarVisibility(boolean visible) {
mIndexBar.setVisibility(visible ? VISIBLE : GONE);
}
private void init(Context context, AttributeSet attrs) {
this.mContext = context;
this.mExecutorService = Executors.newSingleThreadExecutor();
PADDING_RIGHT_OVERLAY = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics());
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.IndexableRecyclerView);
mBarTextColor = a.getColor(R.styleable.IndexableRecyclerView_indexBar_textColor, ContextCompat.getColor(context, R.color.default_indexBar_textColor));
mBarTextSize = a.getDimension(R.styleable.IndexableRecyclerView_indexBar_textSize, getResources().getDimension(R.dimen.default_indexBar_textSize));
mBarFocusTextColor = a.getColor(R.styleable.IndexableRecyclerView_indexBar_selectedTextColor, ContextCompat.getColor(context, R.color.default_indexBar_selectedTextColor));
mBarTextSpace = a.getDimension(R.styleable.IndexableRecyclerView_indexBar_textSpace, getResources().getDimension(R.dimen.default_indexBar_textSpace));
mBarBg = a.getDrawable(R.styleable.IndexableRecyclerView_indexBar_background);
mBarWidth = a.getDimension(R.styleable.IndexableRecyclerView_indexBar_layout_width, getResources().getDimension(R.dimen.default_indexBar_layout_width));
a.recycle();
}
if (mContext instanceof Activity) {
((Activity) mContext).getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
}
mRecy = new RecyclerView(context);
mRecy.setVerticalScrollBarEnabled(false);
mRecy.setOverScrollMode(View.OVER_SCROLL_NEVER);
addView(mRecy, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
mIndexBar = new IndexBar(context);
mIndexBar.init(mBarBg, mBarTextColor, mBarFocusTextColor, mBarTextSize, mBarTextSpace);
LayoutParams params = new LayoutParams((int) mBarWidth, LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.END | Gravity.CENTER_VERTICAL;
addView(mIndexBar, params);
mRealAdapter = new RealAdapter();
mRecy.setHasFixedSize(true);
mRecy.setAdapter(mRealAdapter);
initListener();
}
/**
* {@link #setAdapter(IndexableAdapter)}
*
* @param layoutManager One of LinearLayoutManager and GridLayoutManager
*/
public void setLayoutManager(RecyclerView.LayoutManager layoutManager) {
if (layoutManager == null)
throw new NullPointerException("LayoutManager == null");
mLayoutManager = layoutManager;
if (layoutManager instanceof GridLayoutManager) {
final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
int spanSize = 0;
if (mRealAdapter.getItemViewType(position) == EntityWrapper.TYPE_TITLE) {
spanSize = gridLayoutManager.getSpanCount();
} else if (mRealAdapter.getItemViewType(position) == EntityWrapper.TYPE_CONTENT) {
spanSize = 1;
}
return spanSize;
}
});
}
mRecy.setLayoutManager(mLayoutManager);
}
private void initListener() {
mRecy.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
processScrollListener();
}
});
mIndexBar.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int touchPos = mIndexBar.getPositionForPointY(event.getY());
if (touchPos < 0) return true;
if (!(mLayoutManager instanceof LinearLayoutManager)) return true;
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) mLayoutManager;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
showOverlayView(event.getY(), touchPos);
if (touchPos != mIndexBar.getSelectionPosition()) {
mIndexBar.setSelectionPosition(touchPos);
if (touchPos == 0) {
linearLayoutManager.scrollToPositionWithOffset(0, 0);
} else {
linearLayoutManager.scrollToPositionWithOffset(mIndexBar.getFirstRecyclerViewPositionBySelection(), 0);
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mCenterOverlay != null) mCenterOverlay.setVisibility(GONE);
if (mMDOverlay != null) mMDOverlay.setVisibility(GONE);
break;
}
return true;
}
});
}
private void processScrollListener() {
if (!(mLayoutManager instanceof LinearLayoutManager)) return;
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) mLayoutManager;
int firstItemPosition;
firstItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
if (firstItemPosition == RecyclerView.NO_POSITION) return;
mIndexBar.setSelection(firstItemPosition);
if (!mSticyEnable) return;
ArrayList<EntityWrapper> list = mRealAdapter.getItems();
if (mStickyViewHolder != null && list.size() > firstItemPosition) {
EntityWrapper wrapper = list.get(firstItemPosition);
String wrapperTitle = wrapper.getIndexTitle();
if (EntityWrapper.TYPE_TITLE == wrapper.getItemType()) {
if (mLastInvisibleRecyclerViewItemView != null && mLastInvisibleRecyclerViewItemView.getVisibility() == INVISIBLE) {
mLastInvisibleRecyclerViewItemView.setVisibility(VISIBLE);
mLastInvisibleRecyclerViewItemView = null;
}
mLastInvisibleRecyclerViewItemView = linearLayoutManager.findViewByPosition(firstItemPosition);
if (mLastInvisibleRecyclerViewItemView != null) {
mLastInvisibleRecyclerViewItemView.setVisibility(INVISIBLE);
}
}
// hide -> show
if (wrapperTitle == null && mStickyViewHolder.itemView.getVisibility() == VISIBLE) {
mStickyTitle = null;
mStickyViewHolder.itemView.setVisibility(INVISIBLE);
} else {
stickyNewViewHolder(wrapperTitle);
}
// GirdLayoutManager
if (mLayoutManager instanceof GridLayoutManager) {
GridLayoutManager gridLayoutManager = (GridLayoutManager) mLayoutManager;
if (firstItemPosition + gridLayoutManager.getSpanCount() < list.size()) {
for (int i = firstItemPosition + 1; i <= firstItemPosition + gridLayoutManager.getSpanCount(); i++) {
processScroll(linearLayoutManager, list, i, wrapperTitle);
}
}
} else { // LinearLayoutManager
if (firstItemPosition + 1 < list.size()) {
processScroll(linearLayoutManager, list, firstItemPosition + 1, wrapperTitle);
}
}
}
}
private void processScroll(LinearLayoutManager layoutManager, ArrayList<EntityWrapper> list, int position, String title) {
EntityWrapper nextWrapper = list.get(position);
View nextTitleView = layoutManager.findViewByPosition(position);
if (nextTitleView == null) return;
if (nextWrapper.getItemType() == EntityWrapper.TYPE_TITLE) {
if (nextTitleView.getTop() <= mStickyViewHolder.itemView.getHeight() && title != null) {
mStickyViewHolder.itemView.setTranslationY(nextTitleView.getTop() - mStickyViewHolder.itemView.getHeight());
}
if (INVISIBLE == nextTitleView.getVisibility()) {
//特殊情况:手指向下滑动的时候,需要及时把成为第二个可见View的TitleView设置Visible,
// 这样才能配合StickyView制造两个TitleView切换的动画。
nextTitleView.setVisibility(VISIBLE);
}
return;
} else if (mStickyViewHolder.itemView.getTranslationY() != 0) {
mStickyViewHolder.itemView.setTranslationY(0);
}
return;
}
private void stickyNewViewHolder(String wrapperTitle) {
if ((wrapperTitle != null && !wrapperTitle.equals(mStickyTitle))) {
if (mStickyViewHolder.itemView.getVisibility() != VISIBLE) {
mStickyViewHolder.itemView.setVisibility(VISIBLE);
}
mStickyTitle = wrapperTitle;
mIndexableAdapter.onBindTitleViewHolder(mStickyViewHolder, wrapperTitle);
}
}
private <T extends IndexableEntity> void initStickyView(final IndexableAdapter<T> adapter) {
mStickyViewHolder = adapter.onCreateTitleViewHolder(mRecy);
mStickyViewHolder.itemView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (adapter.getOnItemTitleClickListener() != null) {
int position = mIndexBar.getFirstRecyclerViewPositionBySelection();
ArrayList<EntityWrapper> datas = mRealAdapter.getItems();
if (datas.size() > position && position >= 0) {
adapter.getOnItemTitleClickListener().onItemClick(
v, position, datas.get(position).getIndexTitle());
}
}
}
});
mStickyViewHolder.itemView.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (adapter.getOnItemTitleLongClickListener() != null) {
int position = mIndexBar.getFirstRecyclerViewPositionBySelection();
ArrayList<EntityWrapper> datas = mRealAdapter.getItems();
if (datas.size() > position && position >= 0) {
return adapter.getOnItemTitleLongClickListener().onItemLongClick(
v, position, datas.get(position).getIndexTitle());
}
}
return false;
}
});
for (int i = 0; i < getChildCount(); i++) {
if (getChildAt(i) == mRecy) {
mStickyViewHolder.itemView.setVisibility(INVISIBLE);
addView(mStickyViewHolder.itemView, i + 1);
return;
}
}
}
private void showOverlayView(float y, final int touchPos) {
if (mIndexBar.getIndexList().size() <= touchPos) return;
if (mMDOverlay != null) {
if (mMDOverlay.getVisibility() != VISIBLE) {
mMDOverlay.setVisibility(VISIBLE);
}
if (y < PADDING_RIGHT_OVERLAY - mIndexBar.getTop() && y >= 0) {
y = PADDING_RIGHT_OVERLAY - mIndexBar.getTop();
} else if (y < 0) {
if (mIndexBar.getTop() > PADDING_RIGHT_OVERLAY) {
y = 0;
} else {
y = PADDING_RIGHT_OVERLAY - mIndexBar.getTop();
}
} else if (y > mIndexBar.getHeight()) {
y = mIndexBar.getHeight();
}
mMDOverlay.setY(mIndexBar.getTop() + y - PADDING_RIGHT_OVERLAY);
String index = mIndexBar.getIndexList().get(touchPos);
if (!mMDOverlay.getText().equals(index)) {
if (index.length() > 1) {
mMDOverlay.setTextSize(30);
}
mMDOverlay.setText(index);
}
}
if (mCenterOverlay != null) {
if (mCenterOverlay.getVisibility() != VISIBLE) {
mCenterOverlay.setVisibility(VISIBLE);
}
String index = mIndexBar.getIndexList().get(touchPos);
if (!mCenterOverlay.getText().equals(index)) {
if (index.length() > 1) {
mCenterOverlay.setTextSize(32);
}
mCenterOverlay.setText(index);
}
}
}
private void initCenterOverlay() {
mCenterOverlay = new TextView(mContext);
mCenterOverlay.setBackgroundResource(R.drawable.indexable_bg_center_overlay);
mCenterOverlay.setTextColor(Color.WHITE);
mCenterOverlay.setTextSize(40);
mCenterOverlay.setGravity(Gravity.CENTER);
int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 70, getResources().getDisplayMetrics());
LayoutParams params = new LayoutParams(size, size);
params.gravity = Gravity.CENTER;
mCenterOverlay.setLayoutParams(params);
mCenterOverlay.setVisibility(INVISIBLE);
addView(mCenterOverlay);
}
private void initMDOverlay(int color) {
mMDOverlay = new AppCompatTextView(mContext);
mMDOverlay.setBackgroundResource(R.drawable.indexable_bg_md_overlay);
((AppCompatTextView) mMDOverlay).setSupportBackgroundTintList(ColorStateList.valueOf(color));
mMDOverlay.setSingleLine();
mMDOverlay.setTextColor(Color.WHITE);
mMDOverlay.setTextSize(38);
mMDOverlay.setGravity(Gravity.CENTER);
int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 72, getResources().getDisplayMetrics());
LayoutParams params = new LayoutParams(size, size);
params.rightMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 33, getResources().getDisplayMetrics());
params.gravity = Gravity.END;
mMDOverlay.setLayoutParams(params);
mMDOverlay.setVisibility(INVISIBLE);
addView(mMDOverlay);
}
void onDataChanged() {
if (mFuture != null) {
mFuture.cancel(true);
}
mFuture = mExecutorService.submit(new Runnable() {
@Override
public void run() {
final ArrayList<EntityWrapper> datas = transform(mIndexableAdapter.getItems());
if (datas == null) return;
getSafeHandler().post(new Runnable() {
@Override
public void run() {
mRealAdapter.setDatas(datas);
mIndexBar.setDatas(mShowAllLetter, mRealAdapter.getItems());
if (mIndexableAdapter.getIndexCallback() != null) {
mIndexableAdapter.getIndexCallback().onFinished(datas);
}
processScrollListener();
}
});
}
});
}
/**
* List<T> -> List<Indexable<T>
*/
private <T extends IndexableEntity> ArrayList<EntityWrapper<T>> transform(final List<T> datas) {
try {
TreeMap<String, List<EntityWrapper<T>>> map = new TreeMap<>(new Comparator<String>() {
@Override
public int compare(String lhs, String rhs) {
if (lhs.equals(INDEX_SIGN)) {
return rhs.equals(INDEX_SIGN) ? 0 : 1;
} else if (rhs.equals(INDEX_SIGN)) {
return -1;
}
return lhs.compareTo(rhs);
}
});
for (int i = 0; i < datas.size(); i++) {
EntityWrapper<T> entity = new EntityWrapper<>();
T item = datas.get(i);
String indexName = item.getFieldIndexBy();
String pinyin = PinyinUtil.getPingYin(indexName);
entity.setPinyin(pinyin);
// init EntityWrapper
if (PinyinUtil.matchingLetter(pinyin)) {
entity.setIndex(pinyin.substring(0, 1).toUpperCase());
entity.setIndexByField(item.getFieldIndexBy());
} else if (PinyinUtil.matchingPolyphone(pinyin)) {
entity.setIndex(PinyinUtil.gePolyphoneInitial(pinyin).toUpperCase());
entity.setPinyin(PinyinUtil.getPolyphoneRealPinyin(pinyin));
String hanzi = PinyinUtil.getPolyphoneRealHanzi(indexName);
entity.setIndexByField(hanzi);
// 把多音字的真实indexField重新赋值
item.setFieldIndexBy(hanzi);
} else {
entity.setIndex(INDEX_SIGN);
entity.setIndexByField(item.getFieldIndexBy());
}
entity.setIndexTitle(entity.getIndex());
entity.setData(item);
entity.setOriginalPosition(i);
item.setFieldPinyinIndexBy(entity.getPinyin());
String inital = entity.getIndex();
List<EntityWrapper<T>> list;
if (!map.containsKey(inital)) {
list = new ArrayList<>();
list.add(new EntityWrapper<T>(entity.getIndex(), EntityWrapper.TYPE_TITLE));
map.put(inital, list);
} else {
list = map.get(inital);
}
list.add(entity);
}
ArrayList<EntityWrapper<T>> list = new ArrayList<>();
for (List<EntityWrapper<T>> indexableEntities : map.values()) {
if (mComparator != null) {
Collections.sort(indexableEntities, mComparator);
} else {
Comparator comparator;
if (mCompareMode == MODE_FAST) {
comparator = new InitialComparator<T>();
Collections.sort(indexableEntities, comparator);
} else if (mCompareMode == MODE_ALL_LETTERS) {
comparator = new PinyinComparator<T>();
Collections.sort(indexableEntities, comparator);
}
}
list.addAll(indexableEntities);
}
return list;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private Handler getSafeHandler() {
if (mHandler == null) {
mHandler = new Handler(Looper.getMainLooper());
}
return mHandler;
}
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/InitialComparator.java
================================================
package me.yokeyword.indexablerv;
import java.util.Comparator;
/**
* Created by YoKey on 16/10/14.
*/
class InitialComparator<T extends IndexableEntity> implements Comparator<EntityWrapper<T>> {
@Override
public int compare(EntityWrapper<T> lhs, EntityWrapper<T> rhs) {
return lhs.getIndex().compareTo(rhs.getIndex());
}
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/PinyinComparator.java
================================================
package me.yokeyword.indexablerv;
import android.support.annotation.NonNull;
import java.util.Comparator;
/**
* Created by YoKey on 16/10/7.
*/
class PinyinComparator<T extends IndexableEntity> implements Comparator<EntityWrapper<T>> {
@Override
public int compare(EntityWrapper<T> lhs, EntityWrapper<T> rhs) {
String lhsIndexName = lhs.getIndexByField();
String rhsIndexName = rhs.getIndexByField();
if (lhsIndexName == null) {
lhsIndexName = "";
}
if (rhsIndexName == null) {
rhsIndexName = "";
}
return compareIndexName(lhsIndexName.trim(), rhsIndexName.trim());
}
private int compareIndexName(String lhs, String rhs) {
int index = 0;
String lhsWord = getWord(lhs, index);
String rhsWord = getWord(rhs, index);
while (lhsWord.equals(rhsWord) && !lhsWord.equals("")) {
index++;
lhsWord = getWord(lhs, index);
rhsWord = getWord(rhs, index);
}
return lhsWord.compareTo(rhsWord);
}
@NonNull
private String getWord(String indexName, int index) {
if (indexName.length() < (index + 1)) return "";
String firstWord;
if (PinyinUtil.matchingPolyphone(indexName)) {
firstWord = PinyinUtil.getPingYin(PinyinUtil.getPolyphoneRealHanzi(indexName).substring(index, index + 1));
} else {
firstWord = PinyinUtil.getPingYin(indexName.substring(index, index + 1));
}
return firstWord;
}
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/PinyinUtil.java
================================================
package me.yokeyword.indexablerv;
import com.github.promeg.pinyinhelper.Pinyin;
import java.util.regex.Pattern;
/**
* Created by YoKey on 16/3/20.
*/
public class PinyinUtil {
private static final String PATTERN_POLYPHONE = "^#[a-zA-Z]+#.+";
private static final String PATTERN_LETTER = "^[a-zA-Z].*+";
/**
* Chinese character -> Pinyin
*/
public static String getPingYin(String inputString) {
if (inputString == null) return "";
return Pinyin.toPinyin(inputString, "").toLowerCase();
}
/**
* Are start with a letter
*
* @return if return false, index should be #
*/
static boolean matchingLetter(String inputString) {
return Pattern.matches(PATTERN_LETTER, inputString);
}
static boolean matchingPolyphone(String inputString) {
return Pattern.matches(PATTERN_POLYPHONE, inputString);
}
static String gePolyphoneInitial(String inputString) {
return inputString.substring(1, 2);
}
static String getPolyphoneRealPinyin(String inputString) {
String[] splits = inputString.split("#");
return splits[1];
}
static String getPolyphoneRealHanzi(String inputString) {
String[] splits = inputString.split("#");
return splits[2];
}
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/RealAdapter.java
================================================
package me.yokeyword.indexablerv;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
/**
* Created by YoKey on 16/10/6.
*/
@SuppressWarnings("unchecked")
class RealAdapter<T extends IndexableEntity> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private ArrayList<EntityWrapper<T>> mDatasList = new ArrayList<>();
private ArrayList<EntityWrapper<T>> mDatas;
private ArrayList<EntityWrapper<T>> mHeaderDatasList = new ArrayList<>();
private ArrayList<EntityWrapper<T>> mFooterDatasList = new ArrayList<>();
private IndexableAdapter<T> mAdapter;
private SparseArray<IndexableHeaderAdapter> mHeaderAdapterMap = new SparseArray<>();
private SparseArray<IndexableFooterAdapter> mFooterAdapterMap = new SparseArray<>();
private IndexableAdapter.OnItemTitleClickListener mTitleClickListener;
private IndexableAdapter.OnItemContentClickListener<T> mContentClickListener;
private IndexableAdapter.OnItemTitleLongClickListener mTitleLongClickListener;
private IndexableAdapter.OnItemContentLongClickListener<T> mContentLongClickListener;
void setIndexableAdapter(IndexableAdapter<T> adapter) {
this.mAdapter = adapter;
}
void addIndexableHeaderAdapter(IndexableHeaderAdapter adapter) {
mHeaderDatasList.addAll(0, adapter.getDatas());
mDatasList.addAll(0, adapter.getDatas());
mHeaderAdapterMap.put(adapter.getItemViewType(), adapter);
notifyDataSetChanged();
}
void removeIndexableHeaderAdapter(IndexableHeaderAdapter adapter) {
mHeaderDatasList.removeAll(adapter.getDatas());
if (mDatasList.size() > 0) {
mDatasList.removeAll(adapter.getDatas());
}
mHeaderAdapterMap.remove(adapter.getItemViewType());
notifyDataSetChanged();
}
void addIndexableFooterAdapter(IndexableFooterAdapter adapter) {
mFooterDatasList.addAll(adapter.getDatas());
mDatasList.addAll(adapter.getDatas());
mFooterAdapterMap.put(adapter.getItemViewType(), adapter);
notifyDataSetChanged();
}
void removeIndexableFooterAdapter(IndexableFooterAdapter adapter) {
mFooterDatasList.removeAll(adapter.getDatas());
if (mDatasList.size() > 0) {
mDatasList.removeAll(adapter.getDatas());
}
mFooterAdapterMap.remove(adapter.getItemViewType());
notifyDataSetChanged();
}
void setDatas(ArrayList<EntityWrapper<T>> datas) {
if (mDatas != null && mDatasList.size() > mHeaderDatasList.size() + mFooterDatasList.size()) {
mDatasList.removeAll(mDatas);
}
this.mDatas = datas;
mDatasList.addAll(mHeaderDatasList.size(), datas);
notifyDataSetChanged();
}
ArrayList<EntityWrapper<T>> getItems() {
return mDatasList;
}
@Override
public int getItemViewType(int position) {
return mDatasList.get(position).getItemType();
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, final int viewType) {
final RecyclerView.ViewHolder holder;
if (viewType == EntityWrapper.TYPE_TITLE) {
holder = mAdapter.onCreateTitleViewHolder(parent);
} else if (viewType == EntityWrapper.TYPE_CONTENT) {
holder = mAdapter.onCreateContentViewHolder(parent);
} else {
AbstractHeaderFooterAdapter adapter;
if (mHeaderAdapterMap.indexOfKey(viewType) >= 0) {
adapter = mHeaderAdapterMap.get(viewType);
} else {
adapter = mFooterAdapterMap.get(viewType);
}
holder = adapter.onCreateContentViewHolder(parent);
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
if (position == RecyclerView.NO_POSITION) return;
EntityWrapper<T> wrapper = mDatasList.get(position);
if (viewType == EntityWrapper.TYPE_TITLE) {
if (mTitleClickListener != null) {
mTitleClickListener.onItemClick(v, position, wrapper.getIndexTitle());
}
} else if (viewType == EntityWrapper.TYPE_CONTENT) {
if (mContentClickListener != null) {
mContentClickListener.onItemClick(v, wrapper.getOriginalPosition(), position, wrapper.getData());
}
} else {
AbstractHeaderFooterAdapter adapter;
if (mHeaderAdapterMap.indexOfKey(viewType) >= 0) {
adapter = mHeaderAdapterMap.get(viewType);
} else {
adapter = mFooterAdapterMap.get(viewType);
}
if (adapter != null) {
AbstractHeaderFooterAdapter.OnItemClickListener listener = adapter.getOnItemClickListener();
if (listener != null) {
listener.onItemClick(v, position, wrapper.getData());
}
}
}
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
int position = holder.getAdapterPosition();
EntityWrapper<T> wrapper = mDatasList.get(position);
if (viewType == EntityWrapper.TYPE_TITLE) {
if (mTitleLongClickListener != null) {
return mTitleLongClickListener.onItemLongClick(v, position, wrapper.getIndexTitle());
} else {
return true;
}
} else if (viewType == EntityWrapper.TYPE_CONTENT) {
if (mContentLongClickListener != null) {
return mContentLongClickListener.onItemLongClick(v, wrapper.getOriginalPosition(), position, wrapper.getData());
} else {
return true;
}
} else {
AbstractHeaderFooterAdapter adapter;
if (mHeaderAdapterMap.indexOfKey(viewType) >= 0) {
adapter = mHeaderAdapterMap.get(viewType);
} else {
adapter = mFooterAdapterMap.get(viewType);
}
if (adapter != null) {
AbstractHeaderFooterAdapter.OnItemLongClickListener listener = adapter.getOnItemLongClickListener();
if (listener != null) {
return listener.onItemLongClick(v, position, wrapper.getData());
}
}
}
return false;
}
});
return holder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
EntityWrapper<T> item = mDatasList.get(position);
int viewType = getItemViewType(position);
if (viewType == EntityWrapper.TYPE_TITLE) {
if (View.INVISIBLE == holder.itemView.getVisibility()) {
holder.itemView.setVisibility(View.VISIBLE);
}
mAdapter.onBindTitleViewHolder(holder, item.getIndexTitle());
} else if (viewType == EntityWrapper.TYPE_CONTENT) {
mAdapter.onBindContentViewHolder(holder, item.getData());
} else {
AbstractHeaderFooterAdapter adapter;
if (mHeaderAdapterMap.indexOfKey(viewType) >= 0) {
adapter = mHeaderAdapterMap.get(viewType);
} else {
adapter = mFooterAdapterMap.get(viewType);
}
adapter.onBindContentViewHolder(holder, item.getData());
}
}
@Override
public int getItemCount() {
return mDatasList.size();
}
void setOnItemTitleClickListener(IndexableAdapter.OnItemTitleClickListener listener) {
this.mTitleClickListener = listener;
}
void setOnItemContentClickListener(IndexableAdapter.OnItemContentClickListener<T> listener) {
this.mContentClickListener = listener;
}
void setOnItemTitleLongClickListener(IndexableAdapter.OnItemTitleLongClickListener listener) {
this.mTitleLongClickListener = listener;
}
void setOnItemContentLongClickListener(IndexableAdapter.OnItemContentLongClickListener<T> listener) {
this.mContentLongClickListener = listener;
}
void addHeaderFooterData(boolean header, EntityWrapper preData, EntityWrapper data) {
processAddHeaderFooterData(header ? mHeaderDatasList : mFooterDatasList, preData, data);
}
private void processAddHeaderFooterData(ArrayList<EntityWrapper<T>> list, EntityWrapper preData, EntityWrapper data) {
for (int i = 0; i < list.size(); i++) {
EntityWrapper wrapper = list.get(i);
if (wrapper == preData) {
int index = i + 1;
list.add(index, data);
if (list == mFooterDatasList) {
index += mDatasList.size() - mFooterDatasList.size() + 1;
}
mDatasList.add(index, data);
notifyItemInserted(i + 1);
return;
}
}
}
void removeHeaderFooterData(boolean header, EntityWrapper data) {
processremoveHeaderFooterData(header ? mHeaderDatasList : mFooterDatasList, data);
}
private void processremoveHeaderFooterData(ArrayList<EntityWrapper<T>> list, EntityWrapper data) {
for (int i = 0; i < list.size(); i++) {
EntityWrapper wrapper = list.get(i);
if (wrapper == data) {
list.remove(data);
mDatasList.remove(data);
notifyItemRemoved(i);
return;
}
}
}
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/SimpleFooterAdapter.java
================================================
package me.yokeyword.indexablerv;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
import java.util.List;
/**
* 该HeaderAdapter 接收一个IndexableAdapter, 使其布局以及点击事件和IndexableAdapter一致
* Created by YoKey on 16/10/14.
*/
public class SimpleFooterAdapter<T extends IndexableEntity> extends IndexableFooterAdapter<T> {
private IndexableAdapter<T> mAdapter;
public SimpleFooterAdapter(IndexableAdapter<T> adapter, String index, String indexTitle, List<T> datas) {
super(index, indexTitle, datas);
this.mAdapter = adapter;
}
@Override
public int getItemViewType() {
return EntityWrapper.TYPE_CONTENT;
}
@Override
public RecyclerView.ViewHolder onCreateContentViewHolder(ViewGroup parent) {
return mAdapter.onCreateContentViewHolder(parent);
}
@Override
public void onBindContentViewHolder(RecyclerView.ViewHolder holder, T entity) {
mAdapter.onBindContentViewHolder(holder, entity);
}
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/SimpleHeaderAdapter.java
================================================
package me.yokeyword.indexablerv;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
import java.util.List;
/**
* 该HeaderAdapter 接收一个IndexableAdapter, 使其布局以及点击事件和IndexableAdapter一致
* Created by YoKey on 16/10/8.
*/
public class SimpleHeaderAdapter<T extends IndexableEntity> extends IndexableHeaderAdapter<T> {
private IndexableAdapter<T> mAdapter;
public SimpleHeaderAdapter(IndexableAdapter<T> adapter, String index, String indexTitle, List<T> datas) {
super(index, indexTitle, datas);
this.mAdapter = adapter;
}
@Override
public int getItemViewType() {
return EntityWrapper.TYPE_CONTENT;
}
@Override
public RecyclerView.ViewHolder onCreateContentViewHolder(ViewGroup parent) {
return mAdapter.onCreateContentViewHolder(parent);
}
@Override
public void onBindContentViewHolder(RecyclerView.ViewHolder holder, T entity) {
mAdapter.onBindContentViewHolder(holder, entity);
}
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/database/DataObservable.java
================================================
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.yokeyword.indexablerv.database;
import android.database.Observable;
/**
* A specialization of {@link Observable} for {@link DataObserver}
* that provides methods for sending notifications to a list of
* {@link DataObserver} objects.
*/
public class DataObservable extends Observable<DataObserver> {
/**
* Invokes {@link DataObserver#onInited()} on each observer.
* Called when the data set is no longer valid and cannot be queried again,
* such as when the data set has been closed.
*/
public void notifyInited() {
synchronized (mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onInited();
}
}
}
/**
* Invokes {@link DataObserver#onChanged} on each observer.
* Called when the contents of the data set have changed. The recipient
* will obtain the new contents the next time it queries the data set.
*/
public void notifyChanged() {
synchronized (mObservers) {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
/**
* Invokes {@link DataObserver#onSetListener(int)} on each observer.
* Called when the data set is no longer valid and cannot be queried again,
* such as when the data set has been closed.
*/
public void notifySetListener(int type) {
synchronized (mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onSetListener(type);
}
}
}
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/database/DataObserver.java
================================================
package me.yokeyword.indexablerv.database;
/**
* Created by YoKey on 16/10/13.
*/
public class DataObserver {
/**
* This method is called when the entire data set has changed,
* init datas
*/
public void onInited() {
// Do nothing
}
/**
* This method is called when the entire data set has changed,
* refresh UI
*/
public void onChanged() {
// Do nothing
}
/**
* This method is called when the entire data becomes invalid,
* setListener
*/
public void onSetListener(int type) {
// Do nothing
}
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/database/HeaderFooterDataObservable.java
================================================
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.yokeyword.indexablerv.database;
import android.database.Observable;
/**
* A specialization of {@link Observable} for {@link DataObserver}
* that provides methods for sending notifications to a list of
* {@link DataObserver} objects.
*/
public class HeaderFooterDataObservable extends Observable<HeaderFooterDataObserver> {
/**
* Invokes {@link DataObserver#onChanged} on each observer.
* Called when the contents of the data set have changed. The recipient
* will obtain the new contents the next time it queries the data set.
*/
public void notifyChanged() {
synchronized (mObservers) {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
public void notifyAdd(boolean header, Object preData, Object data) {
synchronized (mObservers) {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onAdd(header, preData, data);
}
}
}
public void notifyRemove(boolean header, Object object) {
synchronized (mObservers) {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onRemove(header, object);
}
}
}
//
// public void notifyAddAll(Object preData, Object data) {
// synchronized (mObservers) {
// // since onChanged() is implemented by the app, it could do anything, including
// // removing itself from {@link mObservers} - and that could cause problems if
// // an iterator is used on the ArrayList {@link mObservers}.
// // to avoid such problems, just march thru the list in the reverse order.
// for (int i = mObservers.size() - 1; i >= 0; i--) {
// mObservers.get(i).onAddAll(itemType, position, datas);
// }
// }
// }
//
// public void notifyRemoveAll(Collection datas) {
// synchronized (mObservers) {
// // since onChanged() is implemented by the app, it could do anything, including
// // removing itself from {@link mObservers} - and that could cause problems if
// // an iterator is used on the ArrayList {@link mObservers}.
// // to avoid such problems, just march thru the list in the reverse order.
// for (int i = mObservers.size() - 1; i >= 0; i--) {
// mObservers.get(i).onRemoveAll(itemType, datas);
// }
// }
// }
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/database/HeaderFooterDataObserver.java
================================================
package me.yokeyword.indexablerv.database;
/**
* Created by YoKey on 16/10/13.
*/
public class HeaderFooterDataObserver<T> {
/**
* This method is called when the entire data set has changed,
* refresh UI
*/
public void onChanged() {
// Do nothing
}
public void onAdd(boolean header, T preData, T data) {
// Do nothing
}
public void onRemove(boolean header, T object) {
// Do nothing
}
//
// public void onAddAll(int itemType, int position, Collection<T> datas) {
// // Do nothing
// }
//
// public void onRemoveAll(int itemType, Collection<T> datas) {
// // Do nothing
// }
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/database/IndexBarDataObservable.java
================================================
package me.yokeyword.indexablerv.database;
import android.database.Observable;
/**
* Created by Sun on 16/10/13.
*/
public class IndexBarDataObservable extends Observable<IndexBarDataObserver> {
public void notifyChanged() {
synchronized (mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
}
================================================
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/database/IndexBarDataObserver.java
================================================
package me.yokeyword.indexablerv.database;
/**
* Created by Sun on 16/10/13.
*/
public class IndexBarDataObserver {
/**
* This method is called when the entire data set has changed,
* refresh UI
*/
public void onChanged() {
// Do nothing
}
}
================================================
FILE: indexablerecyclerview/src/main/res/drawable/indexable_bg_center_overlay.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#70000000"/>
<corners android:radius="4dp"/>
</shape>
================================================
FILE: indexablerecyclerview/src/main/res/values/indexable_attrs.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="IndexableRecyclerView">
<attr name="indexBar_selectedTextColor" format="color" />
<attr name="indexBar_textColor" format="color" />
<attr name="indexBar_textSize" format="dimension" />
<attr name="indexBar_textSpace" format="dimension" />
<attr name="indexBar_background" format="reference|color" />
<attr name="indexBar_layout_width" format="dimension">
<!--<enum name="wrap_content" value="-2" /> 暂不支持 -->
</attr>
</declare-styleable>
</resources>
================================================
FILE: indexablerecyclerview/src/main/res/values/indexable_default.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="default_indexBar_textColor">#8c8c8c</color>
<dimen name="default_indexBar_textSize">14sp</dimen>
<color name="default_indexBar_selectedTextColor">#f33737</color>
<dimen name="default_indexBar_textSpace">4dp</dimen>
<drawable name="dafault_indexBar_background">@android:color/transparent</drawable>
<dimen name="default_indexBar_layout_width">24dp</dimen>
</resources>
================================================
FILE: indexablerecyclerview/src/main/res/values/indexable_strings.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="indexable_letter">
<item>A</item>
<item>B</item>
<item>C</item>
<item>D</item>
<item>E</item>
<item>F</item>
<item>G</item>
<item>H</item>
<item>I</item>
<item>J</item>
<item>K</item>
<item>L</item>
<item>M</item>
<item>N</item>
<item>O</item>
<item>P</item>
<item>Q</item>
<item>R</item>
<item>S</item>
<item>T</item>
<item>U</item>
<item>V</item>
<item>W</item>
<item>X</item>
<item>Y</item>
<item>Z</item>
</string-array>
</resources>
================================================
FILE: sample/.gitignore
================================================
/build
================================================
FILE: sample/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "me.yokeyword.indexablecv.sample"
minSdkVersion 14
targetSdkVersion 24
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:recyclerview-v7:25.3.1'
compile project(':indexablerecyclerview')
}
================================================
FILE: sample/proguard-rules.pro
================================================
================================================
FILE: sample/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="me.yokeyword.sample">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".city.PickCityActivity"
android:screenOrientation="portrait" />
<activity
android:name=".contact.PickContactActivity"
android:screenOrientation="portrait" />
</application>
</manifest>
================================================
FILE: sample/src/main/java/me/yokeyword/sample/MainActivity.java
================================================
package me.yokeyword.sample;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import me.yokeyword.sample.city.PickCityActivity;
import me.yokeyword.sample.contact.PickContactActivity;
/**
* Created by YoKey on 16/10/7.
*/
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_pick_city).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, PickCityActivity.class));
}
});
findViewById(R.id.btn_pick_contact).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, PickContactActivity.class));
}
});
}
}
================================================
FILE: sample/src/main/java/me/yokeyword/sample/ToastUtil.java
================================================
package me.yokeyword.sample;
import android.content.Context;
import android.widget.Toast;
/**
* Created by YoKey on 16/10/9.
*/
public class ToastUtil {
private static Toast toast;
/**
* 短时间显示Toast
*
* @param context
* @param message
*/
public static Toast showShort(Context context, CharSequence message) {
if (null == toast) {
toast = Toast.makeText(context, message, Toast.LENGTH_SHORT);
} else {
toast.setText(message);
}
toast.show();
return toast;
}
}
================================================
FILE: sample/src/main/java/me/yokeyword/sample/city/CityAdapter.java
================================================
package me.yokeyword.sample.city;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import me.yokeyword.indexablerv.IndexableAdapter;
import me.yokeyword.sample.R;
/**
* Created by YoKey on 16/10/7.
*/
public class CityAdapter extends IndexableAdapter<CityEntity> {
private LayoutInflater mInflater;
public CityAdapter(Context context) {
mInflater = LayoutInflater.from(context);
}
@Override
public RecyclerView.ViewHolder onCreateTitleViewHolder(ViewGroup parent) {
View view = mInflater.inflate(R.layout.item_index_city, parent, false);
return new IndexVH(view);
}
@Override
public RecyclerView.ViewHolder onCreateContentViewHolder(ViewGroup parent) {
View view = mInflater.inflate(R.layout.item_city, parent, false);
return new ContentVH(view);
}
@Override
public void onBindTitleViewHolder(RecyclerView.ViewHolder holder, String indexTitle) {
IndexVH vh = (IndexVH) holder;
vh.tv.setText(indexTitle);
}
@Override
public void onBindContentViewHolder(RecyclerView.ViewHolder holder, CityEntity entity) {
ContentVH vh = (ContentVH) holder;
vh.tv.setText(entity.getName());
}
private class IndexVH extends RecyclerView.ViewHolder {
TextView tv;
public IndexVH(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv_index);
}
}
private class ContentVH extends RecyclerView.ViewHolder {
TextView tv;
public ContentVH(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv_name);
}
}
}
================================================
FILE: sample/src/main/java/me/yokeyword/sample/city/CityEntity.java
================================================
package me.yokeyword.sample.city;
import me.yokeyword.indexablerv.IndexableEntity;
/**
* Created by YoKey on 16/10/7.
*/
public class CityEntity implements IndexableEntity {
private long id;
private String name;
private String pinyin;
public CityEntity() {
}
public CityEntity(String name) {
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPinyin() {
return pinyin;
}
public void setPinyin(String pinyin) {
this.pinyin = pinyin;
}
@Override
public String getFieldIndexBy() {
return name;
}
@Override
public void setFieldIndexBy(String indexByField) {
this.name = indexByField;
}
@Override
public void setFieldPinyinIndexBy(String pinyin) {
this.pinyin = pinyin;
}
}
================================================
FILE: sample/src/main/java/me/yokeyword/sample/city/PickCityActivity.java
================================================
package me.yokeyword.sample.city;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.SearchView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.Toast;
import com.github.promeg.pinyinhelper.Pinyin;
import com.github.promeg.tinypinyin.lexicons.android.cncity.CnCityDict;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import me.yokeyword.indexablerv.EntityWrapper;
import me.yokeyword.indexablerv.IndexableAdapter;
import me.yokeyword.indexablerv.IndexableLayout;
import me.yokeyword.indexablerv.SimpleHeaderAdapter;
import me.yokeyword.sample.R;
import me.yokeyword.sample.ToastUtil;
/**
* 选择城市
* Created by YoKey on 16/10/7.
*/
public class PickCityActivity extends AppCompatActivity {
private List<CityEntity> mDatas;
private SearchFragment mSearchFragment;
private SearchView mSearchView;
private FrameLayout mProgressBar;
private SimpleHeaderAdapter mHotCityAdapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pick_city);
getSupportActionBar().setTitle("选择城市");
mSearchFragment = (SearchFragment) getSupportFragmentManager().findFragmentById(R.id.search_fragment);
IndexableLayout indexableLayout = (IndexableLayout) findViewById(R.id.indexableLayout);
mSearchView = (SearchView) findViewById(R.id.searchview);
mProgressBar = (FrameLayout) findViewById(R.id.progress);
// indexableLayout.setLayoutManager(new LinearLayoutManager(this));
indexableLayout.setLayoutManager(new GridLayoutManager(this, 2));
// 多音字处理
Pinyin.init(Pinyin.newConfig().with(CnCityDict.getInstance(this)));
// 添加自定义多音字词典
// Pinyin.init(Pinyin.newConfig()
// .with(new PinyinMapDict() {
// @Override
// public Map<String, String[]> mapping() {
// HashMap<String, String[]> map = new HashMap<String, String[]>();
// map.put("重庆", new String[]{"CHONG", "QING"});
// return map;
// }
// }));
// 快速排序。 排序规则设置为:只按首字母 (默认全拼音排序) 效率很高,是默认的10倍左右。 按需开启~
indexableLayout.setCompareMode(IndexableLayout.MODE_FAST);
// 自定义排序规则
// indexableLayout.setComparator(new Comparator<EntityWrapper<CityEntity>>() {
// @Override
// public int compare(EntityWrapper<CityEntity> lhs, EntityWrapper<CityEntity> rhs) {
// return lhs.getPinyin().compareTo(rhs.getPinyin());
// }
// });
// setAdapter
CityAdapter adapter = new CityAdapter(this);
indexableLayout.setAdapter(adapter);
// set Datas
mDatas = initDatas();
adapter.setDatas(mDatas, new IndexableAdapter.IndexCallback<CityEntity>() {
@Override
public void onFinished(List<EntityWrapper<CityEntity>> datas) {
// 数据处理完成后回调
mSearchFragment.bindDatas(mDatas);
mProgressBar.setVisibility(View.GONE);
}
});
// set Center OverlayView
indexableLayout.setOverlayStyle_Center();
// set Listener
adapter.setOnItemContentClickListener(new IndexableAdapter.OnItemContentClickListener<CityEntity>() {
@Override
public void onItemClick(View v, int originalPosition, int currentPosition, CityEntity entity) {
if (originalPosition >= 0) {
ToastUtil.showShort(PickCityActivity.this, "选中:" + entity.getName() + " 当前位置:" + currentPosition + " 原始所在数组位置:" + originalPosition);
} else {
ToastUtil.showShort(PickCityActivity.this, "选中Header:" + entity.getName() + " 当前位置:" + currentPosition);
}
}
});
adapter.setOnItemTitleClickListener(new IndexableAdapter.OnItemTitleClickListener() {
@Override
public void onItemClick(View v, int currentPosition, String indexTitle) {
ToastUtil.showShort(PickCityActivity.this, "选中:" + indexTitle + " 当前位置:" + currentPosition);
}
});
// 添加 HeaderView DefaultHeaderAdapter接收一个IndexableAdapter, 使其布局以及点击事件和IndexableAdapter一致
// 如果想自定义布局,点击事件, 可传入 new IndexableHeaderAdapter
mHotCityAdapter = new SimpleHeaderAdapter<>(adapter, "热", "热门城市", iniyHotCityDatas());
// 热门城市
indexableLayout.addHeaderAdapter(mHotCityAdapter);
// 定位
final List<CityEntity> gpsCity = iniyGPSCityDatas();
final SimpleHeaderAdapter gpsHeaderAdapter = new SimpleHeaderAdapter<>(adapter, "定", "当前城市", gpsCity);
indexableLayout.addHeaderAdapter(gpsHeaderAdapter);
// 显示真实索引
// indexableLayout.showAllLetter(false);
// 模拟定位
indexableLayout.postDelayed(new Runnable() {
@Override
public void run() {
gpsCity.get(0).setName("杭州市");
gpsHeaderAdapter.notifyDataSetChanged();
}
}, 3000);
// 搜索Demo
initSearch();
}
// 更新数据点击事件
public void update(View view) {
List<CityEntity> list = new ArrayList<>();
list.add(new CityEntity("杭州市"));
list.add(new CityEntity("北京市"));
list.add(new CityEntity("上海市"));
list.add(new CityEntity("广州市"));
mHotCityAdapter.addDatas(list);
Toast.makeText(this, "更新数据", Toast.LENGTH_SHORT).show();
}
private List<CityEntity> initDatas() {
List<CityEntity> list = new ArrayList<>();
List<String> cityStrings = Arrays.asList(getResources().getStringArray(R.array.city_array));
for (String item : cityStrings) {
CityEntity cityEntity = new CityEntity();
cityEntity.setName(item);
list.add(cityEntity);
}
return list;
}
private List<CityEntity> iniyHotCityDatas() {
List<CityEntity> list = new ArrayList<>();
list.add(new CityEntity("杭州市"));
list.add(new CityEntity("北京市"));
list.add(new CityEntity("上海市"));
list.add(new CityEntity("广州市"));
return list;
}
private List<CityEntity> iniyGPSCityDatas() {
List<CityEntity> list = new ArrayList<>();
list.add(new CityEntity("定位中..."));
return list;
}
private void initSearch() {
getSupportFragmentManager().beginTransaction().hide(mSearchFragment).commit();
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
if (newText.trim().length() > 0) {
if (mSearchFragment.isHidden()) {
getSupportFragmentManager().beginTransaction().show(mSearchFragment).commit();
}
} else {
if (!mSearchFragment.isHidden()) {
getSupportFragmentManager().beginTransaction().hide(mSearchFragment).commit();
}
}
mSearchFragment.bindQueryText(newText);
return false;
}
});
}
@Override
public void onBackPressed() {
if (!mSearchFragment.isHidden()) {
// 隐藏 搜索
mSearchView.setQuery(null, false);
getSupportFragmentManager().beginTransaction().hide(mSearchFragment).commit();
return;
}
super.onBackPressed();
}
}
================================================
FILE: sample/src/main/java/me/yokeyword/sample/city/SearchFragment.java
================================================
package me.yokeyword.sample.city;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import me.yokeyword.sample.R;
import me.yokeyword.sample.ToastUtil;
/**
* Demo: 搜索结果显示Fragment
* Created by YoKey on 16/10/9.
*/
public class SearchFragment extends Fragment {
private RecyclerView mRecyclerView;
private TextView mTvNoResult;
private SearchAdapter mAdapter;
private List<CityEntity> mDatas;
private String mQueryText;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_search_city, container, false);
mTvNoResult = (TextView) view.findViewById(R.id.tv_no_result);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recy);
return view;
}
public void bindDatas(List<CityEntity> datas) {
this.mDatas = datas;
mAdapter = new SearchAdapter();
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setAdapter(mAdapter);
if (mQueryText != null) {
mAdapter.getFilter().filter(mQueryText);
}
}
/**
* 根据newText 进行查找, 显示
*/
public void bindQueryText(String newText) {
if (mDatas == null) {
mQueryText = newText.toLowerCase();
} else if (!TextUtils.isEmpty(newText)) {
mAdapter.getFilter().filter(newText.toLowerCase());
}
}
private class SearchAdapter extends RecyclerView.Adapter<SearchAdapter.VH> implements Filterable {
private List<CityEntity> items = new ArrayList<>();
public SearchAdapter() {
items.clear();
items.addAll(mDatas);
}
@Override
public VH onCreateViewHolder(ViewGroup parent, int viewType) {
final VH holder = new VH(LayoutInflater.from(getActivity()).inflate(R.layout.item_city, parent, false));
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
ToastUtil.showShort(getActivity(), "选择了" + items.get(position).getName());
}
});
return holder;
}
@Override
public int getItemCount() {
return items.size();
}
@Override
public void onBindViewHolder(VH holder, int position) {
holder.tvName.setText(items.get(position).getName());
}
@Override
public Filter getFilter() {
return new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
ArrayList<CityEntity> list = new ArrayList<>();
for (CityEntity item : mDatas) {
if (item.getPinyin().startsWith(constraint.toString()) || item.getName().contains(constraint)) {
list.add(item);
}
}
FilterResults results = new FilterResults();
results.count = list.size();
results.values = list;
return results;
}
@Override
@SuppressWarnings("unchecked")
protected void publishResults(CharSequence constraint, FilterResults results) {
ArrayList<CityEntity> list = (ArrayList<CityEntity>) results.values;
items.clear();
items.addAll(list);
if (results.count == 0) {
mTvNoResult.setVisibility(View.VISIBLE);
} else {
mTvNoResult.setVisibility(View.INVISIBLE);
}
notifyDataSetChanged();
}
};
}
class VH extends RecyclerView.ViewHolder {
private TextView tvName;
public VH(View itemView) {
super(itemView);
tvName = (TextView) itemView.findViewById(R.id.tv_name);
}
}
}
}
================================================
FILE: sample/src/main/java/me/yokeyword/sample/contact/ContactAdapter.java
================================================
package me.yokeyword.sample.contact;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import me.yokeyword.indexablerv.IndexableAdapter;
import me.yokeyword.sample.R;
/**
* Created by YoKey on 16/10/8.
*/
public class ContactAdapter extends IndexableAdapter<UserEntity> {
private LayoutInflater mInflater;
public ContactAdapter(Context context) {
mInflater = LayoutInflater.from(context);
}
@Override
public RecyclerView.ViewHolder onCreateTitleViewHolder(ViewGroup parent) {
View view = mInflater.inflate(R.layout.item_index_contact, parent, false);
return new IndexVH(view);
}
@Override
public RecyclerView.ViewHolder onCreateContentViewHolder(ViewGroup parent) {
View view = mInflater.inflate(R.layout.item_contact, parent, false);
return new ContentVH(view);
}
@Override
public void onBindTitleViewHolder(RecyclerView.ViewHolder holder, String indexTitle) {
IndexVH vh = (IndexVH) holder;
vh.tv.setText(indexTitle);
}
@Override
public void onBindContentViewHolder(RecyclerView.ViewHolder holder, UserEntity entity) {
ContentVH vh = (ContentVH) holder;
vh.tvName.setText(entity.getNick());
vh.tvMobile.setText(entity.getMobile());
}
private class IndexVH extends RecyclerView.ViewHolder {
TextView tv;
public IndexVH(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv_index);
}
}
private class ContentVH extends RecyclerView.ViewHolder {
TextView tvName, tvMobile;
public ContentVH(View itemView) {
super(itemView);
tvName = (TextView) itemView.findViewById(R.id.tv_name);
tvMobile = (TextView) itemView.findViewById(R.id.tv_mobile);
}
}
}
================================================
FILE: sample/src/main/java/me/yokeyword/sample/contact/MenuEntity.java
================================================
package me.yokeyword.sample.contact;
/**
* Created by YoKey on 16/10/8.
*/
public class MenuEntity {
private long menuId;
private String menuTitle;
private int menuIconRes;
public MenuEntity(String title, int iconRes) {
this.menuTitle = title;
this.menuIconRes = iconRes;
}
public long getMenuId() {
return menuId;
}
public void setMenuId(long menuId) {
this.menuId = menuId;
}
public String getMenuTitle() {
return menuTitle;
}
public void setMenuTitle(String menuTitle) {
this.menuTitle = menuTitle;
}
public int getMenuIconRes() {
return menuIconRes;
}
public void setMenuIconRes(int menuIconRes) {
this.menuIconRes = menuIconRes;
}
}
================================================
FILE: sample/src/main/java/me/yokeyword/sample/contact/PickContactActivity.java
================================================
package me.yokeyword.sample.contact;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import me.yokeyword.indexablerv.IndexableAdapter;
import me.yokeyword.indexablerv.IndexableHeaderAdapter;
import me.yokeyword.indexablerv.IndexableLayout;
import me.yokeyword.indexablerv.SimpleFooterAdapter;
import me.yokeyword.indexablerv.SimpleHeaderAdapter;
import me.yokeyword.sample.R;
import me.yokeyword.sample.ToastUtil;
/**
* Created by YoKey on 16/10/8.
*/
public class PickContactActivity extends AppCompatActivity {
private ContactAdapter mAdapter;
private MenuHeaderAdapter mMenuHeaderAdapter;
private BannerHeaderAdapter mBannerHeaderAdapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pick_contact);
getSupportActionBar().setTitle("联系人");
IndexableLayout indexableLayout = (IndexableLayout) findViewById(R.id.indexableLayout);
indexableLayout.setLayoutManager(new LinearLayoutManager(this));
// indexableLayout.setLayoutManager(new GridLayoutManager(this, 3));
// setAdapter
mAdapter = new ContactAdapter(this);
indexableLayout.setAdapter(mAdapter);
// set Datas
mAdapter.setDatas(initDatas());
// set Material Design OverlayView
indexableLayout.setOverlayStyle_MaterialDesign(Color.RED);
// 全字母排序。 排序规则设置为:每个字母都会进行比较排序;速度较慢
indexableLayout.setCompareMode(IndexableLayout.MODE_ALL_LETTERS);
// set Listener
mAdapter.setOnItemContentClickListener(new IndexableAdapter.OnItemContentClickListener<UserEntity>() {
@Override
public void onItemClick(View v, int originalPosition, int currentPosition, UserEntity entity) {
if (originalPosition >= 0) {
ToastUtil.showShort(PickContactActivity.this, "选中:" + entity.getNick() + " 当前位置:" + currentPosition + " 原始所在数组位置:" + originalPosition);
} else {
ToastUtil.showShort(PickContactActivity.this, "选中Header/Footer:" + entity.getNick() + " 当前位置:" + currentPosition);
}
}
});
mAdapter.setOnItemTitleClickListener(new IndexableAdapter.OnItemTitleClickListener() {
@Override
public void onItemClick(View v, int currentPosition, String indexTitle) {
ToastUtil.showShort(PickContactActivity.this, "选中:" + indexTitle + " 当前位置:" + currentPosition);
}
});
// 添加我关心的人
indexableLayout.addHeaderAdapter(new SimpleHeaderAdapter<>(mAdapter, "☆", "我关心的", initFavDatas()));
// 构造函数里3个参数,分别对应 (IndexBar的字母索引, IndexTitle, 数据源), 不想显示哪个就传null, 数据源传null时,代表add一个普通的View
mMenuHeaderAdapter = new MenuHeaderAdapter("↑", null, initMenuDatas());
// 添加菜单
indexableLayout.addHeaderAdapter(mMenuHeaderAdapter);
mMenuHeaderAdapter.setOnItemHeaderClickListener(new IndexableHeaderAdapter.OnItemHeaderClickListener<MenuEntity>() {
@Override
public void onItemClick(View v, int currentPosition, MenuEntity entity) {
ToastUtil.showShort(PickContactActivity.this, entity.getMenuTitle());
}
});
// 这里BannerView只有一个Item, 添加一个长度为1的任意List作为第三个参数
List<String> bannerList = new ArrayList<>();
bannerList.add("");
mBannerHeaderAdapter = new BannerHeaderAdapter(null, null, bannerList);
// 添加 Banner
indexableLayout.addHeaderAdapter(mBannerHeaderAdapter);
// FooterView
indexableLayout.addFooterAdapter(new SimpleFooterAdapter<>(mAdapter, "尾", "我是FooterView", initFavDatas()));
}
/**
* 自定义的MenuHeader
*/
class MenuHeaderAdapter extends IndexableHeaderAdapter<MenuEntity> {
private static final int TYPE = 1;
public MenuHeaderAdapter(String index, String indexTitle, List<MenuEntity> datas) {
super(index, indexTitle, datas);
}
@Override
public int getItemViewType() {
return TYPE;
}
@Override
public RecyclerView.ViewHolder onCreateContentViewHolder(ViewGroup parent) {
return new VH(LayoutInflater.from(PickContactActivity.this).inflate(R.layout.header_contact_menu, parent, false));
}
@Override
public void onBindContentViewHolder(RecyclerView.ViewHolder holder, MenuEntity entity) {
VH vh = (VH) holder;
vh.tv.setText(entity.getMenuTitle());
vh.img.setImageResource(entity.getMenuIconRes());
}
private class VH extends RecyclerView.ViewHolder {
private TextView tv;
private ImageView img;
public VH(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv_title);
img = (ImageView) itemView.findViewById(R.id.img);
}
}
}
/**
* 自定义的Banner Header
*/
class BannerHeaderAdapter extends IndexableHeaderAdapter {
private static final int TYPE = 2;
public BannerHeaderAdapter(String index, String indexTitle, List datas) {
super(index, indexTitle, datas);
}
@Override
public int getItemViewType() {
return TYPE;
}
@Override
public RecyclerView.ViewHolder onCreateContentViewHolder(ViewGroup parent) {
View view = LayoutInflater.from(PickContactActivity.this).inflate(R.layout.header_contact_banner, parent, false);
VH holder = new VH(view);
holder.img.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ToastUtil.showShort(PickContactActivity.this, "---点击了Banner---");
}
});
return holder;
}
@Override
public void onBindContentViewHolder(RecyclerView.ViewHolder holder, Object entity) {
// 数据源为null时, 该方法不用实现
}
private class VH extends RecyclerView.ViewHolder {
ImageView img;
public VH(View itemView) {
super(itemView);
img = (ImageView) itemView.findViewById(R.id.img);
}
}
}
private List<UserEntity> initDatas() {
List<UserEntity> list = new ArrayList<>();
// 初始化数据
List<String> contactStrings = Arrays.asList(getResources().getStringArray(R.array.contact_array));
List<String> mobileStrings = Arrays.asList(getResources().getStringArray(R.array.mobile_array));
for (int i = 0; i < contactStrings.size(); i++) {
UserEntity contactEntity = new UserEntity(contactStrings.get(i), mobileStrings.get(i));
list.add(contactEntity);
}
return list;
}
private List<UserEntity> initFavDatas() {
List<UserEntity> list = new ArrayList<>();
list.add(new UserEntity("张三", "10000"));
list.add(new UserEntity("李四", "10001"));
return list;
}
private List<MenuEntity> initMenuDatas() {
List<MenuEntity> list = new ArrayList<>();
list.add(new MenuEntity("新的朋友", R.mipmap.icon_1));
list.add(new MenuEntity("群聊", R.mipmap.icon_2));
list.add(new MenuEntity("标签", R.mipmap.icon_3));
list.add(new MenuEntity("公众号", R.mipmap.icon_4));
return list;
}
}
================================================
FILE: sample/src/main/java/me/yokeyword/sample/contact/UserEntity.java
================================================
package me.yokeyword.sample.contact;
import me.yokeyword.indexablerv.IndexableEntity;
/**
* Created by YoKey on 16/10/8.
*/
public class UserEntity implements IndexableEntity {
private String nick;
private String avatar;
private String mobile;
public UserEntity(String nick, String mobile) {
this.nick = nick;
this.mobile = mobile;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getNick() {
return nick;
}
public void setNick(String name) {
this.nick = name;
}
@Override
public String getFieldIndexBy() {
return nick;
}
@Override
public void setFieldIndexBy(String indexField) {
this.nick = indexField;
}
@Override
public void setFieldPinyinIndexBy(String pinyin) {
// 需要用到拼音时(比如:搜索), 可增添pinyin字段 this.pinyin = pinyin
// 见 CityEntity
}
}
================================================
FILE: sample/src/main/res/layout/activity_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:gravity="center">
<Button
android:id="@+id/btn_pick_city"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="选择城市Demo"/>
<Button
android:id="@+id/btn_pick_contact"
android:layout_width="match_parent"
android:layout_marginTop="32dp"
android:layout_height="wrap_content"
android:text="选择联系人Demo"/>
</LinearLayout>
================================================
FILE: sample/src/main/res/layout/activity_pick_city.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical">
<android.support.v7.widget.SearchView
android:id="@+id/searchview"
android:layout_width="match_parent"
android:layout_height="42dp"
android:layout_marginBottom="4dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
android:background="#f3f3f3"
android:focusable="false"
android:focusableInTouchMode="false"
android:textColor="#757575"
app:iconifiedByDefault="false"
app:queryBackground="@null"
app:queryHint="请输入关键字"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<me.yokeyword.indexablerv.IndexableLayout
android:id="@+id/indexableLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:indexBar_background="#08000000"
app:indexBar_layout_width="24dp"
app:indexBar_textColor="@android:color/black"
app:indexBar_textSpace="8dp"/>
<fragment
android:id="@+id/search_fragment"
class="me.yokeyword.sample.city.SearchFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<FrameLayout
android:id="@+id/progress"
android:layout_width="match_parent"
android:background="@android:color/white"
android:layout_height="match_parent">
<ProgressBar
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_gravity="center"/>
</FrameLayout>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:text="更新数据"
android:onClick="update"/>
</FrameLayout>
</LinearLayout>
================================================
FILE: sample/src/main/res/layout/activity_pick_contact.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<me.yokeyword.indexablerv.IndexableLayout
android:id="@+id/indexableLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
app:indexBar_selectedTextColor="#f33737"
app:indexBar_textColor="@android:color/black"
app:indexBar_textSize="14sp"
app:indexBar_textSpace="4dp" />
</LinearLayout>
================================================
FILE: sample/src/main/res/layout/fragment_search_city.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<android.support.v7.widget.RecyclerView
android:id="@+id/recy"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="@+id/tv_no_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="16dp"
android:text="没有搜索到匹配的内容"
android:textSize="12sp"
android:visibility="gone" />
</FrameLayout>
================================================
FILE: sample/src/main/res/layout/header_contact_banner.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/img"
android:layout_width="match_parent"
android:layout_height="96dp"
android:scaleType="center"
android:alpha="0.6"
android:src="@mipmap/banner" />
</FrameLayout>
================================================
FILE: sample/src/main/res/layout/header_contact_menu.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="?attr/selectableItemBackground">
<ImageView
android:id="@+id/img"
android:layout_width="30dp"
android:layout_centerVertical="true"
tools:src="@mipmap/icon_1"
android:layout_marginLeft="24dp"
android:layout_marginRight="16dp"
android:layout_height="30dp" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginRight="16dp"
android:layout_toRightOf="@id/img"
android:gravity="center_vertical"
android:textColor="@android:color/black"
android:textSize="17sp"
tools:text="张三" />
<ImageView
android:layout_width="match_parent"
android:layout_height="0.8dp"
android:background="#f0f0f0" />
</RelativeLayout>
================================================
FILE: sample/src/main/res/layout/item_city.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="vertical"
android:paddingLeft="16dp">
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="12dp"
android:paddingTop="12dp"
tools:text="合肥市"
android:textSize="16sp" />
<ImageView
android:layout_width="match_parent"
android:layout_height="0.8dp"
android:background="#f1f1f1" />
</LinearLayout>
================================================
FILE: sample/src/main/res/layout/item_contact.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="16dp"
android:background="?attr/selectableItemBackground"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="8dp">
<ImageView
android:id="@+id/img_avatar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="16dp"
android:src="@mipmap/ic_account_circle_grey_400_48dp" />
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/img_avatar"
tools:text="张三"
android:textColor="#373737"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_mobile"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_name"
android:layout_marginTop="12dp"
android:layout_toRightOf="@id/img_avatar"
tools:text="18712345678"
android:textColor="#969696" />
</RelativeLayout>
================================================
FILE: sample/src/main/res/layout/item_index_city.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<TextView
android:id="@+id/tv_index"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#F1F1F1"
android:paddingBottom="8dp"
android:paddingLeft="16dp"
android:paddingTop="8dp"
android:textColor="@color/colorAccent"
android:textSize="13sp"/>
================================================
FILE: sample/src/main/res/layout/item_index_contact.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_index"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#f9f9f9"
android:paddingBottom="8dp"
android:paddingLeft="24dp"
android:paddingTop="8dp"
android:textColor="@color/colorPrimary"
android:textStyle="bold"
android:textSize="14sp" />
<ImageView
android:layout_width="match_parent"
android:layout_height="0.8dp"
android:background="#f0f0f0" />
</FrameLayout>
================================================
FILE: sample/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#f33737</color>
</resources>
================================================
FILE: sample/src/main/res/values/dimens.xml
================================================
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>
================================================
FILE: sample/src/main/res/values/string_city.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="city_array">
<item>北京市</item>
<item>天津市</item>
<item>上海市</item>
<item>重庆市</item>
<item>香港特别行政区</item>
<item>澳门特别行政区</item>
<item>沈阳市</item>
<item>大连市</item>
<item>鞍山市</item>
<item>抚顺市</item>
<item>本溪市</item>
<item>丹东市</item>
<item>锦州市</item>
<item>营口市</item>
<item>阜新市</item>
<item>辽阳市</item>
<item>铁岭市</item>
<item>朝阳市</item>
<item>盘锦市</item>
<item>葫芦岛市</item>
<item>哈尔滨市</item>
<item>齐齐哈尔市</item>
<item>鸡西市</item>
<item>鹤岗市</item>
<item>双鸭山市</item>
<item>大庆市</item>
<item>伊春市</item>
<item>佳木斯市</item>
<item>牡丹江市</item>
<item>七台河市</item>
<item>黑河市</item>
<item>长春市</item>
<item>吉林市</item>
<item>四平市</item>
<item>辽源市</item>
<item>通化市</item>
<item>白山市</item>
<item>松原市</item>
<item>白城市</item>
<item>延吉市</item>
<item>呼和浩特市</item>
<item>包头市</item>
<item>乌海市</item>
<item>赤峰市</item>
<item>通辽市</item>
<item>鄂尔多斯市</item>
<item>呼伦贝尔市</item>
<item>巴彦淖尔市</item>
<item>乌兰察布市</item>
<item>乌兰浩特市</item>
<item>二连浩特市</item>
<item>乌鲁木齐市</item>
<item>克拉玛依市</item>
<item>吐鲁番市</item>
<item>昌吉市</item>
<item>博乐市</item>
<item>库尔勒市</item>
<item>阿克苏市</item>
<item>阿图什市</item>
<item>喀什市</item>
<item>和田市</item>
<item>伊宁市</item>
<item>塔城市</item>
<item>阿勒泰市</item>
<item>西宁市</item>
<item>拉萨市</item>
<item>兰州市</item>
<item>嘉峪关市</item>
<item>金昌市</item>
<item>白银市</item>
<item>天水市</item>
<item>武威市</item>
<item>张掖市</item>
<item>平凉市</item>
<item>酒泉市</item>
<item>庆阳市</item>
<item>定西市</item>
<item>陇南市</item>
<item>临夏市</item>
<item>合作市</item>
<item>银川市</item>
<item>石嘴山市</item>
<item>吴忠市</item>
<item>固原市</item>
<item>中卫市</item>
<item>西安市</item>
<item>铜川市</item>
<item>宝鸡市</item>
<item>咸阳市</item>
<item>渭南市</item>
<item>延安市</item>
<item>汉中市</item>
<item>榆林市</item>
<item>安康市</item>
<item>商洛市</item>
<item>太原市</item>
<item>大同市</item>
<item>阳泉市</item>
<item>长治市</item>
<item>晋城市</item>
<item>朔州市</item>
<item>晋中市</item>
<item>运城市</item>
<item>忻州市</item>
<item>临汾市</item>
<item>吕梁市</item>
<item>石家庄市</item>
<item>唐山市</item>
<item>秦皇岛市</item>
<item>邯郸市</item>
<item>邢台市</item>
<item>保定市</item>
<item>承德市</item>
<item>沧州市</item>
<item>廊坊市</item>
<item>衡水市</item>
<item>济南市</item>
<item>青岛市</item>
<item>淄博市</item>
<item>枣庄市</item>
<item>东营市</item>
<item>烟台市</item>
<item>潍坊市</item>
<item>济宁市</item>
<item>泰安市</item>
<item>威海市</item>
<item>日照市</item>
<item>莱芜市</item>
<item>临沂市</item>
<item>德州市</item>
<item>聊城市</item>
<item>滨州市</item>
<item>菏泽市</item>
<item>南京市</item>
<item>无锡市</item>
<item>徐州市</item>
<item>常州市</item>
<item>苏州市</item>
<item>南通市</item>
<item>连云港市</item>
<item>淮安市</item>
<item>盐城市</item>
<item>扬州市</item>
<item>镇江市</item>
<item>泰州市</item>
<item>宿迁市</item>
<item>合肥市</item>
<item>芜湖市</item>
<item>蚌埠市</item>
<item>淮南市</item>
<item>马鞍山市</item>
<item>淮北市</item>
<item>铜陵市</item>
<item>安庆市</item>
<item>黄山市</item>
<item>滁州市</item>
<item>阜阳市</item>
<item>宿州市</item>
<item>巢湖市</item>
<item>六安市</item>
<item>毫州市</item>
<item>池州市</item>
<item>宣城市</item>
<item>郑州市</item>
<item>开封市</item>
<item>洛阳市</item>
<item>平顶山市</item>
<item>安阳市</item>
<item>鹤壁市</item>
<item>新乡市</item>
<item>焦作市</item>
<item>濮阳市</item>
<item>许昌市</item>
<item>漯河市</item>
<item>三门峡市</item>
<item>南阳市</item>
<item>商丘市</item>
<item>信阳市</item>
<item>周口市</item>
<item>驻马店市</item>
<item>武汉市</item>
<item>黄石市</item>
<item>十堰市</item>
<item>宜昌市</item>
<item>襄樊市</item>
<item>鄂州市</item>
<item>荆门市</item>
<item>孝感市</item>
<item>荆州市</item>
<item>黄冈市</item>
<item>咸宁市</item>
<item>随州市</item>
<item>恩施市</item>
<item>成都市</item>
<item>自贡市</item>
<item>攀枝花市</item>
<item>泸州市</item>
<item>德阳市</item>
<item>绵阳市</item>
<item>广元市</item>
<item>遂宁市</item>
<item>内江市</item>
<item>乐山市</item>
<item>南充市</item>
<item>眉山市</item>
<item>宜宾市</item>
<item>内广安市</item>
<item>达州市</item>
<item>雅安市</item>
<item>巴中市</item>
<item>资阳市</item>
<item>昆明市</item>
<item>曲靖市</item>
<item>玉溪市</item>
<item>保山市</item>
<item>昭通市</item>
<item>丽江市</item>
<item>思茅市</item>
<item>临沧市</item>
<item>贵阳市</item>
<item>六盘水市</item>
<item>遵义市</item>
<item>安顺市</item>
<item>铜仁市</item>
<item>兴义市</item>
<item>毕节市</item>
<item>凯里市</item>
<item>都匀市</item>
<item>长沙市</item>
<item>株洲市</item>
<item>湘潭市</item>
<item>衡阳市</item>
<item>邵阳市</item>
<item>岳阳市</item>
<item>常德市</item>
<item>张家界市</item>
<item>益阳市</item>
<item>郴州市</item>
<item>永州市</item>
<item>怀化市</item>
<item>娄底市</item>
<item>吉首市</item>
<item>南昌市</item>
<item>萍乡市</item>
<item>九江市</item>
<item>新余市</item>
<item>鹰潭市</item>
<item>赣州市</item>
<item>吉安市</item>
<item>宜春市</item>
<item>抚州市</item>
<item>上饶市</item>
<item>杭州市</item>
<item>宁波市</item>
<item>温州市</item>
<item>嘉兴市</item>
<item>湖州市</item>
<item>绍兴市</item>
<item>金华市</item>
<item>衢州市</item>
<item>舟山市</item>
<item>台州市</item>
<item>丽水市</item>
<item>福州市</item>
<item>厦门市</item>
<item>莆田市</item>
<item>三明市</item>
<item>泉州市</item>
<item>漳州市</item>
<item>南平市</item>
<item>龙岩市</item>
<item>宁德市</item>
<item>广州市</item>
<item>韶关市</item>
<item>深圳市</item>
<item>珠海市</item>
<item>汕头市</item>
<item>佛山市</item>
<item>江门市</item>
<item>湛江市</item>
<item>茂名市</item>
<item>肇庆市</item>
<item>惠州市</item>
<item>梅州市</item>
<item>汕尾市</item>
<item>河源市</item>
<item>阳江市</item>
<item>清远市</item>
<item>东莞市</item>
<item>中山市</item>
<item>潮州市</item>
<item>揭阳市</item>
<item>云浮市</item>
<item>南宁市</item>
<item>柳州市</item>
<item>桂林市</item>
<item>梧州市</item>
<item>北海市</item>
<item>防城港市</item>
<item>钦州市</item>
<item>贵港市</item>
<item>玉林市</item>
<item>百色市</item>
<item>贺州市</item>
<item>河池市</item>
<item>来宾市</item>
<item>崇左市</item>
<item>海口市</item>
<item>三亚市</item>
<item>台北市</item>
<item>高雄市</item>
<item>台中市</item>
</string-array>
</resources>
================================================
FILE: sample/src/main/res/values/string_contact.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="contact_array">
<item>$^*张狗蛋</item>
<item>000王二</item>
<item>张三</item>
<item>❤迷</item>
<item>boy</item>
<item>马蜂窝</item>
<item>王了了</item>
<item>李玖折</item>
<item>胡三</item>
<item>张政民</item>
<item>陈九</item>
<item>白玉芬</item>
<item>仓春莲</item>
<item>仓红</item>
<item>陈超云</item>
<item>陈高</item>
<item>陈国祥</item>
<item>陈小荣</item>
<item>陈秀芬</item>
<item>陈艳华</item>
<item>陈兆国</item>
<item>成秀山</item>
<item>仇腊梅</item>
<item>戴金辉</item>
<item>邓海燕</item>
<item>翟蕾</item>
<item>丁德明</item>
<item>丁素琴</item>
<item>董荣柱</item>
<item>范惠</item>
<item>冯霞</item>
<item>伏严成</item>
<item>皋德华</item>
<item>皋歌</item>
<item>高艳</item>
<item>顾其兰</item>
<item>管小云</item>
<item>何佳丽</item>
<item>侯志玲</item>
<item>黄慧</item>
<item>黄伟</item>
<item>惠志刚</item>
<item>季赛花</item>
<item>季铜然</item>
<item>纪海燕</item>
<item>蒋雯</item>
<item>康海霞</item>
<item>乐峰</item>
<item>李金凤</item>
<item>李军</item>
<item>李晓慧</item>
<item>刘琳</item>
<item>马丽</item>
<item>孟亚军</item>
<item>明汉琴</item>
<item>倪燕</item>
<item>潘杰</item>
<item>钱萍</item>
<item>邱文干</item>
<item>沈亚杰</item>
<item>盛静</item>
<item>施伟</item>
<item>施志胜</item>
<item>石磊</item>
<item>时扬</item>
<item>束长元</item>
<item>宋欣</item>
<item>孙新东</item>
<item>孙正兰</item>
<item>汤丽丽</item>
<item>唐新胜</item>
<item>陶椿燕</item>
<item>陶晓雷</item>
<item>王丹</item>
<item>王霞</item>
<item>王晓雷</item>
<item>王雪梅</item>
<item>吴长凤</item>
<item>吴凤祥</item>
<item>吴红兵</item>
<item>吴华强</item>
<item>吴萍</item>
<item>夏祥</item>
<item>肖丽丽</item>
<item>谢莉萍</item>
<item>谢艳</item>
<item>徐海莲</item>
<item>徐进</item>
<item>徐丽云</item>
<item>徐萍</item>
<item>刘世佳</item>
</string-array>
<string-array name="mobile_array">
<item>18019913883</item>
<item>10086</item>
<item>12306</item>
<item>18019913333</item>
<item>18715689899</item>
<item>13877898980</item>
<item>15678989098</item>
<item>10000</item>
<item>0557-3130999</item>
<item>13907767890</item>
<item>13978786823</item>
<item>10086</item>
<item>12306</item>
<item>18019913333</item>
<item>18715689899</item>
<item>13877898980</item>
<item>15678989098</item>
<item>10000</item>
<item>0557-3130999</item>
<item>13907767890</item>
<item>13978786823</item>
<item>10086</item>
<item>12306</item>
<item>18019913333</item>
<item>18715689899</item>
<item>13877898980</item>
<item>15678989098</item>
<item>10000</item>
<item>0557-3130999</item>
<item>13907767890</item>
<item>13978786823</item>
<item>10086</item>
<item>12306</item>
<item>18019913333</item>
<item>18715689899</item>
<item>13877898980</item>
<item>15678989098</item>
<item>10000</item>
<item>0557-3130999</item>
<item>13907767890</item>
<item>13978786823</item>
<item>10086</item>
<item>12306</item>
<item>18019913333</item>
<item>18715689899</item>
<item>13877898980</item>
<item>15678989098</item>
<item>10000</item>
<item>0557-3130999</item>
<item>13907767890</item>
<item>13978786823</item>
<item>10086</item>
<item>12306</item>
<item>18019913333</item>
<item>18715689899</item>
<item>13877898980</item>
<item>15678989098</item>
<item>10000</item>
<item>0557-3130999</item>
<item>13907767890</item>
<item>13978786823</item>
<item>10086</item>
<item>12306</item>
<item>18019913333</item>
<item>18715689899</item>
<item>13877898980</item>
<item>15678989098</item>
<item>10000</item>
<item>0557-3130999</item>
<item>13907767890</item>
<item>13978786823</item>
<item>10086</item>
<item>12306</item>
<item>18019913333</item>
<item>18715689899</item>
<item>13877898980</item>
<item>15678989098</item>
<item>10000</item>
<item>0557-3130999</item>
<item>13907767890</item>
<item>13978786823</item>
<item>10086</item>
<item>12306</item>
<item>18019913333</item>
<item>18715689899</item>
<item>13877898980</item>
<item>15678989098</item>
<item>10000</item>
<item>0557-3130999</item>
<item>13907767890</item>
<item>13978786823</item>
</string-array>
</resources>
================================================
FILE: sample/src/main/res/values/strings.xml
================================================
<resources>
<string name="app_name">IndexableRecyclerView</string>
</resources>
================================================
FILE: sample/src/main/res/values/styles.xml
================================================
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
================================================
FILE: sample/src/main/res/values-w820dp/dimens.xml
================================================
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>
================================================
FILE: settings.gradle
================================================
include ':sample', ':indexablerecyclerview'
gitextract_u96ej4no/ ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── indexablerecyclerview/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── me/ │ │ └── yokeyword/ │ │ └── indexablerv/ │ │ ├── AbstractHeaderFooterAdapter.java │ │ ├── EntityWrapper.java │ │ ├── IndexBar.java │ │ ├── IndexableAdapter.java │ │ ├── IndexableEntity.java │ │ ├── IndexableFooterAdapter.java │ │ ├── IndexableHeaderAdapter.java │ │ ├── IndexableLayout.java │ │ ├── InitialComparator.java │ │ ├── PinyinComparator.java │ │ ├── PinyinUtil.java │ │ ├── RealAdapter.java │ │ ├── SimpleFooterAdapter.java │ │ ├── SimpleHeaderAdapter.java │ │ └── database/ │ │ ├── DataObservable.java │ │ ├── DataObserver.java │ │ ├── HeaderFooterDataObservable.java │ │ ├── HeaderFooterDataObserver.java │ │ ├── IndexBarDataObservable.java │ │ └── IndexBarDataObserver.java │ └── res/ │ ├── drawable/ │ │ └── indexable_bg_center_overlay.xml │ └── values/ │ ├── indexable_attrs.xml │ ├── indexable_default.xml │ └── indexable_strings.xml ├── sample/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── me/ │ │ └── yokeyword/ │ │ └── sample/ │ │ ├── MainActivity.java │ │ ├── ToastUtil.java │ │ ├── city/ │ │ │ ├── CityAdapter.java │ │ │ ├── CityEntity.java │ │ │ ├── PickCityActivity.java │ │ │ └── SearchFragment.java │ │ └── contact/ │ │ ├── ContactAdapter.java │ │ ├── MenuEntity.java │ │ ├── PickContactActivity.java │ │ └── UserEntity.java │ └── res/ │ ├── layout/ │ │ ├── activity_main.xml │ │ ├── activity_pick_city.xml │ │ ├── activity_pick_contact.xml │ │ ├── fragment_search_city.xml │ │ ├── header_contact_banner.xml │ │ ├── header_contact_menu.xml │ │ ├── item_city.xml │ │ ├── item_contact.xml │ │ ├── item_index_city.xml │ │ └── item_index_contact.xml │ ├── values/ │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── string_city.xml │ │ ├── string_contact.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── values-w820dp/ │ └── dimens.xml └── settings.gradle
SYMBOL INDEX (303 symbols across 30 files)
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/AbstractHeaderFooterAdapter.java
class AbstractHeaderFooterAdapter (line 19) | abstract class AbstractHeaderFooterAdapter<T> {
method AbstractHeaderFooterAdapter (line 36) | public AbstractHeaderFooterAdapter(String index, String indexTitle, Li...
method wrapEntity (line 50) | private EntityWrapper<T> wrapEntity() {
method wrapEntity (line 59) | private EntityWrapper<T> wrapEntity(int pos) {
method getItemViewType (line 68) | public abstract int getItemViewType();
method onCreateContentViewHolder (line 70) | public abstract RecyclerView.ViewHolder onCreateContentViewHolder(View...
method onBindContentViewHolder (line 72) | public abstract void onBindContentViewHolder(RecyclerView.ViewHolder h...
method notifyDataSetChanged (line 78) | public void notifyDataSetChanged() {
method addData (line 82) | public void addData(T data) {
method removeData (line 95) | public void removeData(T data) {
method getHeaderFooterType (line 106) | int getHeaderFooterType() {
method addData (line 110) | public void addData(int position, T data) {
method addDatas (line 126) | public void addDatas(List<T> datas) {
method addDatas (line 132) | public void addDatas(int position, List<T> datas) {
method getOnItemClickListener (line 147) | OnItemClickListener<T> getOnItemClickListener() {
method getOnItemLongClickListener (line 152) | OnItemLongClickListener getOnItemLongClickListener() {
method getDatas (line 156) | ArrayList<EntityWrapper<T>> getDatas() {
method registerDataSetObserver (line 165) | void registerDataSetObserver(HeaderFooterDataObserver observer) {
method unregisterDataSetObserver (line 169) | void unregisterDataSetObserver(HeaderFooterDataObserver observer) {
method registerIndexBarDataSetObserver (line 173) | void registerIndexBarDataSetObserver(IndexBarDataObserver observer) {
method unregisterIndexBarDataSetObserver (line 177) | void unregisterIndexBarDataSetObserver(IndexBarDataObserver observer) {
type OnItemClickListener (line 181) | interface OnItemClickListener<T> {
method onItemClick (line 182) | void onItemClick(View v, int currentPosition, T entity);
type OnItemLongClickListener (line 185) | interface OnItemLongClickListener<T> {
method onItemLongClick (line 186) | boolean onItemLongClick(View v, int currentPosition, T entity);
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/EntityWrapper.java
class EntityWrapper (line 6) | public class EntityWrapper<T> {
method EntityWrapper (line 22) | EntityWrapper() {
method EntityWrapper (line 25) | EntityWrapper(String index, int itemType) {
method getIndex (line 32) | public String getIndex() {
method setIndex (line 36) | void setIndex(String index) {
method getIndexTitle (line 40) | public String getIndexTitle() {
method setIndexTitle (line 44) | void setIndexTitle(String indexTitle) {
method getPinyin (line 48) | public String getPinyin() {
method setPinyin (line 52) | void setPinyin(String pinyin) {
method getIndexByField (line 56) | public String getIndexByField() {
method setIndexByField (line 60) | void setIndexByField(String indexByField) {
method getData (line 64) | public T getData() {
method setData (line 68) | void setData(T data) {
method getOriginalPosition (line 72) | public int getOriginalPosition() {
method setOriginalPosition (line 76) | void setOriginalPosition(int originalPosition) {
method getItemType (line 80) | int getItemType() {
method setItemType (line 84) | void setItemType(int itemType) {
method getHeaderFooterType (line 88) | int getHeaderFooterType() {
method setHeaderFooterType (line 92) | void setHeaderFooterType(int headerFooterType) {
method isTitle (line 96) | public boolean isTitle(){
method isContent (line 100) | public boolean isContent(){
method isHeader (line 104) | public boolean isHeader(){
method isFooter (line 108) | public boolean isFooter(){
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/IndexBar.java
class IndexBar (line 22) | class IndexBar extends View {
method IndexBar (line 37) | public IndexBar(Context context) {
method init (line 41) | void init(Drawable barBg, int barTextColor, int barFocusTextColor, flo...
method onMeasure (line 59) | @Override
method onDraw (line 83) | @Override
method getPositionForPointY (line 100) | int getPositionForPointY(float y) {
method getSelectionPosition (line 115) | int getSelectionPosition() {
method setSelectionPosition (line 119) | void setSelectionPosition(int position) {
method getFirstRecyclerViewPositionBySelection (line 124) | int getFirstRecyclerViewPositionBySelection() {
method getIndexList (line 132) | List<String> getIndexList() {
method setDatas (line 136) | void setDatas(boolean showAllLetter, ArrayList<EntityWrapper> datas) {
method setSelection (line 177) | void setSelection(int firstVisibleItemPosition) {
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/IndexableAdapter.java
class IndexableAdapter (line 15) | public abstract class IndexableAdapter<T extends IndexableEntity> {
method onCreateTitleViewHolder (line 31) | public abstract RecyclerView.ViewHolder onCreateTitleViewHolder(ViewGr...
method onCreateContentViewHolder (line 33) | public abstract RecyclerView.ViewHolder onCreateContentViewHolder(View...
method onBindTitleViewHolder (line 35) | public abstract void onBindTitleViewHolder(RecyclerView.ViewHolder hol...
method onBindContentViewHolder (line 37) | public abstract void onBindContentViewHolder(RecyclerView.ViewHolder h...
method setDatas (line 39) | public void setDatas(List<T> datas) {
method setDatas (line 46) | public void setDatas(List<T> datas, IndexCallback<T> callback) {
method setOnItemTitleClickListener (line 55) | public void setOnItemTitleClickListener(OnItemTitleClickListener liste...
method setOnItemContentClickListener (line 63) | public void setOnItemContentClickListener(OnItemContentClickListener<T...
method setOnItemTitleLongClickListener (line 71) | public void setOnItemTitleLongClickListener(OnItemTitleLongClickListen...
method setOnItemContentLongClickListener (line 79) | public void setOnItemContentLongClickListener(OnItemContentLongClickLi...
method notifyDataSetChanged (line 88) | public void notifyDataSetChanged() {
method notifyInited (line 93) | private void notifyInited() {
method notifySetListener (line 97) | private void notifySetListener(int type) {
method getItems (line 101) | public List<T> getItems() {
method getIndexCallback (line 105) | IndexCallback<T> getIndexCallback() {
method getOnItemTitleClickListener (line 109) | OnItemTitleClickListener getOnItemTitleClickListener() {
method getOnItemTitleLongClickListener (line 113) | OnItemTitleLongClickListener getOnItemTitleLongClickListener() {
method getOnItemContentClickListener (line 117) | OnItemContentClickListener getOnItemContentClickListener() {
method getOnItemContentLongClickListener (line 121) | OnItemContentLongClickListener getOnItemContentLongClickListener() {
method registerDataSetObserver (line 125) | void registerDataSetObserver(DataObserver observer) {
method unregisterDataSetObserver (line 129) | void unregisterDataSetObserver(DataObserver observer) {
type IndexCallback (line 133) | public interface IndexCallback<T> {
method onFinished (line 134) | void onFinished(List<EntityWrapper<T>> datas);
type OnItemTitleClickListener (line 137) | public interface OnItemTitleClickListener {
method onItemClick (line 138) | void onItemClick(View v, int currentPosition, String indexTitle);
type OnItemContentClickListener (line 141) | public interface OnItemContentClickListener<T> {
method onItemClick (line 142) | void onItemClick(View v, int originalPosition, int currentPosition, ...
type OnItemTitleLongClickListener (line 145) | public interface OnItemTitleLongClickListener {
method onItemLongClick (line 146) | boolean onItemLongClick(View v, int currentPosition, String indexTit...
type OnItemContentLongClickListener (line 149) | public interface OnItemContentLongClickListener<T> {
method onItemLongClick (line 150) | boolean onItemLongClick(View v, int originalPosition, int currentPos...
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/IndexableEntity.java
type IndexableEntity (line 6) | public interface IndexableEntity {
method getFieldIndexBy (line 8) | String getFieldIndexBy();
method setFieldIndexBy (line 10) | void setFieldIndexBy(String indexField);
method setFieldPinyinIndexBy (line 12) | void setFieldPinyinIndexBy(String pinyin);
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/IndexableFooterAdapter.java
class IndexableFooterAdapter (line 9) | public abstract class IndexableFooterAdapter<T> extends AbstractHeaderFo...
method IndexableFooterAdapter (line 11) | public IndexableFooterAdapter(String index, String indexTitle, List<T>...
method getHeaderFooterType (line 15) | @Override
method setOnItemFooterClickListener (line 23) | public void setOnItemFooterClickListener(OnItemFooterClickListener<T> ...
method setOnItemFooterLongClickListener (line 30) | public void setOnItemFooterLongClickListener(OnItemFooterLongClickList...
type OnItemFooterClickListener (line 34) | public interface OnItemFooterClickListener<T> extends OnItemClickListe...
type OnItemFooterLongClickListener (line 37) | public interface OnItemFooterLongClickListener<T> extends OnItemLongCl...
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/IndexableHeaderAdapter.java
class IndexableHeaderAdapter (line 9) | public abstract class IndexableHeaderAdapter<T> extends AbstractHeaderFo...
method IndexableHeaderAdapter (line 11) | public IndexableHeaderAdapter(String index, String indexTitle, List<T>...
method getHeaderFooterType (line 15) | @Override
method setOnItemHeaderClickListener (line 23) | public void setOnItemHeaderClickListener(OnItemHeaderClickListener<T> ...
method setOnItemHeaderLongClickListener (line 30) | public void setOnItemHeaderLongClickListener(OnItemHeaderLongClickList...
type OnItemHeaderClickListener (line 34) | public interface OnItemHeaderClickListener<T> extends OnItemClickListe...
type OnItemHeaderLongClickListener (line 37) | public interface OnItemHeaderLongClickListener<T> extends OnItemLongCl...
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/IndexableLayout.java
class IndexableLayout (line 47) | @SuppressWarnings("unchecked")
method onChanged (line 98) | @Override
method onAdd (line 104) | @Override
method onRemove (line 110) | @Override
method onChanged (line 118) | @Override
method IndexableLayout (line 124) | public IndexableLayout(Context context) {
method IndexableLayout (line 128) | public IndexableLayout(Context context, AttributeSet attrs) {
method IndexableLayout (line 132) | public IndexableLayout(Context context, AttributeSet attrs, int defSty...
method setAdapter (line 141) | public <T extends IndexableEntity> void setAdapter(final IndexableAdap...
method addHeaderAdapter (line 195) | public <T> void addHeaderAdapter(IndexableHeaderAdapter<T> adapter) {
method removeHeaderAdapter (line 204) | public <T> void removeHeaderAdapter(IndexableHeaderAdapter<T> adapter) {
method addFooterAdapter (line 216) | public <T> void addFooterAdapter(IndexableFooterAdapter<T> adapter) {
method removeFooterAdapter (line 225) | public <T> void removeFooterAdapter(IndexableFooterAdapter<T> adapter) {
method setFastCompare (line 238) | @Deprecated
method setCompareMode (line 251) | public void setCompareMode(@CompareMode int mode) {
method setComparator (line 258) | public <T extends IndexableEntity> void setComparator(Comparator<Entit...
method setStickyEnable (line 265) | public void setStickyEnable(boolean enable) {
method showAllLetter (line 272) | public void showAllLetter(boolean show) {
method setOverlayStyle_MaterialDesign (line 279) | public void setOverlayStyle_MaterialDesign(int color) {
method setOverlayStyle_Center (line 291) | public void setOverlayStyle_Center() {
method getOverlayView (line 301) | public TextView getOverlayView() {
method getRecyclerView (line 308) | public RecyclerView getRecyclerView() {
method setIndexBarVisibility (line 315) | public void setIndexBarVisibility(boolean visible) {
method init (line 319) | private void init(Context context, AttributeSet attrs) {
method setLayoutManager (line 362) | public void setLayoutManager(RecyclerView.LayoutManager layoutManager) {
method initListener (line 386) | private void initListener() {
method processScrollListener (line 431) | private void processScrollListener() {
method processScroll (line 485) | private void processScroll(LinearLayoutManager layoutManager, ArrayLis...
method stickyNewViewHolder (line 505) | private void stickyNewViewHolder(String wrapperTitle) {
method initStickyView (line 517) | private <T extends IndexableEntity> void initStickyView(final Indexabl...
method showOverlayView (line 556) | private void showOverlayView(float y, final int touchPos) {
method initCenterOverlay (line 599) | private void initCenterOverlay() {
method initMDOverlay (line 614) | private void initMDOverlay(int color) {
method onDataChanged (line 632) | void onDataChanged() {
method transform (line 662) | private <T extends IndexableEntity> ArrayList<EntityWrapper<T>> transf...
method getSafeHandler (line 741) | private Handler getSafeHandler() {
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/InitialComparator.java
class InitialComparator (line 8) | class InitialComparator<T extends IndexableEntity> implements Comparator...
method compare (line 9) | @Override
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/PinyinComparator.java
class PinyinComparator (line 10) | class PinyinComparator<T extends IndexableEntity> implements Comparator<...
method compare (line 12) | @Override
method compareIndexName (line 26) | private int compareIndexName(String lhs, String rhs) {
method getWord (line 39) | @NonNull
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/PinyinUtil.java
class PinyinUtil (line 10) | public class PinyinUtil {
method getPingYin (line 17) | public static String getPingYin(String inputString) {
method matchingLetter (line 27) | static boolean matchingLetter(String inputString) {
method matchingPolyphone (line 31) | static boolean matchingPolyphone(String inputString) {
method gePolyphoneInitial (line 35) | static String gePolyphoneInitial(String inputString) {
method getPolyphoneRealPinyin (line 39) | static String getPolyphoneRealPinyin(String inputString) {
method getPolyphoneRealHanzi (line 44) | static String getPolyphoneRealHanzi(String inputString) {
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/RealAdapter.java
class RealAdapter (line 13) | @SuppressWarnings("unchecked")
method setIndexableAdapter (line 29) | void setIndexableAdapter(IndexableAdapter<T> adapter) {
method addIndexableHeaderAdapter (line 33) | void addIndexableHeaderAdapter(IndexableHeaderAdapter adapter) {
method removeIndexableHeaderAdapter (line 40) | void removeIndexableHeaderAdapter(IndexableHeaderAdapter adapter) {
method addIndexableFooterAdapter (line 49) | void addIndexableFooterAdapter(IndexableFooterAdapter adapter) {
method removeIndexableFooterAdapter (line 56) | void removeIndexableFooterAdapter(IndexableFooterAdapter adapter) {
method setDatas (line 65) | void setDatas(ArrayList<EntityWrapper<T>> datas) {
method getItems (line 76) | ArrayList<EntityWrapper<T>> getItems() {
method getItemViewType (line 80) | @Override
method onCreateViewHolder (line 85) | @Override
method onBindViewHolder (line 173) | @Override
method getItemCount (line 196) | @Override
method setOnItemTitleClickListener (line 201) | void setOnItemTitleClickListener(IndexableAdapter.OnItemTitleClickList...
method setOnItemContentClickListener (line 205) | void setOnItemContentClickListener(IndexableAdapter.OnItemContentClick...
method setOnItemTitleLongClickListener (line 209) | void setOnItemTitleLongClickListener(IndexableAdapter.OnItemTitleLongC...
method setOnItemContentLongClickListener (line 213) | void setOnItemContentLongClickListener(IndexableAdapter.OnItemContentL...
method addHeaderFooterData (line 217) | void addHeaderFooterData(boolean header, EntityWrapper preData, Entity...
method processAddHeaderFooterData (line 221) | private void processAddHeaderFooterData(ArrayList<EntityWrapper<T>> li...
method removeHeaderFooterData (line 237) | void removeHeaderFooterData(boolean header, EntityWrapper data) {
method processremoveHeaderFooterData (line 241) | private void processremoveHeaderFooterData(ArrayList<EntityWrapper<T>>...
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/SimpleFooterAdapter.java
class SimpleFooterAdapter (line 12) | public class SimpleFooterAdapter<T extends IndexableEntity> extends Inde...
method SimpleFooterAdapter (line 15) | public SimpleFooterAdapter(IndexableAdapter<T> adapter, String index, ...
method getItemViewType (line 20) | @Override
method onCreateContentViewHolder (line 25) | @Override
method onBindContentViewHolder (line 30) | @Override
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/SimpleHeaderAdapter.java
class SimpleHeaderAdapter (line 12) | public class SimpleHeaderAdapter<T extends IndexableEntity> extends Inde...
method SimpleHeaderAdapter (line 15) | public SimpleHeaderAdapter(IndexableAdapter<T> adapter, String index, ...
method getItemViewType (line 20) | @Override
method onCreateContentViewHolder (line 25) | @Override
method onBindContentViewHolder (line 30) | @Override
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/database/DataObservable.java
class DataObservable (line 26) | public class DataObservable extends Observable<DataObserver> {
method notifyInited (line 33) | public void notifyInited() {
method notifyChanged (line 46) | public void notifyChanged() {
method notifySetListener (line 63) | public void notifySetListener(int type) {
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/database/DataObserver.java
class DataObserver (line 6) | public class DataObserver {
method onInited (line 11) | public void onInited() {
method onChanged (line 19) | public void onChanged() {
method onSetListener (line 27) | public void onSetListener(int type) {
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/database/HeaderFooterDataObservable.java
class HeaderFooterDataObservable (line 26) | public class HeaderFooterDataObservable extends Observable<HeaderFooterD...
method notifyChanged (line 33) | public void notifyChanged() {
method notifyAdd (line 45) | public void notifyAdd(boolean header, Object preData, Object data) {
method notifyRemove (line 57) | public void notifyRemove(boolean header, Object object) {
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/database/HeaderFooterDataObserver.java
class HeaderFooterDataObserver (line 6) | public class HeaderFooterDataObserver<T> {
method onChanged (line 11) | public void onChanged() {
method onAdd (line 15) | public void onAdd(boolean header, T preData, T data) {
method onRemove (line 19) | public void onRemove(boolean header, T object) {
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/database/IndexBarDataObservable.java
class IndexBarDataObservable (line 8) | public class IndexBarDataObservable extends Observable<IndexBarDataObser...
method notifyChanged (line 10) | public void notifyChanged() {
FILE: indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/database/IndexBarDataObserver.java
class IndexBarDataObserver (line 6) | public class IndexBarDataObserver {
method onChanged (line 12) | public void onChanged() {
FILE: sample/src/main/java/me/yokeyword/sample/MainActivity.java
class MainActivity (line 15) | public class MainActivity extends AppCompatActivity {
method onCreate (line 17) | @Override
FILE: sample/src/main/java/me/yokeyword/sample/ToastUtil.java
class ToastUtil (line 9) | public class ToastUtil {
method showShort (line 18) | public static Toast showShort(Context context, CharSequence message) {
FILE: sample/src/main/java/me/yokeyword/sample/city/CityAdapter.java
class CityAdapter (line 16) | public class CityAdapter extends IndexableAdapter<CityEntity> {
method CityAdapter (line 19) | public CityAdapter(Context context) {
method onCreateTitleViewHolder (line 23) | @Override
method onCreateContentViewHolder (line 29) | @Override
method onBindTitleViewHolder (line 35) | @Override
method onBindContentViewHolder (line 41) | @Override
class IndexVH (line 47) | private class IndexVH extends RecyclerView.ViewHolder {
method IndexVH (line 50) | public IndexVH(View itemView) {
class ContentVH (line 56) | private class ContentVH extends RecyclerView.ViewHolder {
method ContentVH (line 59) | public ContentVH(View itemView) {
FILE: sample/src/main/java/me/yokeyword/sample/city/CityEntity.java
class CityEntity (line 8) | public class CityEntity implements IndexableEntity {
method CityEntity (line 13) | public CityEntity() {
method CityEntity (line 16) | public CityEntity(String name) {
method getId (line 20) | public long getId() {
method setId (line 24) | public void setId(long id) {
method getName (line 28) | public String getName() {
method setName (line 32) | public void setName(String name) {
method getPinyin (line 36) | public String getPinyin() {
method setPinyin (line 40) | public void setPinyin(String pinyin) {
method getFieldIndexBy (line 44) | @Override
method setFieldIndexBy (line 49) | @Override
method setFieldPinyinIndexBy (line 54) | @Override
FILE: sample/src/main/java/me/yokeyword/sample/city/PickCityActivity.java
class PickCityActivity (line 30) | public class PickCityActivity extends AppCompatActivity {
method onCreate (line 37) | @Override
method update (line 142) | public void update(View view) {
method initDatas (line 152) | private List<CityEntity> initDatas() {
method iniyHotCityDatas (line 163) | private List<CityEntity> iniyHotCityDatas() {
method iniyGPSCityDatas (line 172) | private List<CityEntity> iniyGPSCityDatas() {
method initSearch (line 178) | private void initSearch() {
method onBackPressed (line 205) | @Override
FILE: sample/src/main/java/me/yokeyword/sample/city/SearchFragment.java
class SearchFragment (line 26) | public class SearchFragment extends Fragment {
method onCreateView (line 34) | @Nullable
method bindDatas (line 43) | public void bindDatas(List<CityEntity> datas) {
method bindQueryText (line 57) | public void bindQueryText(String newText) {
class SearchAdapter (line 65) | private class SearchAdapter extends RecyclerView.Adapter<SearchAdapter...
method SearchAdapter (line 68) | public SearchAdapter() {
method onCreateViewHolder (line 73) | @Override
method getItemCount (line 86) | @Override
method onBindViewHolder (line 91) | @Override
method getFilter (line 96) | @Override
class VH (line 129) | class VH extends RecyclerView.ViewHolder {
method VH (line 132) | public VH(View itemView) {
FILE: sample/src/main/java/me/yokeyword/sample/contact/ContactAdapter.java
class ContactAdapter (line 16) | public class ContactAdapter extends IndexableAdapter<UserEntity> {
method ContactAdapter (line 19) | public ContactAdapter(Context context) {
method onCreateTitleViewHolder (line 23) | @Override
method onCreateContentViewHolder (line 29) | @Override
method onBindTitleViewHolder (line 35) | @Override
method onBindContentViewHolder (line 41) | @Override
class IndexVH (line 48) | private class IndexVH extends RecyclerView.ViewHolder {
method IndexVH (line 51) | public IndexVH(View itemView) {
class ContentVH (line 57) | private class ContentVH extends RecyclerView.ViewHolder {
method ContentVH (line 60) | public ContentVH(View itemView) {
FILE: sample/src/main/java/me/yokeyword/sample/contact/MenuEntity.java
class MenuEntity (line 7) | public class MenuEntity {
method MenuEntity (line 12) | public MenuEntity(String title, int iconRes) {
method getMenuId (line 17) | public long getMenuId() {
method setMenuId (line 21) | public void setMenuId(long menuId) {
method getMenuTitle (line 25) | public String getMenuTitle() {
method setMenuTitle (line 29) | public void setMenuTitle(String menuTitle) {
method getMenuIconRes (line 33) | public int getMenuIconRes() {
method setMenuIconRes (line 37) | public void setMenuIconRes(int menuIconRes) {
FILE: sample/src/main/java/me/yokeyword/sample/contact/PickContactActivity.java
class PickContactActivity (line 31) | public class PickContactActivity extends AppCompatActivity {
method onCreate (line 36) | @Override
class MenuHeaderAdapter (line 104) | class MenuHeaderAdapter extends IndexableHeaderAdapter<MenuEntity> {
method MenuHeaderAdapter (line 107) | public MenuHeaderAdapter(String index, String indexTitle, List<MenuE...
method getItemViewType (line 111) | @Override
method onCreateContentViewHolder (line 116) | @Override
method onBindContentViewHolder (line 121) | @Override
class VH (line 128) | private class VH extends RecyclerView.ViewHolder {
method VH (line 132) | public VH(View itemView) {
class BannerHeaderAdapter (line 143) | class BannerHeaderAdapter extends IndexableHeaderAdapter {
method BannerHeaderAdapter (line 146) | public BannerHeaderAdapter(String index, String indexTitle, List dat...
method getItemViewType (line 150) | @Override
method onCreateContentViewHolder (line 155) | @Override
method onBindContentViewHolder (line 168) | @Override
class VH (line 173) | private class VH extends RecyclerView.ViewHolder {
method VH (line 176) | public VH(View itemView) {
method initDatas (line 183) | private List<UserEntity> initDatas() {
method initFavDatas (line 195) | private List<UserEntity> initFavDatas() {
method initMenuDatas (line 202) | private List<MenuEntity> initMenuDatas() {
FILE: sample/src/main/java/me/yokeyword/sample/contact/UserEntity.java
class UserEntity (line 8) | public class UserEntity implements IndexableEntity {
method UserEntity (line 13) | public UserEntity(String nick, String mobile) {
method getAvatar (line 18) | public String getAvatar() {
method setAvatar (line 22) | public void setAvatar(String avatar) {
method getMobile (line 26) | public String getMobile() {
method setMobile (line 30) | public void setMobile(String mobile) {
method getNick (line 34) | public String getNick() {
method setNick (line 38) | public void setNick(String name) {
method getFieldIndexBy (line 42) | @Override
method setFieldIndexBy (line 47) | @Override
method setFieldPinyinIndexBy (line 52) | @Override
Condensed preview — 69 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (175K chars).
[
{
"path": ".gitignore",
"chars": 105,
"preview": "*.iml\n.gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.idea/\n.DS_Store\n/build\n/captures\n\n"
},
{
"path": "LICENSE",
"chars": 11330,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 5391,
"preview": "# IndexableRecyclerView\nA RecyclerView with indexable, sticky and many other features.\n\n**轻松实现:选择城市,选择联系人等需要索引的功能**\n\n> 替"
},
{
"path": "build.gradle",
"chars": 498,
"preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n r"
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 230,
"preview": "#Wed Jul 05 23:28:02 CST 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
},
{
"path": "gradle.properties",
"chars": 0,
"preview": ""
},
{
"path": "gradlew",
"chars": 4972,
"preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n## Gradle start "
},
{
"path": "gradlew.bat",
"chars": 2315,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
},
{
"path": "indexablerecyclerview/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "indexablerecyclerview/build.gradle",
"chars": 486,
"preview": "apply plugin: 'com.android.library'\n\nandroid {\n compileSdkVersion 25\n buildToolsVersion \"25.0.2\"\n\n defaultConfi"
},
{
"path": "indexablerecyclerview/proguard-rules.pro",
"chars": 676,
"preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /U"
},
{
"path": "indexablerecyclerview/src/main/AndroidManifest.xml",
"chars": 367,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:tools=\"http://schemas.android.com/t"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/AbstractHeaderFooterAdapter.java",
"chars": 5967,
"preview": "package me.yokeyword.indexablerv;\n\nimport android.support.v7.widget.RecyclerView;\nimport android.view.View;\nimport andro"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/EntityWrapper.java",
"chars": 2297,
"preview": "package me.yokeyword.indexablerv;\n\n/**\n * Created by YoKey on 16/10/6.\n */\npublic class EntityWrapper<T> {\n static fi"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/IndexBar.java",
"chars": 6451,
"preview": "package me.yokeyword.indexablerv;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphi"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/IndexableAdapter.java",
"chars": 4731,
"preview": "package me.yokeyword.indexablerv;\n\nimport android.support.v7.widget.RecyclerView;\nimport android.view.View;\nimport andro"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/IndexableEntity.java",
"chars": 237,
"preview": "package me.yokeyword.indexablerv;\n\n/**\n * Created by YoKey on 16/10/9.\n */\npublic interface IndexableEntity {\n\n Strin"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/IndexableFooterAdapter.java",
"chars": 1016,
"preview": "package me.yokeyword.indexablerv;\n\nimport java.util.List;\n\n/**\n * FooterView Adapter\n * Created by YoKey on 16/10/14.\n *"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/IndexableHeaderAdapter.java",
"chars": 1014,
"preview": "package me.yokeyword.indexablerv;\n\nimport java.util.List;\n\n/**\n * HeaderView Adapter\n * Created by YoKey on 16/10/8.\n */"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/IndexableLayout.java",
"chars": 29155,
"preview": "package me.yokeyword.indexablerv;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.r"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/InitialComparator.java",
"chars": 347,
"preview": "package me.yokeyword.indexablerv;\n\nimport java.util.Comparator;\n\n/**\n * Created by YoKey on 16/10/14.\n */\nclass InitialC"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/PinyinComparator.java",
"chars": 1551,
"preview": "package me.yokeyword.indexablerv;\n\nimport android.support.annotation.NonNull;\n\nimport java.util.Comparator;\n\n/**\n * Crea"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/PinyinUtil.java",
"chars": 1300,
"preview": "package me.yokeyword.indexablerv;\n\nimport com.github.promeg.pinyinhelper.Pinyin;\n\nimport java.util.regex.Pattern;\n\n/**\n "
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/RealAdapter.java",
"chars": 10178,
"preview": "package me.yokeyword.indexablerv;\n\nimport android.support.v7.widget.RecyclerView;\nimport android.util.SparseArray;\nimpor"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/SimpleFooterAdapter.java",
"chars": 1003,
"preview": "package me.yokeyword.indexablerv;\n\nimport android.support.v7.widget.RecyclerView;\nimport android.view.ViewGroup;\n\nimport"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/SimpleHeaderAdapter.java",
"chars": 1002,
"preview": "package me.yokeyword.indexablerv;\n\nimport android.support.v7.widget.RecyclerView;\nimport android.view.ViewGroup;\n\nimport"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/database/DataObservable.java",
"chars": 2593,
"preview": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/database/DataObserver.java",
"chars": 606,
"preview": "package me.yokeyword.indexablerv.database;\n\n/**\n * Created by YoKey on 16/10/13.\n */\npublic class DataObserver {\n /**"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/database/HeaderFooterDataObservable.java",
"chars": 4180,
"preview": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/database/HeaderFooterDataObserver.java",
"chars": 674,
"preview": "package me.yokeyword.indexablerv.database;\n\n/**\n * Created by YoKey on 16/10/13.\n */\npublic class HeaderFooterDataObserv"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/database/IndexBarDataObservable.java",
"chars": 413,
"preview": "package me.yokeyword.indexablerv.database;\n\nimport android.database.Observable;\n\n/**\n * Created by Sun on 16/10/13.\n */\n"
},
{
"path": "indexablerecyclerview/src/main/java/me/yokeyword/indexablerv/database/IndexBarDataObserver.java",
"chars": 282,
"preview": "package me.yokeyword.indexablerv.database;\n\n/**\n * Created by Sun on 16/10/13.\n */\npublic class IndexBarDataObserver {\n\n"
},
{
"path": "indexablerecyclerview/src/main/res/drawable/indexable_bg_center_overlay.xml",
"chars": 189,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <solid and"
},
{
"path": "indexablerecyclerview/src/main/res/values/indexable_attrs.xml",
"chars": 601,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <declare-styleable name=\"IndexableRecyclerView\">\n <attr na"
},
{
"path": "indexablerecyclerview/src/main/res/values/indexable_default.xml",
"chars": 455,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"default_indexBar_textColor\">#8c8c8c</color>\n <dim"
},
{
"path": "indexablerecyclerview/src/main/res/values/indexable_strings.xml",
"chars": 724,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <string-array name=\"indexable_letter\">\n <item>A</item>\n "
},
{
"path": "sample/.gitignore",
"chars": 8,
"preview": "/build\n\n"
},
{
"path": "sample/build.gradle",
"chars": 651,
"preview": "apply plugin: 'com.android.application'\n\nandroid {\n compileSdkVersion 25\n buildToolsVersion \"25.0.2\"\n\n defaultC"
},
{
"path": "sample/proguard-rules.pro",
"chars": 0,
"preview": ""
},
{
"path": "sample/src/main/AndroidManifest.xml",
"chars": 967,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package="
},
{
"path": "sample/src/main/java/me/yokeyword/sample/MainActivity.java",
"chars": 1074,
"preview": "package me.yokeyword.sample;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.v7.app.App"
},
{
"path": "sample/src/main/java/me/yokeyword/sample/ToastUtil.java",
"chars": 569,
"preview": "package me.yokeyword.sample;\n\nimport android.content.Context;\nimport android.widget.Toast;\n\n/**\n * Created by YoKey on 1"
},
{
"path": "sample/src/main/java/me/yokeyword/sample/city/CityAdapter.java",
"chars": 1845,
"preview": "package me.yokeyword.sample.city;\n\nimport android.content.Context;\nimport android.support.v7.widget.RecyclerView;\nimport"
},
{
"path": "sample/src/main/java/me/yokeyword/sample/city/CityEntity.java",
"chars": 1038,
"preview": "package me.yokeyword.sample.city;\n\nimport me.yokeyword.indexablerv.IndexableEntity;\n\n/**\n * Created by YoKey on 16/10/7."
},
{
"path": "sample/src/main/java/me/yokeyword/sample/city/PickCityActivity.java",
"chars": 7943,
"preview": "package me.yokeyword.sample.city;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android."
},
{
"path": "sample/src/main/java/me/yokeyword/sample/city/SearchFragment.java",
"chars": 4750,
"preview": "package me.yokeyword.sample.city;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android."
},
{
"path": "sample/src/main/java/me/yokeyword/sample/contact/ContactAdapter.java",
"chars": 2004,
"preview": "package me.yokeyword.sample.contact;\n\nimport android.content.Context;\nimport android.support.v7.widget.RecyclerView;\nimp"
},
{
"path": "sample/src/main/java/me/yokeyword/sample/contact/MenuEntity.java",
"chars": 781,
"preview": "package me.yokeyword.sample.contact;\n\n/**\n * Created by YoKey on 16/10/8.\n */\n\npublic class MenuEntity {\n private lon"
},
{
"path": "sample/src/main/java/me/yokeyword/sample/contact/PickContactActivity.java",
"chars": 7999,
"preview": "package me.yokeyword.sample.contact;\n\nimport android.graphics.Color;\nimport android.os.Bundle;\nimport android.support.an"
},
{
"path": "sample/src/main/java/me/yokeyword/sample/contact/UserEntity.java",
"chars": 1138,
"preview": "package me.yokeyword.sample.contact;\n\nimport me.yokeyword.indexablerv.IndexableEntity;\n\n/**\n * Created by YoKey on 16/10"
},
{
"path": "sample/src/main/res/layout/activity_main.xml",
"chars": 913,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n "
},
{
"path": "sample/src/main/res/layout/activity_pick_city.xml",
"chars": 2351,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n "
},
{
"path": "sample/src/main/res/layout/activity_pick_contact.xml",
"chars": 709,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmln"
},
{
"path": "sample/src/main/res/layout/fragment_search_city.xml",
"chars": 758,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n andro"
},
{
"path": "sample/src/main/res/layout/header_contact_banner.xml",
"chars": 472,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n andro"
},
{
"path": "sample/src/main/res/layout/header_contact_menu.xml",
"chars": 1109,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xm"
},
{
"path": "sample/src/main/res/layout/item_city.xml",
"chars": 790,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmln"
},
{
"path": "sample/src/main/res/layout/item_contact.xml",
"chars": 1323,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xm"
},
{
"path": "sample/src/main/res/layout/item_index_city.xml",
"chars": 422,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<TextView\n android:id=\"@+id/tv_index\"\n xmlns:android=\"http://schemas.andro"
},
{
"path": "sample/src/main/res/layout/item_index_contact.xml",
"chars": 742,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n andr"
},
{
"path": "sample/src/main/res/values/colors.xml",
"chars": 208,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"colorPrimary\">#3F51B5</color>\n <color name=\"color"
},
{
"path": "sample/src/main/res/values/dimens.xml",
"chars": 211,
"preview": "<resources>\n <!-- Default screen margins, per the Android Design guidelines. -->\n <dimen name=\"activity_horizontal"
},
{
"path": "sample/src/main/res/values/string_city.xml",
"chars": 7973,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <string-array name=\"city_array\">\n <item>北京市</item>\n "
},
{
"path": "sample/src/main/res/values/string_contact.xml",
"chars": 5280,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <string-array name=\"contact_array\">\n <item>$^*张狗蛋</item>\n "
},
{
"path": "sample/src/main/res/values/strings.xml",
"chars": 84,
"preview": "<resources>\n <string name=\"app_name\">IndexableRecyclerView</string>\n</resources>\n"
},
{
"path": "sample/src/main/res/values/styles.xml",
"chars": 383,
"preview": "<resources>\n\n <!-- Base application theme. -->\n <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar"
},
{
"path": "sample/src/main/res/values-w820dp/dimens.xml",
"chars": 358,
"preview": "<resources>\n <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n (such as s"
},
{
"path": "settings.gradle",
"chars": 45,
"preview": "include ':sample', ':indexablerecyclerview'\n\n"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the YoKeyword/IndexableStickyListView GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 69 files (154.5 KB), approximately 40.2k tokens, and a symbol index with 303 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.