首先看看實現的效果
可以看出要實現上面效果,有三個步驟:
1.漢字轉化爲拼音,並且根據首字母排序
2.用ItemDecoration實現字母行的顯示
3.自定義實現右側的按字母導航欄
當然重點講講ItemDecoration的實現。都知道RecyclerView本身都沒有分割線,需要分割線都是在item中畫一條線或者使用ItemDecoration來實現分割線。在RecyclerView中我們可以給每一個item都添加ItemDecoration,所以可以自定義ItemDecoration來實現各種我們所需要的效果。
ItemDecoration
ItemDecoration是RecyclerView內部的一個抽象類,要實現這個抽象類自然需要實現內部的抽象方法,除了deprecated的方法只有下面三個方法:
1.void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)
這個方法是用來指定每一個的item對應decoration的大小區域,主要實現方式就是設置outRect的left、top、right、bottom,如果一個item不需要decoration把outRect的上下左右設置爲0即可。盜用網上一張圖看看outRect具體什麼意思
2.void onDraw(Canvas c, RecyclerView parent, State state)
onDraw方法看名字大家都應該很熟悉,這個方法自然是用來畫具體的ItemDecoration的,繪製的內容是顯示在itemView的下層。下層什麼意思,待會來看看。
3.void onDrawOver(Canvas c, RecyclerView parent, State state)
也是一個繪製的方法,不過是繪製在itemView的上層
以上三個方法的調用順序也就是按照上面的排列的順序來調用的。
首先來看看用ItemDecoration實現的分割線DividerItemDecoration
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable mDivider;
private int mOrientation;
public DividerItemDecoration(Context context, int orientation) {
//獲取系統的divider
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
//畫豎直分割線
public void drawVertical(Canvas c, RecyclerView parent) {
//左邊緣距離RecyclerView左邊的距離
final int left = parent.getPaddingLeft();
//右邊緣距離RecyclerView右邊邊的距離
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom;
//去掉最後一條的分割線
if (i == childCount - 1) {//bottom和top相等,即高度爲0 不顯示
bottom = top;
} else {
bottom = top + mDivider.getIntrinsicHeight();
}
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
//畫水平分割線
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
}
主要實現了getItemOffsets和onDraw方法,因爲這兩個方法已經能滿足了,自然不需要全部方法實現。在getItemOffsets方法中判斷是豎直方向還是水平方向的分割線,豎直方向只需要在outRect的bottom加上分割線的寬度即可,當然水平分割線在右邊加上就OK。在onDraw方法中具體的畫出分割線,不知道大家有沒有想過這裏的分割線高度和outRect中設置的高度有什麼關係,那麼下面修改一下代碼實驗一下。
在上面onDraw畫豎直分割線的方法中,把分割線高度加上80px,即:
mDivider.setBounds(left, top, right, bottom+80);
效果沒變化?意思是onDraw裏面畫的區域大小不會超過outRect設置的大小嗎?記得之前說過,onDraw方法繪製的內容是在itemView的下層的,會不會是被itemView遮擋而沒有顯示出來呢,那麼下面我們把itemView的背景色改爲透明,看看效果
android:background="@android:color/transparent"
這次看到了不同了吧,正是我們所猜想的那樣。也就證明了onDraw方法顯示的內容實在itemView的下層,同時它繪製的內容並不是不會超過outRect指定的區域,而outRect指定的區域也是實際分配給ItemDecoration的區域,在這個區域繪製纔不會影響itemView,所以onDraw繪製的內容我們應該要保持和outRect給定的區域是相同的。
顯示字母的ItemDecoration
現在來看看顯示字母的ItemDecoration是怎麼實現的。看上面的效果可以發現,最上面始終顯示了一個ItemDecoration,上面說過onDrawOver方法繪製的內容是顯示在最上層,所以用這個方法來繪製最上面再適合不過了。其他itemView顯示字母的ItemDecoration也並不是採用onDraw方法繪製,而是用xml實現的,因爲採用xml方式來實現可以更方便的來定製ItemDecoration的內容,也可以實現其中的點擊事件。
在看ItemDecoration之前,先看看所用到的一個接口和RecyclerView的Adapter
public interface StickyHeaderAdapter<T extends RecyclerView.ViewHolder> {
String getHeaderId(int position);
T onCreateHeaderViewHolder(ViewGroup parent);
void onBindHeaderViewHolder(T viewholder, int position);
}
這個接口裏面有三個方法,第一個方法是獲取headerId,因爲在顯示是不可能每一個item都要顯示decoration,只有每種首字母第一個才顯示,所用這裏需要一個id來判斷是否需要設置ItemDecoration。後面兩個方法是仿照RecyclerView.Adapter的寫的,因爲我們採用ItemDecoration佈局用xml實現,如果需要顯示的ItemDecoration很多的話,每次都需要去用LayoutInflater去加載佈局,顯然不夠優雅,所用用holder機制來實現複用。下面來看看我們的Adapter
/**
* Created by lzy .
* Date: 16/11/24
*/
public class MedicineAdapter extends RecyclerView.Adapter<MedicineAdapter.ViewHolder> implements
StickyHeaderAdapter<MedicineAdapter.HeaderHolder> {
private Context mContext;
private List<MedicineBean> mDatas;
private LayoutInflater mInflater;
private int i;
public MedicineAdapter(Context mContext, List<MedicineBean> mDatas) {
this.mContext = mContext;
this.mDatas = mDatas;
mInflater = LayoutInflater.from(mContext);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(mInflater.inflate(R.layout.item_medicine, parent, false));
}
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final MedicineBean MedicineBean = mDatas.get(position);
holder.tvName.setText(MedicineBean.getName());
}
@Override
public int getItemCount() {
return mDatas != null ? mDatas.size() : 0;
}
@Override
public String getHeaderId(int position) {
return mDatas.get(position).getLetter();
}
@Override//生成header的佈局
public HeaderHolder onCreateHeaderViewHolder(ViewGroup parent) {
return new HeaderHolder(mInflater.inflate(R.layout.item_decoration, parent, false));
}
@Override//綁定header的數據
public void onBindHeaderViewHolder(HeaderHolder viewholder, int position) {
viewholder.header.setText(mDatas.get(position).getLetter());
}
/**
* 根據分類的首字母獲取其第一次出現該首字母的位置
*/
public int getPositionForSection(String section) {
for (int i = 0; i < mDatas.size(); i++) {
String sortStr = mDatas.get(i).getLetter();
if (sortStr.equals(section)) {
return i;
}
}
return -1;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView tvName;
public ViewHolder(View itemView) {
super(itemView);
tvName = (TextView) itemView.findViewById(R.id.name);
}
}
public static class HeaderHolder extends RecyclerView.ViewHolder {
public TextView header;
public HeaderHolder(View itemView) {
super(itemView);
header = (TextView) itemView;
}
}
}
可以看到這個Adapter實現了之前上面的接口,接口的實現方式和普通的Adapter的實現都類似在onCreateHeaderViewHolder中加載xml文件,在onBindHeaderViewHolder中加載數據,HeaderHolder也是一樣的,headerId是採用的一個letter的,也就是每一條的首字母。下面看看ItemDecoration是怎麼實現的。
getItemOffsets方法
首先看看第一個getItemOffsets方法的實現
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//得到該item所在的位置
int position = parent.getChildAdapterPosition(view);
int headerHeight = 0;
//在使用adapterPosition時最好的加上這個判斷
if (position != RecyclerView.NO_POSITION && hasHeader(position)) {
//獲取到ItemDecoration所需要的高度
View header = getHeader(parent, position).itemView;
headerHeight = header.getHeight();
}
outRect.set(0, headerHeight, 0, 0);
}
很簡單,就是判斷如果這個item需要ItemDecoration就獲取到header的高度,設置給outRect
判斷是否需要header
判斷是否需要header的方法,之前不是在Adapter裏面寫了getHeaderId的方法嗎,這裏就用到了,根據前兩個headerId是否相同來判斷是否需要設置ItemDecoration
private boolean hasHeader(int position) {
if (position == 0) {//第一個位置必然有
return true;
}
//判斷和上一個的id不同則有header
int previous = position - 1;
return !mAdapter.getHeaderId(position).equals(mAdapter.getHeaderId(previous));
}
獲取Header的方法
private RecyclerView.ViewHolder getHeader(RecyclerView parent, int position) {
//創建HeaderViewHolder
MedicineAdapter.HeaderHolder holder = mAdapter.onCreateHeaderViewHolder(parent);
final View header = holder.itemView;
//綁定數據
mAdapter.onBindHeaderViewHolder(holder, position);
//測量View並且layout
int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY);
int heightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED);
//根據父View的MeasureSpec和子view自身的LayoutParams以及padding來獲取子View的MeasureSpec
int childWidth = ViewGroup.getChildMeasureSpec(widthSpec,
parent.getPaddingLeft() + parent.getPaddingRight(), header.getLayoutParams().width);
int childHeight = ViewGroup.getChildMeasureSpec(heightSpec,
parent.getPaddingTop() + parent.getPaddingBottom(), header.getLayoutParams().height);
//進行測量
header.measure(childWidth, childHeight);
//根據測量後的寬高放置位置
header.layout(0, 0, header.getMeasuredWidth(), header.getMeasuredHeight());
return holder;
}
在這裏面調用了之前Adapter實現接口裏面的方法,創建了ViewHolder,綁定了數據。都知道自定義view需要實現onMeasure、onLayout、onDraw方法,所以在這裏對它進行了測量和放置,而draw是在onDrawOver裏面實現的。對這裏熟悉的可以去看看相關的知識點。onDrawOver方法
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
final int count = parent.getChildCount();
for (int layoutPos = 0; layoutPos < count; layoutPos++) {
final View child = parent.getChildAt(layoutPos);
final int adapterPos = parent.getChildAdapterPosition(child);
//只有在最上面一個item或者有header的item才繪製ItemDecoration
if (adapterPos != RecyclerView.NO_POSITION && (layoutPos == 0 || hasHeader(adapterPos))) {
View header = getHeader(parent, adapterPos).itemView;
c.save();
final int left = child.getLeft();
final int top = getHeaderTop(parent, child, header, adapterPos, layoutPos);
c.translate(left, top);
header.setTranslationX(left);
header.setTranslationY(top);
header.draw(c);
c.restore();
}
}
}
這裏就是一個循環,在需要Header的地方進行繪製,當然需要把畫布移動到要繪製的位置,主要是確定它距離頂部的大小。
private int getHeaderTop(RecyclerView parent, View child, View header, int adapterPos, int layoutPos) {
int headerHeight = header.getHeight();
int top = ((int) child.getY()) - headerHeight;
if (layoutPos == 0) {//處理最上面兩個ItemDecoration切換時
final int count = parent.getChildCount();
final String currentId = mAdapter.getHeaderId(adapterPos);
for (int i = 1; i < count; i++) {
int adapterPosHere = parent.getChildAdapterPosition(parent.getChildAt(i));
if (adapterPosHere != RecyclerView.NO_POSITION) {
String nextId = mAdapter.getHeaderId(adapterPosHere);
if (!nextId.equals(currentId)) { //找到下一個不同類的view
final View next = parent.getChildAt(i);
//這裏計算offset畫個圖會很清楚
final int offset = ((int) next.getY()) - (headerHeight + getHeader(parent, adapterPosHere).itemView.getHeight());
if (offset < 0) {//如果大於0的話,此時並沒有切換
return offset;
} else {
break;
}
}
}
}
//top不能小於0,否則最上面的ItemDecoration不會一直存在
top = Math.max(0, top);
}
return top;
}
這裏的邏輯是這樣的:
1.當此view不是最上面的顯示的時候,header距離頂部直接就是此view距離頂部距離減去header的高度即可
2.當此view是最上面的view的時候,首先用for循環找到它下一個和它headerId不同的第一個view,利用找到的這個view和它本身來計算出它的header距離頂部的距離,當這個距離大於0時,代表此view的header還全部顯示出來,這時直接用上面的方式獲取這個距離,當這個距離小於0時就是我們所希望的。
OK完成,下面貼出ItemDecoration的所以代碼
/**
* Created by lzy on 2016/11/23.
*/
public class StickyItemDecoration extends RecyclerView.ItemDecoration {
private static final String TAG = "lzy";
private MedicineAdapter mAdapter;
public StickyItemDecoration(MedicineAdapter mAdapter) {
super();
this.mAdapter = mAdapter;
}
//最後調用 繪製頂部固定的header
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
final int count = parent.getChildCount();
for (int layoutPos = 0; layoutPos < count; layoutPos++) {
final View child = parent.getChildAt(layoutPos);
final int adapterPos = parent.getChildAdapterPosition(child);
//只有在最上面一個item或者有header的item才繪製ItemDecoration
if (adapterPos != RecyclerView.NO_POSITION && (layoutPos == 0 || hasHeader(adapterPos))) {
View header = getHeader(parent, adapterPos).itemView;
c.save();
final int left = child.getLeft();
final int top = getHeaderTop(parent, child, header, adapterPos, layoutPos);
c.translate(left, top);
header.setTranslationX(left);
header.setTranslationY(top);
header.draw(c);
c.restore();
}
}
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//得到該item所在的位置
int position = parent.getChildAdapterPosition(view);
int headerHeight = 0;
//在使用adapterPosition時最好的加上這個判斷
if (position != RecyclerView.NO_POSITION && hasHeader(position)) {
//獲取到ItemDecoration所需要的高度
View header = getHeader(parent, position).itemView;
headerHeight = header.getHeight();
}
outRect.set(0, headerHeight, 0, 0);
}
/**
* 判斷是否有header
*
* @param position
* @return
*/
private boolean hasHeader(int position) {
if (position == 0) {//第一個位置必然有
return true;
}
//判斷和上一個的id不同則有header
int previous = position - 1;
return !mAdapter.getHeaderId(position).equals(mAdapter.getHeaderId(previous));
}
/**
* 獲得自定義的Header
*
* @param parent
* @param position
* @return
*/
private RecyclerView.ViewHolder getHeader(RecyclerView parent, int position) {
//創建HeaderViewHolder
MedicineAdapter.HeaderHolder holder = mAdapter.onCreateHeaderViewHolder(parent);
final View header = holder.itemView;
//綁定數據
mAdapter.onBindHeaderViewHolder(holder, position);
//測量View並且layout
int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY);
int heightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED);
//根據父View的MeasureSpec和子view自身的LayoutParams以及padding來獲取子View的MeasureSpec
int childWidth = ViewGroup.getChildMeasureSpec(widthSpec,
parent.getPaddingLeft() + parent.getPaddingRight(), header.getLayoutParams().width);
int childHeight = ViewGroup.getChildMeasureSpec(heightSpec,
parent.getPaddingTop() + parent.getPaddingBottom(), header.getLayoutParams().height);
//進行測量
header.measure(childWidth, childHeight);
//根據測量後的寬高放置位置
header.layout(0, 0, header.getMeasuredWidth(), header.getMeasuredHeight());
return holder;
}
/**
* 計算距離頂部的高度
*
* @param parent
* @param child
* @param header
* @param adapterPos
* @param layoutPos
* @return
*/
private int getHeaderTop(RecyclerView parent, View child, View header, int adapterPos, int layoutPos) {
int headerHeight = header.getHeight();
int top = ((int) child.getY()) - headerHeight;
if (layoutPos == 0) {//處理最上面兩個ItemDecoration切換時
final int count = parent.getChildCount();
final String currentId = mAdapter.getHeaderId(adapterPos);
for (int i = 1; i < count; i++) {
int adapterPosHere = parent.getChildAdapterPosition(parent.getChildAt(i));
if (adapterPosHere != RecyclerView.NO_POSITION) {
String nextId = mAdapter.getHeaderId(adapterPosHere);
//找到下一個不同類的view
if (!nextId.equals(currentId)) {
final View next = parent.getChildAt(i);
//這裏計算offset畫個圖會很清楚
final int offset = ((int) next.getY()) - (headerHeight + getHeader(parent, adapterPosHere).itemView.getHeight());
if (offset < 0) {//如果大於0的話,此時並沒有切換
return offset;
} else {
break;
}
}
}
}
//top不能小於0,否則最上面的ItemDecoration不會一直存在
top = Math.max(0, top);
}
return top;
}
}
漢字轉拼音
通過一個三方的類庫pinyin4j來實現,這裏就是調用方法PinyinHelper.toHanyuPinyinStringArray獲取一個漢字的拼音,然後得到第一個英文字母並轉化爲大寫
private List<MedicineBean> filledData(String[] data) {
List<MedicineBean> mSortList = new ArrayList<MedicineBean>();
for (int i = 0; i < data.length; i++) {
MedicineBean medicineBean = new MedicineBean();
medicineBean.setName(data[i]);
//漢字轉換成拼音
String[] pinyin = PinyinHelper.toHanyuPinyinStringArray(data[i].toCharArray()[0]);
String sortString = pinyin[0].substring(0, 1).toUpperCase();
// 正則表達式,判斷首字母是否是英文字母
if (sortString.matches("[A-Z]")) {
medicineBean.setLetter(sortString.toUpperCase());
} else {
medicineBean.setLetter("#");
}
mSortList.add(medicineBean);
}
return mSortList;
}
右邊的字母導航欄
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 setTextView(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);
}
@Override
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.rgb(33, 65, 98));
// paint.setColor(Color.WHITE);
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setAntiAlias(true);
paint.setTextSize(20);
// 選中的狀態
if (i == choose) {
paint.setColor(Color.parseColor("#3399ff"));
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:
setBackgroundDrawable(new ColorDrawable(0x00000000));
choose = -1;//
invalidate();
if (mTextDialog != null) {
mTextDialog.setVisibility(View.INVISIBLE);
}
break;
default:
setBackgroundColor(Color.parseColor("#808080"));
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);
}
choose = c;
invalidate();
}
}
break;
}
return true;
}
/**
* 向外公開的方法
*
* @param onTouchingLetterChangedListener
*/
public void setOnTouchingLetterChangedListener(
OnTouchingLetterChangedListener onTouchingLetterChangedListener) {
this.onTouchingLetterChangedListener = onTouchingLetterChangedListener;
}
/**
* 接口
*
* @author coder
*/
public interface OnTouchingLetterChangedListener {
void onTouchingLetterChanged(String s);
}
}
這是在網上找的一個類,相信大家都能看懂,就不多說了,最後貼出MainActivity
public class MainActivity extends AppCompatActivity {
private String[] mData = {"阿魏八味丸", "阿昔洛韋眼膏", "艾司洛爾", "安吖啶注射液", "阿達帕林", "蔘茸追風酒", "草烏", "石斛夜光丸",
"骨質增生片", "烏雞白鳳丸", "人蔘益母丸", "補脾益腸丸", "丹蔘片", "小金丸", "婦寧康", "糖脈康", "菲伯瑞", "乙肝解毒片", "腦血栓片"};
private static final String TAG = "lzy";
private RecyclerView mRecyclerView;
private SideBar sideBar;
private StickyItemDecoration mDecoration;
private PinyinComparator pinyinComparator;
private MedicineAdapter medicineAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.rv);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
//按照字母排序
pinyinComparator = new PinyinComparator();
List<MedicineBean> data = filledData(mData);
Collections.sort(data, pinyinComparator);
medicineAdapter = new MedicineAdapter(this, data);
mRecyclerView.setAdapter(medicineAdapter);
mDecoration = new StickyItemDecoration(medicineAdapter);
mRecyclerView.addItemDecoration(mDecoration);
//添加分割線
mRecyclerView.addItemDecoration(new DividerItemDecoration(MainActivity.this, DividerItemDecoration.VERTICAL_LIST));
sideBar = (SideBar) findViewById(R.id.sideBar);
sideBar.setTextView((TextView) findViewById(R.id.dialog));
//設置右側觸摸監聽
sideBar.setOnTouchingLetterChangedListener(new SideBar.OnTouchingLetterChangedListener() {
@Override
public void onTouchingLetterChanged(String s) {
//該字母首次出現的位置
int position = medicineAdapter.getPositionForSection(s);
if (position != -1) {
mRecyclerView.scrollToPosition(position);
}
}
});
}
/**
* 填充數據
*
* @param data
* @return
*/
private List<MedicineBean> filledData(String[] data) {
List<MedicineBean> mSortList = new ArrayList<MedicineBean>();
for (int i = 0; i < data.length; i++) {
MedicineBean medicineBean = new MedicineBean();
medicineBean.setName(data[i]);
//漢字轉換成拼音
String[] pinyin = PinyinHelper.toHanyuPinyinStringArray(data[i].toCharArray()[0]);
String sortString = pinyin[0].substring(0, 1).toUpperCase();
// 正則表達式,判斷首字母是否是英文字母
if (sortString.matches("[A-Z]")) {
medicineBean.setLetter(sortString.toUpperCase());
} else {
medicineBean.setLetter("#");
}
mSortList.add(medicineBean);
}
return mSortList;
}
}
最後貼出源碼的下載地址:http://download.csdn.net/detail/lylodyf/9695270