高仿小红书笔记详情

作者: ___刘辉良 | 来源:发表于2016-10-23 22:52 被阅读3854次

    效果图:

    redbook.gif

    git链接:https://github.com/BelongsH/RedBook

    大概介绍一下项目里面的一些知识点:

    • 动态更改ViewPager的高度
    • RecyclerView瀑布流的处理
    • RecyclerView多type的处理
    如何动态的去修改ViewPager的高度?

    要动态的修改ViewPager的高度,首先需要知道ViewPager中每一个Item的高度。那么怎么确定高度呢?在开发中,一般服务端会把图片的宽度和高度通过api传给我们, 如果没有的话,我们也可以通过图片加载框架获取到图片本身的宽和高。

    Paste_Image.png

    假设这个时候,服务端传给我们这样的数据,我们就可以知道图片的宽和高。因为宽度是充满全屏不变的。然后我们拿图片的原始宽度/屏幕的宽度=缩放比率。通过这个缩放比率,就可以知道图片的高度。然后把它封装到一个数据结构中,方便使用。

        public class PictureSize {
            private int originalHeight;
            private int originalWidth;
            private float scale;
            private int scaleHeight;
            private int scaleWidth;
    
    
            public static PictureSize caculatePictureSize(int originalHeight, int originalWidth, int screenWidth) {
                PictureSize pictureSize = new PictureSize();
                pictureSize.originalHeight = originalHeight;
                pictureSize.originalWidth = originalWidth;
                pictureSize.scale = (screenWidth + 0f) / originalWidth;
                pictureSize.scaleHeight = (int) (originalHeight * pictureSize.scale);
                pictureSize.scaleWidth = screenWidth;
                return pictureSize;
            }  
        } 
    

    这样的话,我们可以计算出ViewPage中的每一个item的高度。那么现在需要处理的就是滑动的距离和图片高度缩放的问题。首先我们需要获取到ViewPage的滑动比率(是指滑动距离相对整个屏幕的百分比)。通过setPageTransformer的方法就可以获取到一个position,这个position是相对View的一个的滑动比率。然后用这个滑动比率*item之间的高度差,就可以计算出滑动的高度值。

        holder1.vpHeadDelegate.setPageTransformer(true, new ViewPager.PageTransformer() {
                    @Override
                    public void transformPage(View page, float position) {
                        if (position > 0 && position <= 1) {
                            int badgePosition = (int) (page.getX() / screenWidth) - 1;
                            PictureSize offsetModel = mPictureSizes.get(String.valueOf(badgePosition + 1));
                            if (offsetModel == null) return;
                            PictureSize nowModel = mPictureSizes.get(String.valueOf(badgePosition));
                            float disHeight = (offsetModel.getScaleHeight() - nowModel.getScaleHeight()) * (1 - position);
                            LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) holder1.vpHeadDelegate.getLayoutParams();
                            params.width = nowModel.getScaleWidth();
                            params.height = (int) (nowModel.getScaleHeight() + disHeight);
                            holder1.vpHeadDelegate.setLayoutParams(params);
                            holder1.vpHeadDelegate.requestLayout();
                        }
                    }
                });
    

    这样的话,我们就完成了,动态更改ViewPager的高度。

    RecyclerView瀑布流的处理

    使用RecyclerView来处理瀑布流的话,是非常easy的事情.通过

         RecyclerView.LayoutManager layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
                rvMain.setLayoutManager(layoutManager);
    

    然后动态的去计算每一个item的高度

         ViewHolder viewHolder = (ViewHolder) holder;
                NoteModel itemModel = (NoteModel) items.get(position);
                PictureModel model = itemModel.pictures.get(0);
                LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) viewHolder.ivContentImage.getLayoutParams();
                float halfWidth = (ScreenUtils.getScreenWidth(holder.itemView.getContext()) - 60) / 2;
                layoutParams.width = (int) halfWidth;
                PictureSize pictureSize = PictureSize.caculatePictureSize(model.height, model.width, (int) halfWidth);
                layoutParams.height = pictureSize.getScaleHeight();
                viewHolder.ivContentImage.setLayoutParams(layoutParams);
                viewHolder.tvNoteDes.setText(itemModel.desc);
                viewHolder.tvNoteTitle.setText(itemModel.title);
                viewHolder.tvUserName.setText(itemModel.user.nickname);
                viewHolder.tvNoteTitle.setText(itemModel.title);
                StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) viewHolder.itemView.getLayoutParams();
                params.setMargins(15, 15, 15, 15);
                holder.itemView.setLayoutParams(params);
                Glide.with(holder.itemView.getContext()).load(model.url).centerCrop().diskCacheStrategy(DiskCacheStrategy.ALL).into(viewHolder.ivContentImage);
    

    需要注意的是,在StaggeredGridLayoutManager需要让item全屏的话,可以这样:

      HeadViewHolder headViewHolder = new HeadViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_note_head, parent, false));
            StaggeredGridLayoutManager.LayoutParams layoutParams = new StaggeredGridLayoutManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            layoutParams.setFullSpan(true);
            headViewHolder.itemView.setLayoutParams(layoutParams);
            screenWidth = (ScreenUtils.getScreenWidth(parent.getContext()) + 0f);
    
    RecyclerView多type的处理

    在RecyclerView.Adapter 中处理多type的话,会让代码变得臃肿,而且不易协作开发。比如

    public class NoteAdapter extends RecyclerView.Adapter {
    
      final int VIEW_TYPE_ACCESS_0 = 0;
      final int VIEW_TYPE_ACCESS_1 = 1;
      final int VIEW_TYPE_ACCESS_2 = 2;
      final int VIEW_TYPE_ACCESS_3 = 3;
    
      List<Accessory> items;
    
      @Override public int getItemViewType(int position) {
         Accessory accessory = items.get(postion);
         if (position==0){
           return VIEW_TYPE_ACCESS_0;
         } else if(position==1) {
           return VIEW_TYPE_ACCESS_1;
         }else if(position==2) {
           return VIEW_TYPE_ACCESS_2;
         }else  {
           return VIEW_TYPE_ACCESS_3;
         }
      }
    
      @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (VIEW_TYPE_ACCESS_0 == viewType){
          return new ViewHolder1(inflater.inflate(R.layout.item_1, parent));
        } else if(VIEW_TYPE_ACCESS_1==viewType) {
          return new ViewHolder2 (inflater.inflate(R.layout.ietm_2,parent)):
        }else if(VIEW_TYPE_ACCESS_2==viewType) {
          return new ViewHolder2 (inflater.inflate(R.layout.ietm_3,parent)):
        }else if(VIEW_TYPE_ACCESS_3==viewType) {
          return new ViewHolder2 (inflater.inflate(R.layout.ietm_4,parent)):
        }
      }
    }
    

    这样的话,所有的代码都在一个类中,代码会变得越来越臃肿。不易维护。而且协作开发问题也是非常的大,(多人操作同一文件)我们可以通过给Adapter设置一个代理,由它来完成所需要的操作。下面是对这个代理的抽象

       public abstract class AdapterDelegate<T> {
          protected abstract boolean isForViewType(@NonNull T items, int position);
          @NonNull abstract protected RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent);
          protected abstract void onBindViewHolder(@NonNull T items, int position,
              @NonNull RecyclerView.ViewHolder holder, @NonNull List<Object> payloads);
          protected void onViewRecycled(@NonNull RecyclerView.ViewHolder viewHolder) {}
          protected boolean onFailedToRecycleView(@NonNull RecyclerView.ViewHolder holder) {
            return false;
          }
          protected void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder) {}
          protected void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {}
        }
    

    然后我们将自己的核心代理继承并实现这个基类,在isForViewType中来判断当前的position是不是我们需要处理的Item,然后在onCreateViewHolder>onBindViewHolder。

        public class ContentDelegate extends AdapterDelegate<List<HomeModel>> {
    
    
            @Override
            protected boolean isForViewType(@NonNull List<HomeModel> items, int position) {
                return items.get(position) instanceof NoteModel;
            }
    
            @NonNull
            @Override
            protected RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {
                View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_note_notes, parent, false);
                return new ViewHolder(view);
            }
    
            @Override
            protected void onBindViewHolder(@NonNull List<HomeModel> items, int position, @NonNull RecyclerView.ViewHolder holder, @NonNull List<Object> payloads) {
                ViewHolder viewHolder = (ViewHolder) holder;
                NoteModel itemModel = (NoteModel) items.get(position);
                PictureModel model = itemModel.pictures.get(0);
                LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) viewHolder.ivContentImage.getLayoutParams();
                float halfWidth = (ScreenUtils.getScreenWidth(holder.itemView.getContext()) - 60) / 2;
                layoutParams.width = (int) halfWidth;
                PictureSize pictureSize = PictureSize.caculatePictureSize(model.height, model.width, (int) halfWidth);
                layoutParams.height = pictureSize.getScaleHeight();
                viewHolder.ivContentImage.setLayoutParams(layoutParams);
                viewHolder.tvNoteDes.setText(itemModel.desc);
                viewHolder.tvNoteTitle.setText(itemModel.title);
                viewHolder.tvUserName.setText(itemModel.user.nickname);
                viewHolder.tvNoteTitle.setText(itemModel.title);
                StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) viewHolder.itemView.getLayoutParams();
                params.setMargins(15, 15, 15, 15);
                holder.itemView.setLayoutParams(params);
                Glide.with(holder.itemView.getContext()).load(model.url).centerCrop().diskCacheStrategy(DiskCacheStrategy.ALL).into(viewHolder.ivContentImage);
                Glide.with(viewHolder.ivContentImage.getContext()).load(itemModel.user.images).override(pictureSize.getScaleWidth(), pictureSize.getScaleHeight()).diskCacheStrategy(DiskCacheStrategy.ALL).bitmapTransform(new CropCircleTransformation(viewHolder.ivUserHead.getContext())).placeholder(R.drawable.ic_default_head).into(viewHolder.ivUserHead);
            }
        }
    

    而这些核心代理会在AdapterDelegatesManager中被添加到AdapterDelegatesManager中,进行判断当前的逻辑,所以你需要做的就是继承AbsDelegationAdapter,然后,将自己的多type添加进去即可。

        public class HomeAdapter extends AbsDelegationAdapter<List<HomeModel>> {
            private List<HomeModel> mDatas;
            public HomeAdapter(List<HomeModel> datas) {
                super();
                setItems(datas);
                this.mDatas = datas;
                this.delegatesManager.addDelegate(new HeadDelegate());
                this.delegatesManager.addDelegate(new ContentDelegate());
                this.delegatesManager.addDelegate(new CommentDelegate());
            }
    
            @Override
            public int getItemCount() {
                return mDatas.size();
            }
        }
    

    关于多type的更多介绍:
    http://hannesdorfmann.com/android/adapter-delegates
    https://github.com/BelongsH/RedBook

    相关文章

      网友评论

      本文标题:高仿小红书笔记详情

      本文链接:https://www.haomeiwen.com/subject/yidcuttx.html