高仿小红书笔记详情

效果图:

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容