App列表快速定位和筛选,如微信联系人索引。
相关开源库:
woozzu/IndexableListView@[Github]
bhavyahmehta/ListviewFilter@[Github]
ndraskindler/quickscroll@[Github]
此库利用了一个
IndexScroller
和
GestureDetector
来对
IndexableListView
类中的
Touch
事件进行操作。
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mScroller != null && mScroller.onTouchEvent(ev))
return true;
return super.onTouchEvent(ev);
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(mScroller.contains(ev.getX(), ev.getY()))
return true;
return super.onInterceptTouchEvent(ev);
IndexScroller
中 通过getSectionByPoint(float y)
来获取当前Section
,调用draw(Canvas canvas)
方法绘制到屏幕上。
canvas.drawRoundRect(mIndexbarRect, 5 * mDensity, 5 * mDensity, indexbarPaint);
canvas.drawText(mSections[i], mIndexbarRect.left + paddingLeft
, mIndexbarRect.top + mIndexbarMargin + sectionHeight * i + paddingTop - indexPaint.ascent(), indexPaint);
此库实现了索引定位和搜索过滤,需要了解Filter
接口。
其在onPostCreate方法中通过TextWatcher接口观察EditText中键入的关键字,通过Filter接口过滤,最终调用AsyncTask来设置SectionPos 和 列表数据,进而展示到界面上。
Collections.sort(items, new SortIgnoreCase());
String prev_section = "";
for (String current_item : items) {
String current_section = current_item.substring(0, 1).toUpperCase(Locale.getDefault());
if (!prev_section.equals(current_section)) {
mListItems.add(current_section);
mListItems.add(current_item);
mListSectionPos.add(mListItems.indexOf(current_section));
prev_section = current_section;
} else {
mListItems.add(current_item);
IndexBarView
:索引自定义View
,onTouchEvent
方法中不断回调自定义接口,向外公布状态。
public interface IIndexBarFilter {
void filterList(float sideIndexY,int position,String previewText);
PinnedHeaderListView
:主类,实现了上述接口,调用setSelection(position);
来设置list的滚动位置。
@Override
public void filterList(float indexBarY, int position,String previewText) {
this.mIndexBarY=indexBarY;
setSelection(position);
PinnedHeaderAdapter
:实现了OnScrollListener, IPinnedHeader
接口,监测滚动事件,掉用PinnedHeaderListView
的configureHeaderView
来设置头布局,总体设置十分精妙。
@Override
public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {
if (view instanceof PinnedHeaderListView) {
((PinnedHeaderListView) view).configureHeaderView(firstVisibleItem);
此库动画较多,通过传入的不同的type类型来create 不同的View,监听ListView的onScroll方法,来回调不同的selection。
@SuppressLint("NewApi")
protected void scroll(final float height) {
scrollIndicatorTextView.setText(scrollable.getIndicatorForPosition(position, groupPosition));
listView.setSelection(scrollable.getScrollPosition(position, groupPosition));
@SuppressLint("NewApi")
protected void moveHandlebar(final float where) {
float move = where;
ViewHelper.setTranslationY(handleBar, move);
知道了原理,那么我们就来实现一个不依赖于ListView的自定义控件,一个简单的竖直的长条自定义View
,在里面绘制上我们的A~Z
字母,回调出Touch
事件,可以配合RecyclerView。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="IndexView">
<attr name="textSize" format="dimension" />
<attr name="textColor" format="color"/>
<attr name="selectTextColor" format="color"/>
<attr name="selectBackGround" format="color"/>
</declare-styleable>
</resources>
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = getMeasuredWidth();
height = getMeasuredHeight();
singleHeight = height/letters.length;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (showBg) {
canvas.drawColor(selectBackGround);
for (int i = 0; i < letters.length; i++) {
if (i == choose) {
paint.setColor(selectTextColor);
paint.setFakeBoldText(true);
}else{
paint.setColor(textColor);
paint.setFakeBoldText(false);
float posX = width / 2 - paint.measureText(letters[i]) / 2;
float posY = i * singleHeight + singleHeight;
canvas.drawText(letters[i], posX, posY, paint);
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
final float y = event.getY();
final int index = (int) (y / getHeight() * letters.length);
final int oldChoose = choose;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
showBg = true;
if (oldChoose != index && listenner != null && index > 0
&& index < letters.length) {
choose = index;
listenner.onTouchLetterChange(showBg, letters[index]);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
if (oldChoose != index && listenner != null && index > 0
&& index < letters.length) {
choose = index;
listenner.onTouchLetterChange(showBg, letters[index]);
invalidate();
break;
case MotionEvent.ACTION_UP:
showBg = false;
choose = -1;
if (listenner != null) {
if (index <= 0) {
listenner.onTouchLetterChange(showBg, "A");
} else if (index > 0 && index < letters.length) {
listenner.onTouchLetterChange(showBg, letters[index]);
} else if (index >= letters.length) {
listenner.onTouchLetterChange(showBg, "Z");
invalidate();
break;
return true;
这里通过invalidate()
来使界面重绘.
在activity
中我们引用并设置回调,即可快速实现效果,
indexView.setOnTouchLetterChangeListenner(new IndexView.OnTouchLetterChangeListenner() {
@Override
public void onTouchLetterChange(boolean isTouched, String s) {
float_view.setText(s);
if (isTouched) {
float_view.setVisibility(View.VISIBLE);
} else {
float_view.postDelayed(new Runnable() {
@Override
public void run() {
float_view.setVisibility(View.GONE);
}, 100);
int position = index.indexOf(s);
id_recyclerview.scrollToPosition(position); }
相关源码:
IndexView@[Github]
App列表快速定位和筛选,如微信联系人索引。相关开源库:<a href="https://github.com/woozzu/IndexableListView" target="_blank">woozzu/IndexableListView@[Github]</a><a href="https://github.com/bhavyahmehta/ListviewFilter" target="
Android 简单易用的SideBar 快速定位侧边栏A~Z 仿微信国家或地区代码选择前言效果使用方法demo
很多项目里都会用到快速定位侧边栏,好比如联系人列表界面、城市列表等;
所以为了方便,我就自己打包了一个控件依赖库,方便自己使用也方便别人使用;
普通图片:
GIF图:
Step 1. Add the JitPack repository to your bui...
之前做项目的时候遇到一个需求是实现品牌的字母排序功能,网上的资料很多,但是有一部分有bug,这篇文章是我学习和解决部分bug之后的总结。今天带来的是RecyclerView的A-Z字母排序和过滤搜索功能。
首先上效果图:
重点:1、实现数据排序分类 2、实现右侧的字母导航 3、搜索这里使用了一个中文转化为拼音的工具包,即pinyin4j-2.5.0.jar。官网地址:http://pinyin4
WaveSideBar
You can use WaveSideBar in the contacts page of your application.
Refer to AlexLiuSheng/AnimSideBar.
Screenshot
Include the WaveSideBar to Your Project
With gradle:
dependencies {
compile 'com.gjiazhe:wavesidebar:1.3'
Use WaveSideBar in Layout File
Description of Attributes
Attributes
Format
Default
Description
app:highlightBackground="#10000000"
<!--设置触摸后的高亮字体颜色-->
app:highlightColor="@color/colorAccent"
public class MainActivity extends AppCompatActivity {
private LetterBar letter_bar;
set OnTouchLetterChangeListener
mSideBa
rView.setOnTouchLetterChangeListener(new WaveSideBa
rView.OnTouchLetterChangeListener() {
@Override
public void onLetterChange(String letter) {
int pos = adapter.getLetterPosition(letter);
if (pos != -1) {
mRe
cyclerView.scrollToPosition(pos);
用户需求很多带有列表信息的控件,如ListView,RecyclerView,当需要显示大量信息的时候,
右侧都有一个竖直排列的字母表,手指滑动即可实现按字母定位,比如你手机里的系统联系人界面。概念设计
右侧的字母栏,实现View.OnTouchListener接口,当手指触摸滑动时,
获取相对于触发这个事件的视图(这里即指字母栏)的左上点的坐标的纵坐标Y,是不是有点绕口。 用当前的Y除以字母栏
Android Studio自定义控件可以实现以下功能:
1. 实现特定的UI效果:通过自定义控件,可以实现一些Android原生控件无法实现的特定UI效果,比如自定义圆形进度条、自定义表格控件等。
2. 提高UI的复用性:自定义控件可以被多个Activity或Fragment共用,从而提高UI的复用性,减少代码冗余。
3. 提高开发效率:通过自定义控件,可以将一些常用的UI组件封装成一个控件,从而提高开发效率,减少代码量。
4. 实现特定的交互行为:通过自定义控件,可以实现一些特定的交互行为,比如手势识别、拖拽、缩放等。
5. 提高应用的性能:通过自定义控件,可以优化应用的性能,比如减少内存的占用、提高绘制效率等。