IT技術互(hu)動交流(liu)平台(tai)

九州体彩官网

來(lai)源︰IT165收集  發布日期(qi)︰2020-02-20 07:14:50

首(shou)先看看實現的(de)效果(guo)

可(ke)以看出要實現上面效果(guo),有(you)三個步(bu)驟︰

1.漢(han)字轉化為拼(pin)音,並且根(gen)據首(shou)字母排序

2.用ItemDecoration實現字母行的(de)顯示

3.自定義(yi)實現右(you)側的(de)按字母導(dao)航欄

當然重點講bu)temDecoration的(de)實現。都知道(dao)RecyclerView本(ben)身都沒有(you)分割線,需要分割線都是在item中畫一條線或者(zhe)使用ItemDecoration來(lai)實現zhi)指釹摺TecyclerView中我們(men)可(ke)以給每(mei)一個item都添加ItemDecoration,所以可(ke)以自定義(yi)ItemDecoration來(lai)實現各種我們(men)所需要的(de)效果(guo)。

ItemDecoration

ItemDecoration是RecyclerView內部的(de)一個抽象類,要實現zhong)飧齔橄罄嘧勻恍枰 迪幟誆康de)抽象方法,除(chu)了deprecated的(de)方法只有(you)下(xia)面三個方法︰

1.void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)

這個方法是用來(lai)指定每(mei)一個的(de)item對應decoration的(de)大小區域,主(zhu)要實現zhi)絞shi)就(jiu)是設置outRect的(de)left、top、right、bottom,如果(guo)一個item不需要decoration把(ba)outRect的(de)上下(xia)左右(you)設置為0即可(ke)。盜用網上一張圖(tu)看看outRect具(ju)體什(shi)麼意思

2.void onDraw(Canvas c, RecyclerView parent, State state)

onDraw方法看名(ming)字大家都應該很熟悉(xi),這個方法自然是用來(lai)畫具(ju)體的(de)ItemDecoration的(de),繪制的(de)內容是顯示在itemView的(de)下(xia)層。下(xia)層什(shi)麼意思,待會來(lai)看看。

3.void onDrawOver(Canvas c, RecyclerView parent, State state)

也(ye)是一個繪制的(de)方法,不過是繪制在itemView的(de)上層

以上三個方法的(de)調用順序也(ye)就(jiu)是按照上面的(de)排pa)械de)順序來(lai)調用的(de)。

首(shou)先來(lai)看看用ItemDecoration實現的(de)分割線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) { //獲取系統的(de)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) { //左邊(bian)緣距離RecyclerView左邊(bian)的(de)距離 final int left = parent.getPaddingLeft(); //右(you)邊(bian)緣距離RecyclerView右(you)邊(bian)邊(bian)的(de)距離 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;  //去(qu)掉(diao)最後一條的(de)分割線  if (i == childCount - 1) {//bottom和top相等,即高(gao)度為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); } }}
主(zhu)要實現了getItemOffsets和onDraw方法,因(yin)為這兩(liang)個方法已(yi)經能(neng)滿足了,自然不需要全部方法實現。在getItemOffsets方法中判斷是豎直方向(xiang)還是水平方向(xiang)的(de)分割線,豎直方向(xiang)只需要在outRect的(de)bottom加you)戲指釹叩de)寬(kuan)度即可(ke),當然水平分割線在右(you)邊(bian)加you)暇jiu)OK。在onDraw方法中具(ju)體的(de)畫出分割線,不知道(dao)大家有(you)沒有(you)想過這里的(de)分割線高(gao)度和outRect中設置的(de)高(gao)度有(you)什(shi)麼關系,那麼下(xia)面修改一下(xia)代碼實驗一下(xia)。

 

在上面onDraw畫豎直分割線的(de)方法中,把(ba)分割線高(gao)度加you)0px,即︰

 

mDivider.setBounds(left, top, right, bottom+80);

 

效果(guo)沒變(bian)化?意思是onDraw里面畫的(de)區域大小不會超過outRect設置的(de)大小嗎(ma)?記(ji)得之前說(shuo)過,onDraw方法繪制的(de)內容是在itemView的(de)下(xia)層的(de),會不會是被itemView遮擋而沒有(you)顯示出來(lai)呢,那麼下(xia)面我們(men)把(ba)itemView的(de)背景色改為透明,看看效果(guo)

android:background="@android:color/transparent"

 

這次看到(dao)了不同(tong)了吧,正是我們(men)所猜想的(de)那樣。也(ye)就(jiu)證明了onDraw方法顯示的(de)內容實在itemView的(de)下(xia)層,同(tong)時它繪制的(de)內容並不是不會超過outRect指定的(de)區域,而outRect指定的(de)區域也(ye)是實dao)史峙涓temDecoration的(de)區域,在這個區域繪制才不會影響itemView,所以onDraw繪制的(de)內容我們(men)應該要保持和outRect給定的(de)區域是相同(tong)的(de)。

顯示字母的(de)ItemDecoration

現在來(lai)看看顯示字母的(de)ItemDecoration是怎麼實現的(de)。看上面的(de)效果(guo)可(ke)以發現,最上面始(shi)終(zhong)顯示了一個ItemDecoration,上面說(shuo)過onDrawOver方法繪制的(de)內容是顯示在最上層,所以用這個方法來(lai)繪制最上面再適合不過了。其他itemView顯示字母的(de)ItemDecoration也(ye)並不是采用onDraw方法繪制,而是用xml實現的(de),因(yin)為采用xml方式(shi)來(lai)實現可(ke)以更(geng)方便的(de)來(lai)定制ItemDecoration的(de)內容,也(ye)可(ke)以實現其中的(de)點擊事件。

在看ItemDecoration之前,先看看所用到(dao)的(de)一個接口和RecyclerView的(de)Adapter

 

public interface StickyHeaderAdapter<T extends RecyclerView.ViewHolder> { String getHeaderId(int position); T onCreateHeaderViewHolder(ViewGroup parent); void onBindHeaderViewHolder(T viewholder, int position);}
這個接口里面有(you)三個方法,第一個方法是獲取headerId,因(yin)為在顯示是不可(ke)能(neng)每(mei)一個item都要顯示decoration,只有(you)每(mei)種首(shou)字母第一個才顯示,所用這里需要一個id來(lai)判斷是否需要設置ItemDecoration。後面兩(liang)個方法是仿(fang)照RecyclerView.Adapter的(de)寫的(de),因(yin)為我們(men)采用ItemDecoration布局(ju)用xml實現,如果(guo)需要顯示的(de)ItemDecoration很多的(de)話,每(mei)次都需要去(qu)用LayoutInflater去(qu)加載(zai)布局(ju),顯然不夠優雅,所用用holder機制ping)詞迪指從謾Oxia)面來(lai)看看我們(men)的(de)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//生成(cheng)header的(de)布局(ju) public HeaderHolder onCreateHeaderViewHolder(ViewGroup parent) { return new HeaderHolder(mInflater.inflate(R.layout.item_decoration, parent, false)); } @Override//綁(bang)定header的(de)數據 public void onBindHeaderViewHolder(HeaderHolder viewholder, int position) { viewholder.header.setText(mDatas.get(position).getLetter()); } /** * 根(gen)據分類的(de)首(shou)字母獲取其第一次出現該首(shou)字母的(de)位(wei)置 */ 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; } }}
可(ke)以看到(dao)這個Adapter實現了之前上面的(de)接口,接口的(de)實現zhi)絞shi)和普通的(de)Adapter的(de)實現都類似在onCreateHeaderViewHolder中加載(zai)xml文件,在onBindHeaderViewHolder中加載(zai)數據,HeaderHolder也(ye)是一樣的(de),headerId是采用的(de)一個letter的(de),也(ye)就(jiu)是每(mei)一條的(de)首(shou)字母。下(xia)面看看ItemDecoration是怎麼實現的(de)。

 

getItemOffsets方法

首(shou)先看看第一個getItemOffsets方法的(de)實現

 

 @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); //得到(dao)該item所在的(de)位(wei)置 int position = parent.getChildAdapterPosition(view); int headerHeight = 0; //在使用adapterPosition時最好(hao)的(de)加you)險飧讎卸if (position != RecyclerView.NO_POSITION && hasHeader(position)) {  //獲取到(dao)ItemDecoration所需要的(de)高(gao)度  View header = getHeader(parent, position).itemView;  headerHeight = header.getHeight(); } outRect.set(0, headerHeight, 0, 0); }
很簡(jian)單(dan),就(jiu)是判斷如果(guo)這個item需要ItemDecoration就(jiu)獲取到(dao)header的(de)高(gao)度,設置給outRect

 

判斷是否需要header

判斷是否需要header的(de)方法,之前an)皇竊dapter里面寫了getHeaderId的(de)方法嗎(ma),這里就(jiu)用到(dao)了,根(gen)據前兩(liang)個headerId是否相同(tong)來(lai)判斷是否需要設置ItemDecoration

 

private boolean hasHeader(int position) { if (position == 0) {//第一個位(wei)置必然有(you)  return true; } //判斷和上一個的(de)id不同(tong)則有(you)header int previous = position - 1; return !mAdapter.getHeaderId(position).equals(mAdapter.getHeaderId(previous)); }

獲取Header的(de)方法

 

private RecyclerView.ViewHolder getHeader(RecyclerView parent, int position) { //創建HeaderViewHolder MedicineAdapter.HeaderHolder holder = mAdapter.onCreateHeaderViewHolder(parent); final View header = holder.itemView; //綁(bang)定數據 mAdapter.onBindHeaderViewHolder(holder, position); //測(ce)量View並且layout int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY); int heightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED); //根(gen)據父View的(de)MeasureSpec和子view自身的(de)LayoutParams以及padding來(lai)獲取子View的(de)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); //進行測(ce)量 header.measure(childWidth, childHeight); //根(gen)據測(ce)量後的(de)寬(kuan)高(gao)放置位(wei)置 header.layout(0, 0, header.getMeasuredWidth(), header.getMeasuredHeight()); return holder; }
在這里面調用了之前Adapter實現接口里面的(de)方法,創建了ViewHolder,綁(bang)定了數據。都知道(dao)自定義(yi)view需要實現onMeasure、onLayout、onDraw方法,所以在這里對它進行了測(ce)量和放置,而draw是在onDrawOver里面實現的(de)。對這里熟悉(xi)的(de)可(ke)以去(qu)看看相關的(de)知識點。

 

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);  //只有(you)在最上面一個item或者(zhe)有(you)header的(de)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();  } } }
這里就(jiu)是一個循環(huan),在需要Header的(de)地方進行繪制,當然需要把(ba)畫布移動到(dao)要繪制的(de)位(wei)置,主(zhu)要是確定它距離頂部的(de)大小。

 

 

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) {//處理最上面兩(liang)個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)) { //找到(dao)下(xia)一個不同(tong)類的(de)view   final View next = parent.getChildAt(i);   //這里計(ji)算offset畫個圖(tu)會很清(qing)楚   final int offset = ((int) next.getY()) - (headerHeight + getHeader(parent, adapterPosHere).itemView.getHeight());   if (offset < 0) {//如果(guo)大于0的(de)話,此時並沒有(you)切換    return offset;   } else {    break;   }   }  }  }  //top不能(neng)小于0,否則最上面的(de)ItemDecoration不會一直存在  top = Math.max(0, top); } return top; }
這里的(de)邏輯是這樣的(de)︰

 

1.當此view不是最上面的(de)顯示的(de)時候,header距離頂部直接就(jiu)是此view距離頂部距離減去(qu)header的(de)高(gao)度即可(ke)
2.當此view是最上面的(de)view的(de)時候,首(shou)先用for循環(huan)找到(dao)它下(xia)一個和它headerId不同(tong)的(de)第一個view,利用找到(dao)的(de)這個view和它本(ben)身來(lai)計(ji)算出它的(de)header距離頂部的(de)距離,當這個距離大于0時,代表此view的(de)header還全部顯示出來(lai),這時直接用上面的(de)方式(shi)獲取這個距離,當這個距離小于0時ben)褪俏頤men)所希望的(de)。


OK完成(cheng),下(xia)面貼出ItemDecoration的(de)所以代碼

 

/** * 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; } //最後調用 繪制頂部固定的(de)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);  //只有(you)在最上面一個item或者(zhe)有(you)header的(de)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); //得到(dao)該item所在的(de)位(wei)置 int position = parent.getChildAdapterPosition(view); int headerHeight = 0; //在使用adapterPosition時最好(hao)的(de)加you)險飧讎卸if (position != RecyclerView.NO_POSITION && hasHeader(position)) {  //獲取到(dao)ItemDecoration所需要的(de)高(gao)度  View header = getHeader(parent, position).itemView;  headerHeight = header.getHeight(); } outRect.set(0, headerHeight, 0, 0); } /** * 判斷是否有(you)header * * @param position * @return */ private boolean hasHeader(int position) { if (position == 0) {//第一個位(wei)置必然有(you)  return true; } //判斷和上一個的(de)id不同(tong)則有(you)header int previous = position - 1; return !mAdapter.getHeaderId(position).equals(mAdapter.getHeaderId(previous)); } /** * 獲得自定義(yi)的(de)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; //綁(bang)定數據 mAdapter.onBindHeaderViewHolder(holder, position); //測(ce)量View並且layout int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY); int heightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED); //根(gen)據父View的(de)MeasureSpec和子view自身的(de)LayoutParams以及padding來(lai)獲取子View的(de)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); //進行測(ce)量 header.measure(childWidth, childHeight); //根(gen)據測(ce)量後的(de)寬(kuan)高(gao)放置位(wei)置 header.layout(0, 0, header.getMeasuredWidth(), header.getMeasuredHeight()); return holder; } /** * 計(ji)算距離頂部的(de)高(gao)度 * * @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) {//處理最上面兩(liang)個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);   //找到(dao)下(xia)一個不同(tong)類的(de)view   if (!nextId.equals(currentId)) {   final View next = parent.getChildAt(i);   //這里計(ji)算offset畫個圖(tu)會很清(qing)楚   final int offset = ((int) next.getY()) - (headerHeight + getHeader(parent, adapterPosHere).itemView.getHeight());   if (offset < 0) {//如果(guo)大于0的(de)話,此時並沒有(you)切換    return offset;   } else {    break;   }   }  }  }  //top不能(neng)小于0,否則最上面的(de)ItemDecoration不會一直存在  top = Math.max(0, top); } return top; }}

漢(han)字轉拼(pin)音

通過一個三方的(de)類庫pinyin4j來(lai)實現,這里就(jiu)是調用方法PinyinHelper.toHanyuPinyinStringArray獲取一個漢(han)字的(de)拼(pin)音,然後得到(dao)第一個英文字母並轉化為大寫

 

 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]);  //漢(han)字轉換成(cheng)拼(pin)音  String[] pinyin = PinyinHelper.toHanyuPinyinStringArray(data[i].toCharArray()[0]);  String sortString = pinyin[0].substring(0, 1).toUpperCase();  // 正則表達式(shi),判斷首(shou)字母是否是英文字母  if (sortString.matches("[A-Z]")) {  medicineBean.setLetter(sortString.toUpperCase());  } else {  medicineBean.setLetter("#");  }  mSortList.add(medicineBean); } return mSortList; }


右(you)邊(bian)的(de)字母導(dao)航欄

 

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); // 獲取焦點改變(bian)背景ba)丈 int height = getHeight();// 獲取對應高(gao)度 int width = getWidth(); // 獲取對應寬(kuan)度 int singleHeight = height / b.length;// 獲取每(mei)一個字母的(de)高(gao)度 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);  // 選中的(de)狀(zhuang)態  if (i == choose) {  paint.setColor(Color.parseColor("#3399ff"));  paint.setFakeBoldText(true);  }  // x坐(zuo)標等于中間-字zhi) kuan)度的(de)一半.  float xPos = width / 2 - paint.measureText(b[i]) / 2;  float yPos = singleHeight * i + singleHeight;  canvas.drawText(b[i], xPos, yPos, paint);  paint.reset();// 重置畫筆(bi) } } @Override public boolean dispatchTouchEvent(MotionEvent event) { final int action = event.getAction(); final float y = event.getY();// 點擊y坐(zuo)標 final int oldChoose = choose; final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener; final int c = (int) (y / getHeight() * b.length);// 點擊y坐(zuo)標所佔總高(gao)度的(de)比例*b數組的(de)長度就(jiu)等于點擊b中的(de)個數. 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; } /** * 向(xiang)外公開的(de)方法 * * @param onTouchingLetterChangedListener */ public void setOnTouchingLetterChangedListener(  OnTouchingLetterChangedListener onTouchingLetterChangedListener) { this.onTouchingLetterChangedListener = onTouchingLetterChangedListener; } /** * 接口 * * @author coder */ public interface OnTouchingLetterChangedListener { void onTouchingLetterChanged(String s); }}
這是在網上找的(de)一個類,相信大家都能(neng)看懂,就(jiu)不多說(shuo)了,最後貼出MainActivity

 

 

public class MainActivity extends AppCompatActivity { private String[] mData = {"阿(a)魏八味丸", "阿(a)昔洛韋眼(yan)膏", "艾司洛爾(er)", "安(an) 啶注射液", "阿(a)達帕(pa)林", "參茸(rong)追風(feng)酒", "草烏", "石斛夜光丸",  "骨質shi)鏨pian)", "烏雞白鳳丸", "人參益(yi)母丸", "補脾益(yi)腸丸", "丹(dan)參片(pian)", "小金丸", "婦寧(ning)康", "糖脈康", "菲(fei)伯瑞(rui)", "乙肝解毒(du)片(pian)", "腦血栓片(pian)"}; 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)); //設置右(you)側觸摸監听 sideBar.setOnTouchingLetterChangedListener(new SideBar.OnTouchingLetterChangedListener() {  @Override  public void onTouchingLetterChanged(String s) {  //該字母首(shou)次出現的(de)位(wei)置  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]);  //漢(han)字轉換成(cheng)拼(pin)音  String[] pinyin = PinyinHelper.toHanyuPinyinStringArray(data[i].toCharArray()[0]);  String sortString = pinyin[0].substring(0, 1).toUpperCase();  // 正則表達式(shi),判斷首(shou)字母是否是英文字母  if (sortString.matches("[A-Z]")) {  medicineBean.setLetter(sortString.toUpperCase());  } else {  medicineBean.setLetter("#");  }  mSortList.add(medicineBean); } return mSortList; }}

最後貼出源碼的(de)下(xia)載(zai)地址(zhi)︰http://download.csdn.net/detail/lylodyf/9695270

延you)煸畝粒/h3>

    Tag標簽(qian)︰字母  
    • 九州体彩官网

    • Directx11 游戲編程入門教程
    • 專題(ti)主(zhu)要學習DirectX的(de)初級編程入門學習,對Directx11的(de)入門及初學者(zhe)有(you)...... 詳細
    About IT165 - 廣告服務 - 隱私聲明 - 版權申明 - 免(mian)責條款(kuan) - 網站地圖(tu) - 網友投稿 - 聯系方式(shi)
    本(ben)站內容來(lai)自于互(hu)聯網,僅供用于網絡技術學習,學習中請遵循相關法律法規
    九州体彩官网 | 下一页