有关RecyclerView瀑布流的问题处理

一、随机高度问题

一般使用RecyclerView+StaggeredGridLayoutManager构建瀑布流时,如果事先知道图片的长宽比,则直接设置就好,如果不知道的一般也采用随机高度,但是会导致图片不完整问题。可以尝试使用Glide加载图片时,通过Target对象获取图片长宽比。

  • 每个Item的布局文件高度采用wrap_content
android:layout_height="wrap_content"
  • 每个 Item宽度固定,高度保存在Map中imageHeightMap,方便复用
  • 重写getItemViewType(int position)因为每个Item高度都不同,导致每个ViewHolder都不一样,为每个ViewHolder制定唯一的viewType值。(用途:处理Item乱跳的问题)
  • 使用Glide加载
if (!this.imageHeightMap.containsKey(position)){
//当首次加载图片时,调用 loadImageFirst(),保存图片高度
            loadImageFirst(imageVH.imageView,position);
        }else{  
//非首次加载,直接根据保存的长宽,获取图片          
            Glide.with(this.mContext)
                    .load(this.entities.get(position).getUrl())                    .override(this.imageWidth,this.imageHeightMap.get(position))
                    .into(imageVH.imageView);
        }

loadImageFirst()方法代码:

    public void loadImageFirst(View view, final int position){
        //构造方法中参数view,就是回调方法中的this.view
        ViewTarget target = new ViewTarget(view) {
            @Override
            public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) {
                //加载图片成功后调用
                float scaleType = ((float) resource.getHeight())/resource.getWidth();
                int imageHeight = (int) (imageWidth*scaleType);
                //获取图片高度,保存在Map中
                imageHeightMap.put(position,imageHeight);
                //设置图片布局的长宽,Glide会根据布局的自动加载适应大小的图片
                ViewGroup.LayoutParams lp = this.view.getLayoutParams();
                lp.width=imageWidth;
                lp.height=imageHeight;
                this.view.setLayoutParams(lp);
                //resource就是加载成功后的图片资源
                ((ImageView)view).setImageBitmap(resource);
            }
            @Override
            public void onLoadFailed(Exception e, Drawable errorDrawable) {
                //加载图片失败后调用
                super.onLoadFailed(e, errorDrawable);
                int imageHeight = imageWidth;
                imageHeightMap.put(position,imageHeight);
                ViewGroup.LayoutParams lp = this.view.getLayoutParams();
                lp.width=imageWidth;
                lp.height=imageHeight;
                this.view.setLayoutParams(lp); 
               ((ImageView)view).setImageResource(R.mipmap.ic_launcher);
            }
        };
        Glide.with(this.mContext)
                .load(this.entities.get(position).getUrl())
                .asBitmap()                 //作为Bitmap加载,对应onResourceReady回调中第一个参数的类型
                .into(target);
    }

二、快速上拉时Item乱跳的问题

解决方法:在RecyclerView.adapter中重写getItemViewType(int position)方法
原理分析
查看源码,针对该方法的注释为:

Return the view type of the item at position for the purposes of view recycling.The default implementation of this method returns 0, making the assumption of a single view type for the adapter. Unlike ListView adapters, types need not be contiguous. Consider using id resources to uniquely identify item view types.

大概的意思是view type作为View复用时的标记,可以是不连续的整数,默认返回为0。
RecyclerView.adapter复用ViewHolder构建列表视图的过程来看:

  1. 针对不同的position调用getViewForPosition(int position, boolean dryRun)。依次从 mChangedScrapmAttachedScrapmCachedViews中获取。都得不到的话,则从RecyclerViewPool中通过viewType来取,如果有多个ViewHolderviewType一样,则取最后一个。仍然取不到的话,则通过final方法createViewHolder创建
  • createViewHolder方法调用我们重写的onCreateViewHolder方法。如果没有重写getItemViewType()方法,则创建过程中的viewType值为0。即RecyclerViewPool中有多个以0为标志的ViewHolder
  • 所以当上拉时,之前的ViewHolder重新从RecyclerViewPool获取,而不是调用onCreateViewHolder方法创建。因为之前多次创建了viewType为0的ViewHolder,所以获取的并不是当前我们需要的,而是最后一次由onCreateViewHolder方法创建的。从而导致该position位置的图片高度不合适,出现一定的gaps(空隙)。而在StaggeredGridLayoutManager有专门的一个线程,每当滚动停止时对出现的gaps进行重新布局,导致Item乱跳

你可能感兴趣的:(有关RecyclerView瀑布流的问题处理)