RecyclerView+侧边字母索引排序

部分引用于:http://blog.csdn.net/cq361106306/article/details/38386783http://www.cnblogs.com/tianzhijiexian/p/4397552.html 感谢!

最近在做音乐播放器,其中涉及到本地音乐的一个集合展现,所以这里需要实现一个类似与电话薄中的一个侧边框的字母索引的一个定位功能。所以记录一下,为以后留个借鉴。

效果预览:






首先我们需要将获取到的歌曲名称转为我们需要的拼音,这里我通过使用android自带的一个类库:HanziToPinyin,当然使用第三方的例如Pinyin4J等也是可以的,这里为了方便就直接用了android自带的,使用方法: 

// 输入汉字返回拼音的通用方法函数。    
private static HanziToPinyin mHanziToPinyin=HanziToPinyin.getInstance();
    public static String getPinYin(String hanzi) {  
        ArrayList<Token> tokens = .get(hanzi);  
        StringBuilder sb = new StringBuilder();  
        if (tokens != null && tokens.size() > 0) {  
            for (Token token : tokens) {  
            if (Token.UNKNOWN == token.type) {
sb.append("[");//这里对于无法判断的类型,放入到最后的“#”中,这里使用“[”是为了让其在字母排序的时候处于最后因为其ascii位于大写的Z之后。如果要变成倒叙Z-A就要换成”@“。
} else {
sb.append(token.target.toUpperCase());//这里统一转成大写是为了考虑有英文大小写的问题,这样可以使其排序的时候不会由于大小写排序出现问题。
}
            }  
        }  
  
        return sb.toString().toUpperCase();  
    } 

然后转完之后,我们将其放入我们的数据库中,查询的时候就可以以这个拼音的字段去排序。得到A-Z-[的一个序列了。

得到了我们想要的数据之后,我们就需要实现我们的侧边框了,对于侧边框的实现比较简单,引用于:http://blog.csdn.net/cq361106306/article/details/38386783 感谢!代码:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;


public class SideBar extends View {
// 触摸事件
private OnTouchingLetterChangedListener onTouchingLetterChangedListener;
// 26个字母
public static String[] b = { "A", "B", "C", "D", "E", "F", "G", "H", "I",
"J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
"W", "X", "Y", "Z", "#" };
private int choose = -1;// 选中
private Paint paint = new Paint();


private TextView mTextDialog;


public void setView(TextView mTextDialog) {
this.mTextDialog = mTextDialog;
}


public SideBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}


public SideBar(Context context, AttributeSet attrs) {
super(context, attrs);
}


public SideBar(Context context) {
super(context);
}


/**
* 重写这个方法
*/
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 获取焦点改变背景颜色.
int height = getHeight();// 获取对应高度
int width = getWidth(); // 获取对应宽度
int singleHeight = height / b.length;// 获取每一个字母的高度


for (int i = 0; i < b.length; i++) {
paint.setColor(Color.argb(66, 0, 0, 0));
// paint.setColor(Color.WHITE);
paint.setTypeface(Typeface.SANS_SERIF);
paint.setAntiAlias(true);
paint.setTextSize(getResources().getDimensionPixelSize(R.dimen.side_bar_text_size));
// 选中的状态
if (i == choose) {
paint.setColor(Color.argb(230, 0, 0, 0));
paint.setFakeBoldText(true);
}
// x坐标等于中间-字符串宽度的一半.
float xPos = width / 2 - paint.measureText(b[i]) / 2;
float yPos = singleHeight * i + singleHeight;
canvas.drawText(b[i], xPos, yPos, paint);
paint.reset();// 重置画笔
}


}


@Override
public boolean dispatchTouchEvent(MotionEvent event) {
final int action = event.getAction();
final float y = event.getY();// 点击y坐标
final int oldChoose = choose;
final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener;
final int c = (int) (y / getHeight() * b.length);// 点击y坐标所占总高度的比例*b数组的长度就等于点击b中的个数.


switch (action) {
case MotionEvent.ACTION_UP:
setBackgroundResource(R.color.transparent);
choose = -1;//
invalidate();
if (mTextDialog != null) {
mTextDialog.setVisibility(View.INVISIBLE);
}
break;


default:
//setBackgroundResource(R.drawable.sort_listview_sidebar_background);
if (oldChoose != c) {
if (c >= 0 && c < b.length) {
if (listener != null) {
listener.onTouchingLetterChanged(b[c]);
}
if (mTextDialog != null) {
mTextDialog.setText(b[c]);
mTextDialog.setVisibility(View.VISIBLE);//这里是指的在屏幕中央显示当前点击的一个A,B,C,D...的一个状态显示。
}


choose = c;
invalidate();
}
}


break;
}
return true;
}


/**
* 向外公开的方法

* @param onTouchingLetterChangedListener
*/
public void setOnTouchingLetterChangedListener(
OnTouchingLetterChangedListener onTouchingLetterChangedListener) {
this.onTouchingLetterChangedListener = onTouchingLetterChangedListener;
}


/**
* 接口

* @author coder

*/
public interface OnTouchingLetterChangedListener {
public void onTouchingLetterChanged(String s);
}


public void setSelected(String nowChar) {
Log.i("OnRecyclerViewOnScrollListener","setSelected:"+nowChar);
if(nowChar!=null){
for(int i=0;i<b.length;i++){
if(b[i].equals(nowChar)){
choose=i;
break;
}
if(i==b.length-1){
choose=-1;
}
}
invalidate();//刷新整个view
}
}
}

这里有了侧边框的view之后,我们就需要将我们的RecylerView与之进行关联起来:

mSideBar.setOnTouchingLetterChangedListener(new OnTouchingLetterChangedListener() {


@Override
public void onTouchingLetterChanged(String s) {
// the character first position
Character clickChar = s.charAt(0);
int position = getPositionForSection(clickChar);
Log.i(TAG, "click " + s + " character first position:"
+ position);
if (position != -1) {
// mRecyclerView.scrollToPosition(position);
LinearLayoutManager llm = (LinearLayoutManager) mRecyclerView
.getLayoutManager();
llm.scrollToPositionWithOffset(position, 0);//将指定的position滑动到距离上面第0个的位置,也就是顶部。
}


}
});

private int getPositionForSection(Character key) {
if (key.equals('#')) {
key = '[';
}
if (!mPinyinPositionMap.containsKey(key)) {
return -1;
}
return mPinyinPositionMap.get(key);
}

通过实现其Touch的接口,我们可以通过其接口获取到相应位置的值,然后我们通过这个值,再去定位到RecyclerView中的位置,这里scrollToPosition和smoothScrollToPosition的方法是不够全面的,原因是:

他们只负责让不在当前页面的positition划过来显示,但是对于,已经在当前页面的他是不会划到顶部的。所以这里与我想要的结果不太一样,于是这里我也是查找许久终于找到了解决的方案:就是采用上面的方法,通过该方法可以较为理想的滑到到我们想要的结果。

于是这个侧边点击的联动我们已经完成了,接下来我们需要做的是便是在RecyclerView滑动的时候,我们如何定位到当前滑动到了哪里的一个问题了。对于这个问题,感谢:http://www.cnblogs.com/tianzhijiexian/p/4397552.html

我基于该博客中给出的方法,对于我的应用进行了一下裁剪和修改,然后得到了我想要的一个功能:



import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.Log;


/**
 * @brief recycler view scroll listener
 */
public class OnRecyclerViewOnScrollListener extends
RecyclerView.OnScrollListener implements OnPositionListener {




public static enum LAYOUT_MANAGER_TYPE {
LINEAR, GRID, STAGGERED_GRID
}


/**
* layoutManager的类型(枚举)
*/
protected LAYOUT_MANAGER_TYPE layoutManagerType;




/**
* 第一个可见的item的位置
*/
private int firstVisibleItemPosition;


@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
RecyclerView.LayoutManager layoutManager = recyclerView
.getLayoutManager();
if (layoutManagerType == null) {
if (layoutManager instanceof LinearLayoutManager) {
layoutManagerType = LAYOUT_MANAGER_TYPE.LINEAR;
} else if (layoutManager instanceof GridLayoutManager) {
layoutManagerType = LAYOUT_MANAGER_TYPE.GRID;
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
layoutManagerType = LAYOUT_MANAGER_TYPE.STAGGERED_GRID;
} else {
throw new RuntimeException(
"Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager and StaggeredGridLayoutManager");
}
}


switch (layoutManagerType) {
case LINEAR:
firstVisibleItemPosition = ((LinearLayoutManager) layoutManager)
.findFirstCompletelyVisibleItemPosition();
break;
case GRID:
firstVisibleItemPosition = ((GridLayoutManager) layoutManager)
.findFirstCompletelyVisibleItemPosition();
break;
default:
break;
}
Log.i("OnRecyclerViewOnScrollListener", "firstVisibleItemPosition:"+firstVisibleItemPosition);
}


@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
onPosition(firstVisibleItemPosition);
}


@Override
public void onPosition(int firstCompleteVisiblePosition) {
}
}


mRecyclerView.setOnScrollListener(new OnRecyclerViewOnScrollListener() {
@Override
public void onPosition(int firstCompleteVisiblePosition) {
mSideBar.setSelected(getSortBarCharacterFromRecyclerViewPosition(firstCompleteVisiblePosition));
}
});

private String getSortBarCharacterFromRecyclerViewPosition(int position) {
Log.i("OnRecyclerViewOnScrollListener","getSortBarCharacterFromRecyclerViewPosition:"+position);
if(mRecyclerAdapter.mCursor!=null&&mRecyclerAdapter.mCursor.getCount()>position){
mRecyclerAdapter.mCursor.moveToPosition(position);
Character c = mRecyclerAdapter.mCursor.getString(
你的拼音字段的index).charAt(0);
if (c.equals('[')) {
return "#";
}

return c + "";
}else{
return null;
}
}

这里,我通过OnRecyclerViewOnScrollListener 获取到当前第一个完全可见的position,然后从cursor当中取出我们所需要的拼音的字段然后处理特殊的拼音,然后在转换之后,返回我们SideBar的相应的位置,然后设置,这样就可以在我们实现滑动的之后定位到当前我们具体是什么字母了。这样就可以实现滑动之后侧边栏字母高亮提示用户当前滑动到的位置是什么字母了。

总结:其中对于我们侧边框的定位搜索的功能,基本上实现了,总体来说未发现较大BUG,除了对于数字开头的内容,没有对应的右侧来定位以外,和右侧定位时,RecyclerView滑动没有特效以外应该使用问题不大。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值