1. 程式人生 > >實現類似貓眼影片詳情頁電影海報滾動效果(RcyclerView+LinearSnapHelper )

實現類似貓眼影片詳情頁電影海報滾動效果(RcyclerView+LinearSnapHelper )

(前一篇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);
 }

貼上程式碼,詳細註釋

佈局


    <android.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<RecAdapter.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);
        }
    }

}

寫部落格要是封裝了,會影響閱讀性,不便於理解思路,但是為了方便使用,大家可以封裝一下