温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Android如何实现Recycleview悬浮粘性头部外加右侧字母导航

发布时间:2021-09-27 11:30:12 来源:亿速云 阅读:148 作者:小新 栏目:编程语言

这篇文章主要介绍Android如何实现Recycleview悬浮粘性头部外加右侧字母导航,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

实现思路:

右侧的联动可以用recycyeview中adapter的scrollToPositionWithOffset方法实现。左侧就是recycleview,后台返回的城市数据是这种类型的:

复制代码 代码如下:

{"returnCode":1,"returnMsg":"操作成功","data":[{"startWord":"A","trainCityList":[{"cityId":531,"cityName":"昂昂溪","code":null},{"cityId":2137,

我进行了一层封装

1.建立实体类用来封装下标和城市名字:

public class ContactModel { private String index; private String name; public ContactModel(String name){  this.index = NewFirstLetterUtil.getFirstLetter(name);  this.name = name; } public String getIndex() {  return index; } public String getName() {  return name; } }

2.讲服务器返回的数据进行封装:

List<ContactModel> contacts = new ArrayList<>();   for (int i=0;i<mTrainCityList.size();i++){   // ContactModel contactModel = new ContactModel(mTrainCityList.get(i).getCityName());   contacts.add(new ContactModel(mTrainCityList.get(i).getCityName()));   Collections.sort(contacts, new LetterComparator());     }    mContactModels.addAll(contacts);    mShowModels.addAll(mContactModels);

3.设置适配器

private void setNewAdapter() {   ContactsAdapter mAdapter = new ContactsAdapter(mShowModels);   mMainRecycleview.setLayoutManager(new LinearLayoutManager(this));   final StickyRecyclerHeadersDecoration headersDecor = new StickyRecyclerHeadersDecoration(mAdapter);   mMainRecycleview.addItemDecoration(headersDecor);   mAdapter.setOnItemClickListtener(new ContactsAdapter.OnItemClickListtener() {    @Override    public void onItemClick(int pos) {     // Toast.makeText(TrainNewStartActivity.this,"惦记的pos:"+pos+"数据:"+mShowModels.get(pos).getName(),Toast.LENGTH_SHORT).show();     Intent intent = new Intent();     intent.putExtra("data", mShowModels.get(pos).getName());     Log.d("lwp","data:"+mShowModels.get(pos).getName());     setResult(RESULT_OK, intent);     finish();    }   });   mMainRecycleview.setAdapter(mAdapter);   mMain_side_bar.setLazyRespond(false);   // 侧边设置相关   mMain_side_bar.setOnSelectIndexItemListener(new WaveSideBarView.OnSelectIndexItemListener() {    @Override    public void onSelectIndexItem(String letter) {     for (int i = 0; i< mContactModels.size(); i++) {      if (mContactModels.get(i).getIndex().equals(letter)) {       ((LinearLayoutManager) mMainRecycleview.getLayoutManager()).scrollToPositionWithOffset(i, 0);       return;      }     }    }   });}

4.适配器代码:

public class ContactsAdapter extends RecyclerView.Adapter<ContactsAdapter.ContactsViewHolder> implements StickyRecyclerHeadersAdapter { private List<ContactModel> contacts; private static final String TAG = "ContactsAdapter"; private ContactModel contact; public ContactsAdapter(List<ContactModel> contacts) {  this.contacts = contacts; } @Override public ContactsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  LayoutInflater inflater = LayoutInflater.from(parent.getContext());  View view = inflater.inflate(R.layout.layaout_item_contacts, null);  return new ContactsViewHolder(view); } @Override public void onBindViewHolder(ContactsViewHolder holder, final int position) {  contact = contacts.get(position);  Log.e(TAG, "onBindViewHolder: index:" + contact.getIndex());  if (position == 0 || !contacts.get(position-1).getIndex().equals(contact.getIndex())) {   holder.tvIndex.setVisibility(View.GONE);   holder.tvIndex.setText(contact.getIndex());  } else {   holder.tvIndex.setVisibility(View.GONE);  }  holder.tvName.setText(contact.getName());  holder.tvName.setOnClickListener(new View.OnClickListener() {   @Override   public void onClick(View v) {    Log.d("lwp","惦记的pos:"+position);    onItemClickListtener.onItemClick(position);   }  }); } public interface OnItemClickListtener{  void onItemClick(int pos); } public OnItemClickListtener onItemClickListtener; public void setOnItemClickListtener(OnItemClickListtener onItemClickListtener) {  this.onItemClickListtener = onItemClickListtener; } @Override public long getHeaderId(int position) {  if (contacts.get(position).getIndex().equals("A")){   return 0;  }else if (contacts.get(position).getIndex().equals("B")){   return 1;  }else if (contacts.get(position).getIndex().equals("C")){   return 2;  }else if (contacts.get(position).getIndex().equals("D")){   return 3;  }else if (contacts.get(position).getIndex().equals("E")){   return 4;  }else if (contacts.get(position).getIndex().equals("F")){   return 5;  }else if (contacts.get(position).getIndex().equals("G")){   return 6;  }else if (contacts.get(position).getIndex().equals("H")){   return 7;  }else if (contacts.get(position).getIndex().equals("I")){   return 8;  }else if (contacts.get(position).getIndex().equals("J")){   return 9;  }else if (contacts.get(position).getIndex().equals("K")){   return 10;  }else if (contacts.get(position).getIndex().equals("L")){   return 11;  }else if (contacts.get(position).getIndex().equals("M")){   return 12;  }else if (contacts.get(position).getIndex().equals("N")){   return 13;  }else if (contacts.get(position).getIndex().equals("O")){   return 14;  }else if (contacts.get(position).getIndex().equals("P")){   return 15;  }else if (contacts.get(position).getIndex().equals("Q")){   return 16;  }else if (contacts.get(position).getIndex().equals("R")){   return 17;  }else if (contacts.get(position).getIndex().equals("S")){   return 18;  }else if (contacts.get(position).getIndex().equals("T")){   return 19;  }else if (contacts.get(position).getIndex().equals("U")){   return 20;  }else if (contacts.get(position).getIndex().equals("V")){   return 21;  }else if (contacts.get(position).getIndex().equals("Y")){   return 22;  }else if (contacts.get(position).getIndex().equals("X")){   return 23;  }else if (contacts.get(position).getIndex().equals("Y")){   return 24;  }else if (contacts.get(position).getIndex().equals("Z")){   return 25;  }else {   return -1;  } } @Override public RecyclerView.ViewHolder onCreateHeaderViewHolder(ViewGroup parent) {  View view = LayoutInflater.from(parent.getContext())    .inflate(R.layout.view_header, parent, false);  return new RecyclerView.ViewHolder(view) {  }; } @Override public void onBindHeaderViewHolder(RecyclerView.ViewHolder holder, int position) {  TextView textView = (TextView) holder.itemView;  textView.setText(String.valueOf(contacts.get(position).getIndex())); } @Override public int getItemCount() {  return contacts.size(); } class ContactsViewHolder extends RecyclerView.ViewHolder {  TextView tvIndex;  ImageView ivAvatar;  TextView tvName;  ContactsViewHolder(View itemView) {   super(itemView);   tvIndex = (TextView) itemView.findViewById(R.id.tv_index);   ivAvatar = (ImageView) itemView.findViewById(R.id.iv_avatar);   tvName = (TextView) itemView.findViewById(R.id.tv_name);  } }}

5.两个布局文件:

layaout_item_contacts.xml:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView  android:id="@+id/tv_index"  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:paddingLeft="12dp"  android:text="A"  android:textSize="14sp"  android:background="#E0E0E0"/> <RelativeLayout  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:background="?android:attr/selectableItemBackground">  <ImageView   android:id="@+id/iv_avatar"   android:layout_width="40dp"   android:layout_height="40dp"   android:visibility="gone"   android:layout_margin="10dp"   />  <TextView   android:id="@+id/tv_name"   android:layout_marginLeft="12dp"   android:layout_width="wrap_content"   android:layout_height="34dp"   android:layout_toRightOf="@+id/iv_avatar"   android:text="南尘"   android:gravity="center_vertical"   android:textColor="#424242"   android:textSize="16sp"   android:layout_centerVertical="true" /> </RelativeLayout> <View  android:layout_width="match_parent"  android:layout_height="1dp"  android:layout_marginLeft="15dp"  android:background="#e8e8e8" /></LinearLayout>

view_header.xml:

<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingLeft="12dp" android:textSize="14sp" android:textStyle="bold" android:background="#E0E0E0" tools:text="Animals starting with A" />

采用的第三方:

compile 'com.github.nanchen2251:WaveSideBar:1.0.6'compile 'com.timehop.stickyheadersrecyclerview:library:0.4.3@aar'

右侧字母用的是wavesidebar,但是由于不太符合设计图,所有我没有用他的,而是自己拿过来重新定义了(该类没提供修改,建议完善),如下:

public class SlfWaveSlideBarView extends View { private final static int DEFAULT_TEXT_SIZE = 14; // sp private final static int DEFAULT_MAX_OFFSET = 80; //dp private final static String[] DEFAULT_INDEX_ITEMS = {"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 String[] mIndexItems; /**  * the index in {@link #mIndexItems} of the current selected index item,  * it's reset to -1 when the finger up  */ private int mCurrentIndex = -1; /**  * Y coordinate of the point where finger is touching,  * the baseline is top of {@link #mStartTouchingArea}  * it's reset to -1 when the finger up  */ private float mCurrentY = -1; private Paint mPaint; private int mTextColor; private float mTextSize; /**  * the height of each index item  */ private float mIndexItemHeight; /**  * offset of the current selected index item  */ private float mMaxOffset; /**  * {@link #mStartTouching} will be set to true when {@link MotionEvent#ACTION_DOWN}  * happens in this area, and the side bar should start working.  */ private RectF mStartTouchingArea = new RectF(); /**  * height and width of {@link #mStartTouchingArea}  */ private float mBarHeight; private float mBarWidth; /**  * Flag that the finger is starting touching.  * If true, it means the {@link MotionEvent#ACTION_DOWN} happened but  * {@link MotionEvent#ACTION_UP} not yet.  */ private boolean mStartTouching = false; /**  * if true, the {@link WaveSideBarView.OnSelectIndexItemListener#onSelectIndexItem(String)}  * will not be called until the finger up.  * if false, it will be called when the finger down, up and move.  */ private boolean mLazyRespond = false; /**  * the position of the side bar, default is {@link #POSITION_RIGHT}.  * You can set it to {@link #POSITION_LEFT} for people who use phone with left hand.  */ private int mSideBarPosition; public static final int POSITION_RIGHT = 0; public static final int POSITION_LEFT = 1; /**  * the alignment of items, default is {@link #TEXT_ALIGN_CENTER}.  */ private int mTextAlignment; public static final int TEXT_ALIGN_CENTER = 0; public static final int TEXT_ALIGN_LEFT = 1; public static final int TEXT_ALIGN_RIGHT = 2; /**  * observe the current selected index item  */ private WaveSideBarView.OnSelectIndexItemListener onSelectIndexItemListener; /**  * the baseline of the first index item text to draw  */ private float mFirstItemBaseLineY; /**  * for {@link #dp2px(int)} and {@link #sp2px(int)}  */ private DisplayMetrics mDisplayMetrics; public SlfWaveSlideBarView(Context context) {  this(context, null); } public SlfWaveSlideBarView(Context context, AttributeSet attrs) {  this(context, attrs, 0); } public SlfWaveSlideBarView(Context context, AttributeSet attrs, int defStyleAttr) {  super(context, attrs, defStyleAttr);  mDisplayMetrics = context.getResources().getDisplayMetrics();  TypedArray typedArray = context.obtainStyledAttributes(attrs, com.nanchen.wavesidebar.R.styleable.WaveSideBarView);  mLazyRespond = typedArray.getBoolean(com.nanchen.wavesidebar.R.styleable.WaveSideBarView_sidebar_lazy_respond, false);  mTextColor = typedArray.getColor(com.nanchen.wavesidebar.R.styleable.WaveSideBarView_sidebar_text_color, Color.GRAY);  mMaxOffset = typedArray.getDimension(com.nanchen.wavesidebar.R.styleable.WaveSideBarView_sidebar_max_offset, dp2px(DEFAULT_MAX_OFFSET));  mSideBarPosition = typedArray.getInt(com.nanchen.wavesidebar.R.styleable.WaveSideBarView_sidebar_position, POSITION_RIGHT);  mTextAlignment = typedArray.getInt(com.nanchen.wavesidebar.R.styleable.WaveSideBarView_sidebar_text_alignment, TEXT_ALIGN_CENTER);  typedArray.recycle();  mTextSize = sp2px(DEFAULT_TEXT_SIZE);  mIndexItems = DEFAULT_INDEX_ITEMS;  initPaint(); } private void initPaint() {  mPaint = new Paint();  mPaint.setAntiAlias(true);  mPaint.setColor(mTextColor);  mPaint.setTextSize(mTextSize);  switch (mTextAlignment) {   case TEXT_ALIGN_CENTER: mPaint.setTextAlign(Paint.Align.CENTER); break;   case TEXT_ALIGN_LEFT: mPaint.setTextAlign(Paint.Align.LEFT); break;   case TEXT_ALIGN_RIGHT: mPaint.setTextAlign(Paint.Align.RIGHT); break;  } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  super.onMeasure(widthMeasureSpec, heightMeasureSpec);  int height = MeasureSpec.getSize(heightMeasureSpec);  int width = MeasureSpec.getSize(widthMeasureSpec);  Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();  mIndexItemHeight = fontMetrics.bottom - fontMetrics.top;  mBarHeight = mIndexItems.length * mIndexItemHeight;  // calculate the width of the longest text as the width of side bar  for (String indexItem : mIndexItems) {   mBarWidth = Math.max(mBarWidth, mPaint.measureText(indexItem));  }  float areaLeft = (mSideBarPosition == POSITION_LEFT) ? 0 : (width - mBarWidth - getPaddingRight());  float areaRight = (mSideBarPosition == POSITION_LEFT) ? (getPaddingLeft() + areaLeft + mBarWidth) : width;  float areaTop = height/2 - mBarHeight/2;  float areaBottom = areaTop + mBarHeight;  mStartTouchingArea.set(    areaLeft,    areaTop,    areaRight,    areaBottom);  // the baseline Y of the first item' text to draw  mFirstItemBaseLineY = (height/2 - mIndexItems.length*mIndexItemHeight/2)    + (mIndexItemHeight/2 - (fontMetrics.descent-fontMetrics.ascent)/2)    - fontMetrics.ascent; } @Override protected void onDraw(Canvas canvas) {  super.onDraw(canvas);  // draw each item  for (int i = 0, mIndexItemsLength = mIndexItems.length; i < mIndexItemsLength; i++) {   float baseLineY = mFirstItemBaseLineY + mIndexItemHeight*i;   // calculate the scale factor of the item to draw   float scale = getItemScale(i);   int alphaScale = (i == mCurrentIndex) ? (255) : (int) (255 * (1-scale));   mPaint.setAlpha(alphaScale);   mPaint.setTextSize(mTextSize + mTextSize*scale);   float baseLineX = 0f;   if (mSideBarPosition == POSITION_LEFT) {    switch (mTextAlignment) {     case TEXT_ALIGN_CENTER:      baseLineX = getPaddingLeft() + mBarWidth/2 + mMaxOffset*scale;      break;     case TEXT_ALIGN_LEFT:      baseLineX = getPaddingLeft() + mMaxOffset*scale;      break;     case TEXT_ALIGN_RIGHT:      baseLineX = getPaddingLeft() + mBarWidth + mMaxOffset*scale;      break;    }   } else {    switch (mTextAlignment) {     case TEXT_ALIGN_CENTER:      baseLineX = getWidth() - getPaddingRight() - mBarWidth/2 - mMaxOffset*scale;      break;     case TEXT_ALIGN_RIGHT:      baseLineX = getWidth() - getPaddingRight() - mMaxOffset*scale;      break;     case TEXT_ALIGN_LEFT:      baseLineX = getWidth() - getPaddingRight() - mBarWidth - mMaxOffset*scale;      break;    }   }   // draw   canvas.drawText(     mIndexItems[i], //item text to draw     baseLineX, //baseLine X     baseLineY, // baseLine Y     mPaint);  }  // reset paint  mPaint.setAlpha(255);  mPaint.setTextSize(mTextSize); } /**  * calculate the scale factor of the item to draw  *  * @param index the index of the item in array {@link #mIndexItems}  * @return the scale factor of the item to draw  */ private float getItemScale(int index) {  float scale = 0;  if (mCurrentIndex != -1) {   float distance = Math.abs(mCurrentY - (mIndexItemHeight*index+mIndexItemHeight/2)) / mIndexItemHeight;   scale = 1 - distance*distance/16;   scale = Math.max(scale, 0);  }  return scale; } @Override public boolean onTouchEvent(MotionEvent event) {  if (mIndexItems.length == 0) {   return super.onTouchEvent(event);  }  float eventY = event.getY();  float eventX = event.getX();  mCurrentIndex = getSelectedIndex(eventY);  switch (event.getAction()) {   case MotionEvent.ACTION_DOWN:    if (mStartTouchingArea.contains(eventX, eventY)) {     mStartTouching = true;     if (!mLazyRespond && onSelectIndexItemListener != null) {      onSelectIndexItemListener.onSelectIndexItem(mIndexItems[mCurrentIndex]);     }    //  invalidate();     return true;    } else {     mCurrentIndex = -1;     return false;    }   case MotionEvent.ACTION_MOVE:    if (mStartTouching && !mLazyRespond && onSelectIndexItemListener != null) {     onSelectIndexItemListener.onSelectIndexItem(mIndexItems[mCurrentIndex]);    }   //  invalidate();    return true;   case MotionEvent.ACTION_UP:   case MotionEvent.ACTION_CANCEL:    if (mLazyRespond && onSelectIndexItemListener != null) {     onSelectIndexItemListener.onSelectIndexItem(mIndexItems[mCurrentIndex]);    }    mCurrentIndex = -1;    mStartTouching = false;   //  invalidate();    return true;  }  return super.onTouchEvent(event); } private int getSelectedIndex(float eventY) {  mCurrentY = eventY - (getHeight()/2 - mBarHeight /2);  if (mCurrentY <= 0) {   return 0;  }  int index = (int) (mCurrentY / this.mIndexItemHeight);  if (index >= this.mIndexItems.length) {   index = this.mIndexItems.length - 1;  }  return index; } private float dp2px(int dp) {  return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, this.mDisplayMetrics); } private float sp2px(int sp) {  return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, this.mDisplayMetrics); } public void setIndexItems(String... indexItems) {  mIndexItems = Arrays.copyOf(indexItems, indexItems.length);  requestLayout(); } public void setTextColor(int color) {  mTextColor = color;  mPaint.setColor(color);  invalidate(); } public void setPosition(int position) {  if (position != POSITION_RIGHT && position != POSITION_LEFT) {   throw new IllegalArgumentException("the position must be POSITION_RIGHT or POSITION_LEFT");  }  mSideBarPosition = position;  requestLayout(); } public void setMaxOffset(int offset) {  mMaxOffset = offset;  invalidate(); } public void setLazyRespond(boolean lazyRespond) {  mLazyRespond = lazyRespond; } public void setTextAlign(int align) {  if (mTextAlignment == align) {   return;  }  switch (align) {   case TEXT_ALIGN_CENTER: mPaint.setTextAlign(Paint.Align.CENTER); break;   case TEXT_ALIGN_LEFT: mPaint.setTextAlign(Paint.Align.LEFT); break;   case TEXT_ALIGN_RIGHT: mPaint.setTextAlign(Paint.Align.RIGHT); break;   default:    throw new IllegalArgumentException(      "the alignment must be TEXT_ALIGN_CENTER, TEXT_ALIGN_LEFT or TEXT_ALIGN_RIGHT");  }  mTextAlignment = align;  invalidate(); } public void setOnSelectIndexItemListener(WaveSideBarView.OnSelectIndexItemListener onSelectIndexItemListener) {  this.onSelectIndexItemListener = onSelectIndexItemListener; } public interface OnSelectIndexItemListener {  void onSelectIndexItem(String letter); }}

以上是“Android如何实现Recycleview悬浮粘性头部外加右侧字母导航”这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注亿速云行业资讯频道!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI