实现类似猫眼影片详情页电影海报滚动效果(RcyclerView+LinearSnapHelper )

Github

(前一篇ViewPager实现和本篇的代码,都在这个项目)

效果

第一行为ViewPager实现效果
第二行为RcyclerView+LinearSnapHelper 实现效果

重点

LinearSnapHelper是什么

LinearSnapHelper是一个让
RcyclerView在滑动scroll、快速滑动fling过程中,使得最后停止在一个Item的中间位置,而不是随意的一个位置

使用LinearSnapHelper后,如何让RcyclerView的第0个Item和最后一个Item居中显示

很显然,不做特殊处理,第0个和最后一个Item,无法居中;

我们只需要在第0个Item之前加上一个leftMargin,最后一个Item加上一个rightMargin即可

这个margin=RcyclerView中心点X坐标 减去 一个Item的宽度/2

 final ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) holder.itemRoot.getLayoutParams();
        // 为了居中, 第一个条目leftMagrin、最后一个条目的rightMargin是(recyclerView宽度减去一个条目的宽度)/2
        int margin = (mRecyclerViewWidth - p.width) / 2;
        if (position == 0) {
            p.leftMargin = margin;
            p.rightMargin = 0;
            holder.itemRoot.setLayoutParams(p);
        } else if (position == imageIdArray.length - 1) {
            p.leftMargin = 0;
            p.rightMargin = margin;
            holder.itemRoot.setLayoutParams(p);
        } else {
            p.leftMargin = 0;
            p.rightMargin = 0;
            holder.itemRoot.setLayoutParams(p);

        }

渐变动画的是实现(难点+数学)

  1. 遍历RcyclerView的每一个子view

    (注意RcyclerView的子view等于当前屏幕内所有子view的总和,而不是RcyclerView内部item的总数量,缓存你懂得)

  2. 得到所有子view的x坐标

  3. 换算出每个子view的中心点x坐标,x+itemWidth/2
  4. 获得RcyclerView的中心点x坐标
  5. 计算出每个Item中心点x坐标与RcyclerView的中心点x坐标之差offX
  6. 根据第offX,计算出每个Item所应该缩放的比例interpretateScale
  7. 让每一个Item去缩放吧
mRecyclerview.addOnScrollListener(new RecyclerView.OnScrollListener() {


            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);


                int childCount = mRecyclerview.getChildCount();
                Log.e("ccc", childCount + "");

                int[] location = new int[2];
                for (int i = 0; i < childCount; i++) {
                    View v = mRecyclerview.getChildAt(i);
                    v.getLocationOnScreen(location);


                    int recyclerViewCenterX = mRecyclerview.getLeft() + mRecyclerview.getWidth() / 2;
                    int itemCenterX = location[0] + v.getWidth() / 2;

// ★ 两边的图片缩放比例
                    float scale = 0.8f;
// ★某个item中心X坐标距recyclerview中心X坐标的偏移量
                    int offX =  Math.abs(itemCenterX - recyclerViewCenterX);
// ★ 在一个item的宽度范围内,item从1缩放至scale,那么改变了(1-scale),
// 从下列公式算出随着offX变化,item的变化缩放百分比
                    float percent =offX * (1 - scale) / v.getWidth();
// ★ 取反哟
                    float interpretateScale = 1 - percent;
// 这个if不走的话,得到的是多级渐变模式
                    if (interpretateScale < scale) {
                        interpretateScale = scale;
                    }
                    v.setScaleX((interpretateScale));
                    v.setScaleY((interpretateScale));
                }
            }
        });

如何点击一个Item,让这个Item居中

  1. 找到这个Item的中心点x坐标
  2. 计算与RcyclerView中心点x坐标的差值offX
  3. mRecyclerView.smoothScrollBy(offX, 0);
int[] location = new int[2];   
v.getLocationOnScreen(location);
 int currentX = location[0];
 int currentCenterX = (int) (currentX + p.width / 2 * 0.8f);//因为除了中间外的其他条目是被缩放为0.8的状态
 int recyclerViewCenterX = mRecyclerViewWidth / 2;
 int offX = currentCenterX - recyclerViewCenterX;

 if (Math.abs(offX) >p.width / 2 * 0.21f) {//因为已经居中的Item,已经被放大到比例1了
     mRecyclerView.smoothScrollBy(offX, 0);
 }

粘贴代码,详细注释

布局


    .support.v7.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="170dp"
        android:layout_gravity="center"
        android:layout_marginTop="30dp"
        android:background="#f00987"
        android:clipChildren="false"/>

Activity

 mRecyclerview = findViewById(R.id.recyclerview);


        final LinearLayoutManager manager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);

        mRecyclerview.setLayoutManager(manager);


        int mRecyclerviewWidth;
        ViewGroup.LayoutParams layoutParams = mRecyclerview.getLayoutParams();
        if (layoutParams.width == -1) {
            mRecyclerviewWidth = DisplayUtils.getScreenWidth(this);//我这里是全屏幕宽度,根据实际情况定
        } else {
            mRecyclerviewWidth = layoutParams.width;
        }
        mRecAdapter = new RecAdapter(this, mRecyclerviewWidth,mRecyclerview);
        mRecyclerview.setAdapter(mRecAdapter);
        final LinearSnapHelper helper = new LinearSnapHelper();
        helper.attachToRecyclerView(mRecyclerview);
        mRecyclerview.addOnScrollListener(new RecyclerView.OnScrollListener() {


            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);


                int childCount = mRecyclerview.getChildCount();
                Log.e("ccc", childCount + "");

                int[] location = new int[2];
                for (int i = 0; i < childCount; i++) {
                    View v = mRecyclerview.getChildAt(i);
                    v.getLocationOnScreen(location);


                    int recyclerViewCenterX = mRecyclerview.getLeft() + mRecyclerview.getWidth() / 2;
                    int itemCenterX = location[0] + v.getWidth() / 2;

// ★ 两边的图片缩放比例
                    float scale = 0.8f;
// ★某个item中心X坐标距recyclerview中心X坐标的偏移量
                    int offX =  Math.abs(itemCenterX - recyclerViewCenterX);
// ★ 在一个item的宽度范围内,item从1缩放至scale,那么改变了(1-scale),从下列公式算出随着offX变化,item的变化缩放百分比

                    float percent =offX * (1 - scale) / v.getWidth();
// ★ 取反哟
                    float interpretateScale = 1 - percent;


// 这个if不走的话,得到的是多级渐变模式
                    if (interpretateScale < scale) {
                        interpretateScale = scale;
                    }
                    v.setScaleX((interpretateScale));
                    v.setScaleY((interpretateScale));

// Log.e("qwe", recyclerViewCenterX + "///" + itemCenterX + "///" + interpretateScale + "///" + percent + "///" + i);
// Log.e("qwe", "-----");

                }
// Log.e("qwe", "====================");

            }

        });

adapter

package com.custom.view.gallerydemo;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;

/** * Created by apple on 2018/1/2. */

class RecAdapter extends RecyclerView.Adapter.VH> {
    private int mRecyclerViewWidth;
    private Context mContext;
    private RecyclerView mRecyclerView;

    public RecAdapter(Context context, int recyclerViewWidth, RecyclerView recyclerview) {
        mContext = context;
        mRecyclerViewWidth = recyclerViewWidth;
        mRecyclerView = recyclerview;

    }

    // 准备要显示的图片资源
    private int[] imageIdArray = {R.drawable.iv1, R.drawable.iv2,
            R.drawable.iv3, R.drawable.iv4, R.drawable.iv1, R.drawable.iv2,
            R.drawable.iv3, R.drawable.iv4, R.drawable.iv1, R.drawable.iv2,
            R.drawable.iv3, R.drawable.iv4, R.drawable.iv1, R.drawable.iv2,
            R.drawable.iv3, R.drawable.iv4, R.drawable.iv1, R.drawable.iv2,
            R.drawable.iv3, R.drawable.iv4, R.drawable.iv1, R.drawable.iv2,
            R.drawable.iv3, R.drawable.iv4, R.drawable.iv1, R.drawable.iv2,
            R.drawable.iv3, R.drawable.iv4, R.drawable.iv1, R.drawable.iv2,
            R.drawable.iv3, R.drawable.iv4, R.drawable.iv1, R.drawable.iv2,
            R.drawable.iv3, R.drawable.iv4, R.drawable.iv1, R.drawable.iv2,
            R.drawable.iv3, R.drawable.iv4, R.drawable.iv1, R.drawable.iv2,
            R.drawable.iv3, R.drawable.iv4, R.drawable.iv1, R.drawable.iv2,
            R.drawable.iv3, R.drawable.iv4};


    @Override
    public VH onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater from = LayoutInflater.from(mContext);
        View view = from.inflate(R.layout.viewpager_item, parent, false);
        return new VH(view);
    }

    @Override
    public void onBindViewHolder(final VH holder, final int position) {
        holder.iv.setImageResource(imageIdArray[position]);
        final ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) holder.itemRoot.getLayoutParams();
        // 为了居中, 第一个条目leftMagrin、最后一个条目的rightMargin是(recyclerView宽度减去一个条目的宽度)/2
        int margin = (mRecyclerViewWidth - p.width) / 2;
        if (position == 0) {
            p.leftMargin = margin;
            p.rightMargin = 0;
            holder.itemRoot.setLayoutParams(p);
        } else if (position == imageIdArray.length - 1) {
            p.leftMargin = 0;
            p.rightMargin = margin;
            holder.itemRoot.setLayoutParams(p);
        } else {
            p.leftMargin = 0;
            p.rightMargin = 0;
            holder.itemRoot.setLayoutParams(p);

        }


        holder.itemRoot.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                int[] location = new int[2];
                v.getLocationOnScreen(location);
                int currentX = location[0];
                int currentCenterX = (int) (currentX + p.width / 2 * 0.8f);//因为除了中间外的其他条目是被缩放为0.8的状态
                int recyclerViewCenterX = mRecyclerViewWidth / 2;
                int offX = currentCenterX - recyclerViewCenterX;

                if (Math.abs(offX) >p.width / 2 * 0.21f) {//因为已经居中的Item,已经被放大到比例1了
                    mRecyclerView.smoothScrollBy(offX, 0);
                }
            }
        });
    }

    @Override
    public int getItemCount() {
        return imageIdArray.length;
    }


    class VH extends RecyclerView.ViewHolder {

        private RelativeLayout itemRoot;
        private ImageView iv;

        public VH(View itemView) {
            super(itemView);
            itemRoot = itemView.findViewById(R.id.item_root);

            iv = itemView.findViewById(R.id.iv);
        }
    }

}

写博客要是封装了,会影响阅读性,不便于理解思路,但是为了方便使用,大家可以封装一下

你可能感兴趣的:(实现类似猫眼影片详情页电影海报滚动效果(RcyclerView+LinearSnapHelper ))