Android RecyclerView加载复杂布局

Android RecyclerView加载复杂布局

 

用一个RecyclerView实现多种复杂布局,复用机制要保存

1.jpg


  • 我们先看看两种内存消耗的情况

     

    gaollg0.GIF

  • 第一种是NestedScrollView ,可以看到瞬间内存就增加,然后不停的滑动,加载越来越多的内容,内存消耗越来越大,没用复用机制的缺点。

gaollg1.GIF

  • 第二种是只用一个RecyclerView的情况,内存就不会继续上涨了,即使后面再增加内容,内存也是相对稳定。

  • 比如像这样的布局要求,如果用一个NestedScrollView里面嵌套多个RecyclerView,就能轻松解决,但是随着越用越久,上滑继续加载,越滑动,消耗的内存越多,而且,一进到这个界面,就开始加载全部的图片,有的用户不需要看到最底下的东西,就导致消耗流量。网速慢的情况下,下面的图片内容也在加载,抢占上面的网络资源,就会下面有的图片,比上面先出现,这样体验不好。如果你试试后台给你1000个item,下一子加载出来,小米6都会吃不消的。

  • 因为NestedScrollView嵌套的缺点,导致我换一种写法,用一个RecyclerView,多种item,来实现这种布局,一是复用了,内存消耗就小了,然后你滑到哪里,就加载到哪,流量消耗也好一些.


  • 先看NestedScrollView实现功能
 
  1. <android.support.v4.widget.NestedScrollView

  2. android:id="@+id/nestedScrollView"

  3. android:layout_width="match_parent"

  4. android:layout_height="match_parent">

  5.  
  6. <LinearLayout

  7. android:layout_width="match_parent"

  8. android:layout_height="match_parent"

  9. android:orientation="vertical">

  10.  
  11. <include layout="@layout/item_head"/>

  12.  
  13. <include layout="@layout/item_message_1"/>

  14.  
  15. <android.support.v7.widget.RecyclerView

  16. android:id="@+id/recyclerView_image"

  17. android:layout_width="match_parent"

  18. android:layout_height="wrap_content"/>

  19.  
  20. <android.support.v7.widget.RecyclerView

  21. android:id="@+id/recyclerView_product"

  22. android:layout_width="match_parent"

  23. android:layout_height="wrap_content"/>

  24.  
  25. <View

  26. android:layout_width="match_parent"

  27. android:layout_height="20dp"

  28. android:background="@color/line_color"/>

  29.  
  30. <android.support.v7.widget.RecyclerView

  31. android:id="@+id/recyclerView_other"

  32. android:layout_width="match_parent"

  33. android:layout_height="wrap_content"/>

  34.  
  35.  
  36. </LinearLayout>

  37.  
  38. </android.support.v4.widget.NestedScrollView>

  1. 这是NestedScrollView是布局xml,我用的是25.3.1版本,就不会有无法计算里面RecyclerView的高度问题。
 
  1. //设置滑动惯性

  2. recyclerViewProduct.setNestedScrollingEnabled(false);

  3.  
  1. 要记得RecyclerView要设置恢复滑动惯性,不然上下滑动没有惯性了。
 
  1. //GridLayoutManager,垂直方向

  2. gridLayoutManagerProduct = new GridLayoutManager(this, 3, GridLayoutManager.VERTICAL, false);

  3. //设置头部占3个,其他占1个

  4. gridLayoutManagerProduct.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup()

  5. {

  6. @Override

  7. public int getSpanSize(int position)

  8. {

  9. if (productAdapter.getItemViewType(position) == ProductAdapter.ITEM_HEAD)

  10. {

  11. return 3;

  12. } else

  13. {

  14. return 1;

  15. }

  16. }

  17. });

  18.  
  1. 使用网格布局的话,就用setSpanSizeLookup这个 方法,这个不是说每行3个item,你也能写6,然后分组头占6份,他就占满整行宽度了,item写2份,这样的话,就能每行3个item。这个自己去试一试就懂了,就是return 多少,占几份。
 
  1. //滑动监听

  2. nestedScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener()

  3. {

  4. @Override

  5. public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY)

  6. {

  7. View view = v.getChildAt(0);

  8. if (view.getHeight() == scrollY + v.getHeight())

  9. {

  10. //加载更多、增加数据

  11. otherAdapter.addData(DataOther.getOtherList());

  12. }

  13. }

  14. });

  15.  

4.NestedScrollView的滑动监听,判断滑动到底部的方法。


  • 接下来说重点,就是使用一个RecyclerView来实现复制的布局,主要讲Adapter。
 
  1. //头部

  2. public static final int ITEM_HEAD = 0;

  3. //信息

  4. public static final int ITEM_MESSAGE = 1;

  5. //图片

  6. public static final int ITEM_IMAGE = 2;

  7. //还有别类型

  8.  
  1. 首先把这个布局进行拆分,我将它分为 6 种,像“头部”部分,还有“信息”部分,只会出现一次的,其他的就会多出出现,
    其实之前,我是将“头部”和“信息”放一起,但是这个item太大了,超出2个屏幕高度,导致显示不全,原因估计是item的高度计算问题,如果不解决问题,可以把item拆成不大于屏幕高度的大小。
 
  1. @Override

  2. public int getItemViewType(int position)

  3. {

  4. if (position == 0)

  5. {

  6. //确定第一个是头部

  7. return ITEM_HEAD;

  8. } else if (position == 1)

  9. {

  10. //确定第二个是信息

  11. return ITEM_MESSAGE;

  12. } else if (datas.get(position) instanceof BeanImage)

  13. {

  14. return ITEM_IMAGE;

  15. } else if (datas.get(position) instanceof BeanProduct.BeanData)

  16. {

  17. return ITEM_PRODUCT;

  18. } else if (datas.get(position) instanceof BeanOther)

  19. {

  20. return ITEM_OTHER;

  21. } else

  22. {

  23. //确定String用户商品头部

  24. return ITEM_PRODUCT_HEAD;

  25. }

  26. }

  27.  
  1. 在adapter里面,要重写 getItemViewType 这个方法,这个方法呢,就是根据 item 的下标 position 返回对应的 ViewHolder,
    demo可以看到,“头部”和“信息”部分,我能确定他是出现一次,而且位置是固定的,所以,position 为 0 和 1 的时候,我就放回对应的值,其他ViewHolder 我根据对应的数据类型来放回。
 
  1. @Override

  2. public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)

  3. {

  4. if (viewType == ITEM_HEAD)

  5. {

  6. return new HeadViewHolder(LayoutInflater.from(parent.getContext())

  7. .inflate(R.layout.item_head, parent, false));

  8. }

  9. .

  10. .

  11. //还有别的类型,省略

  12. }

  13.  
  1. adapter必须重新的 onCreateViewHolder 这个 方法,给我们一个参数 int viewType,这个参数就是上面 getItemViewType 这个方法判断类型后返回的值,我们根据这个值,来 create 对应的 ViewHolder。
 
  1. @Override

  2. public void onAttachedToRecyclerView(RecyclerView recyclerView)

  3. {

  4. super.onAttachedToRecyclerView(recyclerView);

  5. RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();

  6. if (layoutManager instanceof GridLayoutManager)

  7. {

  8. GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;

  9. gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup()

  10. {

  11. @Override

  12. public int getSpanSize(int position)

  13. {

  14. //取多个item的每行占用个数的最小公倍数,

  15. //比如item1一行2个,item2一行3个,item3一行1个,就取6

  16. //他们对应的return 3,return 2,return 6

  17. if (ITEM_PRODUCT == getItemViewType(position))

  18. {

  19. return 2;

  20. } else if (ITEM_OTHER == getItemViewType(position))

  21. {

  22. return 3;

  23. } else

  24. {

  25. return 6;

  26. }

  27. }

  28. });

  29. }

  30. }

  31.  
  1. adapter再写一个方法 onAttachedToRecyclerView,这个方法就能获取到RecyclerView ,然后再获取到 对应的LayoutManager,复制的布局,一般都是用网格(GridLayoutManager),或者瀑布流( StaggeredGridLayoutManager)
    这个看自己的需求了,获取到之后,再根据position对应的ViewHolder,设置他们的占位值(我理解的)。
 
  1. //滑动监听

  2. recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener()

  3. {

  4. @Override

  5. public void onScrollStateChanged(RecyclerView recyclerView, int newState)

  6. {

  7. super.onScrollStateChanged(recyclerView, newState);

  8. //判断是否到底部,offset是滑动距离,Extent是控件高度,range是总的内容高度

  9. if (recyclerView.getChildCount() > 0

  10. && recyclerView.computeVerticalScrollOffset() + recyclerView.computeVerticalScrollExtent()

  11. >= recyclerView.computeVerticalScrollRange())

  12. {

  13. detailAdapter.addData(DataOther.getOtherList());

  14. }

  15. }

  16. });

  17.  
  1. 最后来个RecyclerView的滑动到底部,可靠的方法。

这个demo用原始的方法,继承 RecyclerView.Adapter ,将方法一个个重写,好处就是让你理解RecyclerView,可以实现复杂的布局复用,缺点就是写起来,有点费时间,不过学习嘛,搞懂了再去用开源框架。
介绍一些可能用的到的
BaseRecyclerViewAdapterHelper:https://github.com/CymChad/BaseRecyclerViewAdapterHelper/wiki/%E9%A6%96%E9%A1%B5

SwipeLayout 滑动(删除)菜单:https://github.com/daimajia/AndroidSwipeLayout
http://blog.csdn.net/yanzhenjie1003/article/details/52115566

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值