Android-使用RecyclerView的ItemDecoration 实现炫酷的 吸顶效果


开始逐渐领略到ItemDecoration的美~

 

源码已上传至github,感兴趣的同学可以下载:

吸顶效果源码 https://github.com/junmei520/LSY_PullupRecyclerView

 

今天让我 使用 ItemDecoration 来完成 可推动的悬浮导航栏的效果,最终实现的效果如下图:


 

如果你对分类型的RecyclerView还不是太了解,可以参看我前面的文章,让你一分钟征服RecyclerView的基本使用

如果你对ItemDecoration的使用也不太了解,那就再看下我的另外两篇文章吧:

使用ItemDecoration为RecyclerView的Item设置padding使用ItemDecoration为RecyclerView的Item设置分割线。

总觉得循序渐进是一个不错的学习方法。

 

具体实现步骤如下:

根据我前面的文章所讲的RecyclerView的基本使用,我们先来完成基本的recyclerView

第一步:布局里写一个RecyclerView

 

第二步:实例化

 

recyclerView = (RecyclerView) findViewById(R.id.recyclerView);

 

第三步:获取所需的数据 (这里我们来个真实点的情景,去联网请求数据)

 

  1. /**
  2. * 联网请求所需的url
  3. */
  4. public String url="http://api.meituan.com/mmdb/movie/v2/list/rt/order/coming.json?ci=1&limit=12&token=&__vhost=api.maoyan.com&utm_campaign=AmovieBmovieCD-1&movieBundleVersion=6801&utm_source=xiaomi&utm_medium=android&utm_term=6.8.0&utm_content=868030022327462&net=255&dModel=MI%205&uuid=0894DE03C76F6045D55977B6D4E32B7F3C6AAB02F9CEA042987B380EC5687C43&lat=40.100673&lng=116.378619&__skck=6a375bce8c66a0dc293860dfa83833ef&__skts=1463704714271&__skua=7e01cf8dd30a179800a7a93979b430b2&__skno=1a0b4a9b-44ec-42fc-b110-ead68bcc2824&__skcy=sXcDKbGi20CGXQPPZvhCU3%2FkzdE%3D";
  1. //联网获取数据
  2. getDataFromNet();
  1. /**
  2. * 使用okhttpUtils进行联网请求数据
  3. */
  4. private void getDataFromNet() {
  5. OkHttpUtils.
  6. get()
  7. .url(url)
  8. .build()
  9. .execute( new StringCallback() {
  10. @Override
  11. public void onError(okhttp3.Call call, Exception e, int id) {
  12. Log.e( "TAG", "联网失败" + e.getMessage());
  13. }
  14.  
  15. @Override
  16. public void onResponse(String response, int id) {
  17. Log.e( "TAG", "联网成功==" + response);
  18.  
  19. //联网成功后使用fastjson解析
  20. processData(response);
  21. }
  22. });
  23. }
  1. /**
  2. * 使用fastjson进行解析
  3. *
  4. * @param json
  5. */
  6. private void processData(String json) {
  7. //这里使用GsonFormat生成对应的bean类
  8. JSONObject jsonObject = parseObject(json);
  9.  
  10. String data = jsonObject.getString( "data");
  11. JSONObject dataObj = JSON.parseObject(data);
  12.  
  13. String coming = dataObj.getString( "coming");
  14. List<WaitMVBean.DataBean.ComingBean> comingslist = parseArray(coming, WaitMVBean.DataBean.ComingBean.class);
  15.  
  16. //测试是否解析数据成功
  17. // String strTest = comingslist.get(0).getCat();
  18. // Log.e("TAG", strTest + "222");
  19.  
  20. //解析数据成功,设置适配器-->
  21.  
  22. }
  23.  
  24. }


第四步:解析数据成功后,创建并设置适配器,并传递相关数据

 

  1. //解析数据成功,设置适配器
  2. MyRecyclerAdapter adapter = new MyRecyclerAdapter( mContext,comingslist);
  3. recyclerView.setAdapter(adapter);

适配器:

 

  1. public class MyRecyclerAdapter extends RecyclerView.Adapter {
  2.  
  3. private final List<WaitMVBean.DataBean.ComingBean> comingslist;
  4. private final Context mContext;
  5. private final LayoutInflater mLayoutInflater;
  6.  
  7.  
  8. public MyRecyclerAdapter(Context mContext, List<WaitMVBean.DataBean.ComingBean> comingslist) {
  9. this.mContext = mContext;
  10. this.comingslist = comingslist;
  11. mLayoutInflater = LayoutInflater.from(mContext);
  12. }
  13.  
  14. @Override
  15. public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  16. return new MyViewHolder(mLayoutInflater.inflate(R.layout.date_item, null));
  17. }
  18.  
  19. @Override
  20. public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
  21. MyViewHolder myholder = (MyViewHolder) holder;
  22. myholder.setData(position);
  23. }
  24.  
  25. @Override
  26. public int getItemCount() {
  27. return comingslist.size();
  28. }
  29.  
  30. class MyViewHolder extends RecyclerView.ViewHolder {
  31. private TextView mv_name;
  32. private TextView mv_dec;
  33. private TextView mv_date;
  34. private ImageView imageView;
  35.  
  36. public MyViewHolder(View itemView) {
  37. super(itemView);
  38. mv_name = (TextView) itemView.findViewById(R.id.mv_name);
  39. mv_dec = (TextView) itemView.findViewById(R.id.mv_dec);
  40. mv_date = (TextView) itemView.findViewById(R.id.mv_date);
  41. imageView = (ImageView) itemView.findViewById(R.id.image);
  42. }
  43.  
  44. public void setData(int position) {
  45. WaitMVBean.DataBean.ComingBean coming = comingslist.get(position);
  46.  
  47. String name = coming.getNm();
  48. mv_name.setText(name);
  49.  
  50. String date = coming.getShowInfo();
  51. mv_date.setText(date);
  52.  
  53. String dec = coming.getScm();
  54. mv_dec.setText(dec);
  55.  
  56. //注:当你发下图片无法打开是,做个字符串替换即可
  57. String imagUrl = coming.getImg();
  58. String newImagUrl = imagUrl.replaceAll( "w.h", "50.80");
  59.  
  60. //使用Glide加载图片
  61. Glide.with(mContext)
  62. .load(newImagUrl)
  63. .into(imageView);
  64. }
  65. }
  66. }

item的布局:

 

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="wrap_content"
  4. android:layout_height="wrap_content"
  5. android:background="#ffffff"
  6. android:gravity="center_vertical"
  7. android:orientation="horizontal">
  8.  
  9. <ImageView
  10. android:id="@+id/image"
  11. android:layout_width="70dp"
  12. android:layout_height="110dp"
  13. android:layout_marginBottom="5dp"
  14. android:layout_marginLeft="10dp"
  15. android:layout_marginRight="8dp"
  16. android:layout_marginTop="5dp" />
  17.  
  18. <LinearLayout
  19. android:layout_width="0dp"
  20. android:layout_height="wrap_content"
  21. android:layout_marginLeft="6dp"
  22. android:layout_weight="1"
  23. android:orientation="vertical">
  24.  
  25. <TextView
  26. android:id="@+id/mv_name"
  27. android:layout_width="wrap_content"
  28. android:layout_height="wrap_content"
  29. android:text="神奇動物在哪裏"
  30. android:textColor="#000000"
  31. android:textSize="15sp" />
  32.  
  33. <LinearLayout
  34. android:layout_width="wrap_content"
  35. android:layout_height="wrap_content"
  36. android:orientation="horizontal">
  37.  
  38. <TextView
  39. android:layout_width="wrap_content"
  40. android:layout_height="wrap_content"
  41. android:text="观众"
  42. android:textColor="#55000000"
  43. android:textSize="14sp" />
  44.  
  45. <TextView
  46. android:id="@+id/tv_people"
  47. android:layout_width="wrap_content"
  48. android:layout_height="wrap_content"
  49. android:text="9.0 "
  50. android:textColor="#FFCE42"
  51. android:textSize="18sp" />
  52.  
  53. <TextView
  54. android:layout_width="wrap_content"
  55. android:layout_height="wrap_content"
  56. android:text=" | 专业"
  57. android:textColor="#55000000"
  58. android:textSize="14sp" />
  59.  
  60. <TextView
  61. android:id="@+id/tv_professional"
  62. android:layout_width="wrap_content"
  63. android:layout_height="wrap_content"
  64. android:text="6.7"
  65. android:textColor="#FFCE42"
  66. android:textSize="18sp" />
  67. </LinearLayout>
  68.  
  69. <TextView
  70. android:id="@+id/mv_dec"
  71. android:layout_width="wrap_content"
  72. android:layout_height="wrap_content"
  73. android:layout_marginTop="8dp"
  74. android:text="神奇動物城,法師顯超能"
  75. android:textColor="#99000000"
  76. android:textSize="11sp" />
  77.  
  78. <TextView
  79. android:id="@+id/mv_date"
  80. android:layout_width="wrap_content"
  81. android:layout_height="wrap_content"
  82. android:layout_marginTop="10dp"
  83. android:text="今天165家影院放映2088场"
  84. android:textColor="#99000000"
  85. android:textSize="11sp" />
  86. </LinearLayout>
  87.  
  88. </LinearLayout>


第五步:一定不能忘!!!

recycleView不仅要设置适配器还要设置布局管理者,否则图片不显示

 

  1. GridLayoutManager manager = new GridLayoutManager(this, 1);
  2. recyclerView.setLayoutManager(manager);


此时RecyclerView简单的完成效果如下:


 

 

下面开始做 可推动的 悬浮导航栏:

第一步:首先我们来写一个类,它起标记的作用,来放每一个item的对应的悬浮栏的字符串

 

  1. public class NameBean {
  2. String name;
  3.  
  4. public String getName() {
  5. return name;
  6. }
  7.  
  8. public void setName(String name) {
  9. this.name = name;
  10. }
  11. }

 

第二步:自定义一个SectionDecoration 类 继承 RecyclerView的ItemDecoration

 

  1. public class SectionDecoration extends RecyclerView.ItemDecoration {
  2. private static final String TAG = "SectionDecoration";
  3.  
  4. private List<NameBean> dataList;
  5.  
  6. private DecorationCallback callback;
  7. private TextPaint textPaint;
  8. private Paint paint;
  9. private int topGap;
  10. private int alignBottom;
  11. private Paint.FontMetrics fontMetrics;
  12.  
  13.  
  14. public SectionDecoration(List<NameBean> dataList, Context context, DecorationCallback decorationCallback) {
  15. Resources res = context.getResources();
  16. this.dataList = dataList;
  17. this.callback = decorationCallback;
  18. //设置悬浮栏的画笔---paint
  19. paint = new Paint();
  20. paint.setColor(res.getColor(R.color.colorGray));
  21.  
  22. //设置悬浮栏中文本的画笔
  23. textPaint = new TextPaint();
  24. textPaint.setAntiAlias( true);
  25. textPaint.setTextSize(DensityUtil.dip2px(context, 14));
  26. textPaint.setColor(Color.DKGRAY);
  27. textPaint.setTextAlign(Paint.Align.LEFT);
  28. fontMetrics = new Paint.FontMetrics();
  29. //决定悬浮栏的高度等
  30. topGap = res.getDimensionPixelSize(R.dimen.sectioned_top);
  31. //决定文本的显示位置等
  32. alignBottom = res.getDimensionPixelSize(R.dimen.sectioned_alignBottom);
  33. }
  34.  
  35. @Override
  36. public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
  37. super.getItemOffsets(outRect, view, parent, state);
  38. int pos = parent.getChildAdapterPosition(view);
  39. Log.i(TAG, "getItemOffsets:" + pos);
  40. String groupId = callback.getGroupId(pos);
  41. if (groupId.equals("-1")) return;
  42. //只有是同一组的第一个才显示悬浮栏
  43. if (pos == 0 || isFirstInGroup(pos)) {
  44. outRect.top = topGap;
  45. if (dataList.get(pos).getName() == "") {
  46. outRect.top = 0;
  47. }
  48. } else {
  49. outRect.top = 0;
  50. }
  51. }
  52.  
  53. @Override
  54. public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
  55. super.onDraw(c, parent, state);
  56. int left = parent.getPaddingLeft();
  57. int right = parent.getWidth() - parent.getPaddingRight();
  58. int childCount = parent.getChildCount();
  59. for (int i = 0; i < childCount; i++) {
  60. View view = parent.getChildAt(i);
  61. int position = parent.getChildAdapterPosition(view);
  62. String groupId = callback.getGroupId(position);
  63. if (groupId.equals("-1")) return;
  64. String textLine = callback.getGroupFirstLine(position).toUpperCase();
  65. if (textLine == "") {
  66. float top = view.getTop();
  67. float bottom = view.getTop();
  68. c.drawRect(left, top, right, bottom, paint);
  69. return;
  70. } else {
  71. if (position == 0 || isFirstInGroup(position)) {
  72. float top = view.getTop() - topGap;
  73. float bottom = view.getTop();
  74. //绘制悬浮栏
  75. c.drawRect(left, top - topGap, right, bottom, paint);
  76. //绘制文本
  77. c.drawText(textLine, left, bottom, textPaint);
  78. }
  79. }
  80. }
  81. }
  82.  
  83. @Override
  84. public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
  85. super.onDrawOver(c, parent, state);
  86. int itemCount = state.getItemCount();
  87. int childCount = parent.getChildCount();
  88. int left = parent.getPaddingLeft();
  89. int right = parent.getWidth() - parent.getPaddingRight();
  90. float lineHeight = textPaint.getTextSize() + fontMetrics.descent;
  91.  
  92. String preGroupId = "";
  93. String groupId = "-1";
  94. for (int i = 0; i < childCount; i++) {
  95. View view = parent.getChildAt(i);
  96. int position = parent.getChildAdapterPosition(view);
  97.  
  98. preGroupId = groupId;
  99. groupId = callback.getGroupId(position);
  100. if (groupId.equals("-1") || groupId.equals(preGroupId)) continue;
  101.  
  102. String textLine = callback.getGroupFirstLine(position).toUpperCase();
  103. if (TextUtils.isEmpty(textLine)) continue;
  104.  
  105. int viewBottom = view.getBottom();
  106. float textY = Math.max(topGap, view.getTop());
  107. //下一个和当前不一样移动当前
  108. if (position + 1 < itemCount) {
  109. String nextGroupId = callback.getGroupId(position + 1);
  110. //组内最后一个view进入了header
  111. if (nextGroupId != groupId && viewBottom < textY) {
  112. textY = viewBottom;
  113. }
  114. }
  115. //textY - topGap决定了悬浮栏绘制的高度和位置
  116. c.drawRect(left, textY - topGap, right, textY, paint);
  117. //left+2*alignBottom 决定了文本往左偏移的多少(加-->向左移)
  118. //textY-alignBottom 决定了文本往右偏移的多少 (减-->向上移)
  119. c.drawText(textLine, left + 2 * alignBottom, textY - alignBottom, textPaint);
  120. }
  121. }
  122.  
  123.  
  124. /**
  125. * 判断是不是组中的第一个位置
  126. *
  127. * @param pos
  128. * @return
  129. */
  130. private boolean isFirstInGroup(int pos) {
  131. if (pos == 0) {
  132. return true;
  133. } else {
  134. // 因为是根据 字符串内容的相同与否 来判断是不是同意组的,所以此处的标记id 要是String类型
  135. // 如果你只是做联系人列表,悬浮框里显示的只是一个字母,则标记id直接用 int 类型就行了
  136. String prevGroupId = callback.getGroupId(pos - 1);
  137. String groupId = callback.getGroupId(pos);
  138. //判断前一个字符串 与 当前字符串 是否相同
  139. if (prevGroupId.equals(groupId)) {
  140. return false;
  141. } else {
  142. return true;
  143. }
  144. }
  145. }
  146.  
  147. //定义一个借口方便外界的调用
  148. interface DecorationCallback {
  149. String getGroupId(int position);
  150.  
  151. String getGroupFirstLine(int position);
  152. }
  153. }


第三步:在向list集合中先把每一个item的 起“标记”作用的字符串都加进去

 

setPullAction(comingslist);
  1. private void setPullAction(List<WaitMVBean.DataBean.ComingBean> comingslist) {
  2. dataList = new ArrayList<>();
  3.  
  4. for (int i = 0; i < comingslist.size(); i++) {
  5. NameBean nameBean = new NameBean();
  6. String name0 = comingslist.get(i).getComingTitle();
  7. nameBean.setName(name0);
  8. dataList.add(nameBean);
  9. }
  10. }
  11.  
  12.  

 

第四步:在setAdapter(),为RecyclerView添加ItemDecoration:

 

  1. recyclerView.addItemDecoration( new SectionDecoration(dataList,mContext, new SectionDecoration.DecorationCallback() {
  2. //返回标记id (即每一项对应的标志性的字符串)
  3. @Override
  4. public String getGroupId(int position) {
  5. if(dataList.get(position).getName()!=null) {
  6. return dataList.get(position).getName();
  7. }
  8. return "-1";
  9. }
  10.  
  11. //获取同组中的第一个内容
  12. @Override
  13. public String getGroupFirstLine(int position) {
  14. if(dataList.get(position).getName()!=null) {
  15. return dataList.get(position).getName();
  16. }
  17. return "";
  18. }
  19. }));


这样就完成了~

再看一眼最终效果感受一下:


 

 

--------------------- 本文来自 李诗雨 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/cjm2484836553/article/details/53453982?utm_source=copy


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM