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

公司项目要实现这个效果:Android实现Recycleview悬浮粘性头部外加右侧字母导航

图一是开始的画面,图二是滑动的画面,点击右侧字母需要滑动左侧到指定位置,然后左侧的顶部字母A,B等需要悬浮。

实现思路:

右侧的联动可以用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悬浮粘性头部外加右侧字母导航 的全部内容, 来源链接: utcz.com/p/241895.html

回到顶部