天天看點

RecyclerView廣告輪播

RecyclerView廣告輪播有兩個難點,首先是無限循環,網上很多修改Adapter的有缺陷,自定義一個LayoutManager無疑是一個好選擇;難點二:每次隻滾動一頁,這個就需要自定義PagerSnapHelper,并且LayoutManager需要implements RecyclerView.SmoothScroller.ScrollVectorProvider

如果你是新手可以看看這篇文章,講解很詳細

https://blog.csdn.net/ww897532167/article/details/86585214

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white">

   <android.support.v7.widget.RecyclerView
       android:id="@+id/advertisingCarousel"
       android:layout_width="match_parent"
       android:layout_height="200dp"/>

   <RadioGroup
       android:id="@+id/radioGroup"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_centerHorizontal="true"
       android:layout_marginTop="150dp"
       android:orientation="horizontal">
   </RadioGroup>
</RelativeLayout>
           

item布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="200dp" />
</LinearLayout>
           

初始化

List<SmartDrinkBean> list = new ArrayList<>();
list.add(new SmartDrinkBean(R.drawable.image_sz));
list.add(new SmartDrinkBean(R.drawable.image_sz));
list.add(new SmartDrinkBean(R.drawable.image_sz));
list.add(new SmartDrinkBean(R.drawable.image_sz));

RadioButtons = new ArrayList<>();
for (int i=0;i< list.size();i++) {
    RadioButton radioButton = new RadioButton(mContext);
    radioGroup.addView(radioButton);
    RadioButtons.add(radioButton);
}

SmartDrinkAdapter smartDrinkAdapter = new SmartDrinkAdapter(R.layout.item_smart_drink,list);

layoutManager = new AdvertisingCarouselLayoutManager(mContext);
advertisingCarousel.setLayoutManager(layoutManager);
advertisingCarousel.setAdapter(smartDrinkAdapter);
//像ViewPager一樣的效果,一次隻能滑一頁,而且居中顯示。
ViewPagerSnapHelper snapHelper = new ViewPagerSnapHelper();
snapHelper.attachToRecyclerView(advertisingCarousel);

int position = layoutManager.getLayoutPosition();
RadioButtons.get(position).setChecked(true);

advertisingCarousel.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
        if (newState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE ){
            int position = layoutManager.getLayoutPosition();
            RadioButtons.get(position).setChecked(true);
        }

    }

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

自定義LayoutManager

public class AdvertisingCarouselLayoutManager extends RecyclerView.LayoutManager implements RecyclerView.SmoothScroller.ScrollVectorProvider {

    private Context context;
    //目前item的位置
    private int layoutPosition = 0;

    public AdvertisingCarouselLayoutManager(Context context){
        this.context = context;
    }

    @Override
    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
        return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
    }

    /**
     * 擷取目前item的Position
     * @return
     */
    public int getLayoutPosition(){
        return layoutPosition;
    }


    public void setLayoutPosition(int layoutPosition){
        this.layoutPosition = layoutPosition;
    }

    /**
     * 繪制初始化item
     * @param recycler
     * @param state
     */
    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        //分離并且回收目前附加的所有View
        detachAndScrapAttachedViews(recycler);

        if (getItemCount() == 0) {
            return;
        }

        //橫向繪制子View,則需要知道 X軸的偏移量
        int offsetX = 0;

        //繪制并添加view
        for (int i = 0; i < getItemCount(); i++) {
            View view = recycler.getViewForPosition(i);
            addView(view);

            measureChildWithMargins(view, 0, 0);
            int viewWidth = getDecoratedMeasuredWidth(view);
            int viewHeight = getDecoratedMeasuredHeight(view);
            layoutDecorated(view, offsetX, 0, offsetX + viewWidth, viewHeight);
            offsetX += viewWidth;
            //超出RecyclerView範圍則不繪制子View
            if (offsetX > getWidth()){
                break;
            }
        }
    }

    /**
     * 是否可橫向滑動
     * @return
     */
    @Override
    public boolean canScrollHorizontally() {
        return true;
    }

    @Override
    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
        recycleViews(dx, recycler);
        fill(dx,recycler);
        /**
         * 橫向移動所有子View
         * 為什麼要 * -1 ? 螢幕xy軸原點在左上角,左移則需要View的坐标 x - offset  右移則需要 x + offset
         * 是以需要 dx * -1
         */
        offsetChildrenHorizontal(dx * -1);

        return dx;
    }

    /**
     * 橫向清單循環
     *
     * @param dx
     * @param recycler
     */
    private void fill(int dx, RecyclerView.Recycler recycler) {
        //左滑
        if (dx > 0) {
            while (true) {
                //得到目前已添加(可見)的最後一個子View
                View lastVisibleView = getChildAt(getChildCount() - 1);
                if (lastVisibleView == null) {
                    break;
                }
                //如果滑動過後,View還是未完全顯示出來就 不進行繪制下一個View
                if (lastVisibleView.getRight() - dx > getWidth()) {
                    break;
                }

                //得到View對應的位置
                layoutPosition = getPosition(lastVisibleView);

                View newView;
                //getItemCount()-1 最後一個item的位置
                if (layoutPosition == getItemCount() - 1) {
                    newView = recycler.getViewForPosition(0);
                } else {
                    newView = recycler.getViewForPosition(layoutPosition + 1);
                }

                //添加一個新view
                addView(newView);

                measureChildWithMargins(newView, 0, 0);
                //擷取新view的大小
                int viewWidth = getDecoratedMeasuredWidth(newView);
                int viewHeight = getDecoratedMeasuredHeight(newView);
                //最後一個可見view的右邊距
                int offsetX = lastVisibleView.getRight();
                layoutDecorated(newView, offsetX, 0, offsetX + viewWidth, viewHeight);
            }

        } else { //右滑
            while (true) {
                View firstVisibleView = getChildAt(0);

                if (firstVisibleView == null) {
                    break;
                }

                if (firstVisibleView.getLeft() - dx < 0) break;

                //得到View對應的位置
                layoutPosition = getPosition(firstVisibleView);

                View newView;
                //如果目前第一個可見View為第0個,則左側顯示最後一個View ,如果不是,下一個就顯示前一個
                if (layoutPosition == 0) {
                    newView = recycler.getViewForPosition(getItemCount() - 1);
                } else {
                    newView = recycler.getViewForPosition(layoutPosition - 1);
                }

                //第一個可見位置添加一個新view
                addView(newView, 0);

                measureChildWithMargins(newView, 0, 0);
                //擷取新view的大小
                int viewWidth = getDecoratedMeasuredWidth(newView);
                int viewHeight = getDecoratedMeasuredHeight(newView);
                //第一個可見view的右邊距
                int offsetX = firstVisibleView.getLeft();
                layoutDecorated(newView, offsetX - viewWidth, 0, offsetX, viewHeight);
            }

        }
    }

    /**
     * 回收view
     * @param dx
     * @param recycler
     */
    private void recycleViews(int dx, RecyclerView.Recycler recycler){
        for (int i = 0;i < getItemCount() ; i++){
            //擷取可見item
            View view = getChildAt(i);
            if (view == null) return;

            //左滑
            if (dx > 0) {
                //移除并回收 原點 左側的子View
                if (view.getRight() - dx < 0) {
                    removeAndRecycleViewAt(i, recycler);
                }
            } else { //右滑
                //移除并回收 右側即RecyclerView寬度之以外的子View
                if (view.getLeft() - dx > getWidth()) {
                    removeAndRecycleViewAt(i, recycler);
                }
            }
        }
    }

    /**
     * 像ViewPager一樣的效果,一次隻能滑一頁,而且居中顯示。
     * @param targetPosition
     * @return
     */
    @Override
    public PointF computeScrollVectorForPosition(int targetPosition) {
        if (getChildCount() == 0) {
            return null;
        }
        int firstChildPos = getPosition(getChildAt(0));
        int direction;
        if (targetPosition < firstChildPos) {
            direction = -1;
        } else {
            direction = 1;
        }
        return new PointF(direction, 0);
    }
}
           

自定義PagerSnapHelper

public class ViewPagerSnapHelper extends PagerSnapHelper{

    @Override
    public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, int velocityY) {
        int position = super.findTargetSnapPosition(layoutManager, velocityX, velocityY);
        if (position >= layoutManager.getItemCount()) {
            return 0;
        }
        return super.findTargetSnapPosition(layoutManager, velocityX, velocityY);
    }
}
           

擴充卡

public class SmartDrinkAdapter extends BaseRecyclerViewAdapter<SmartDrinkBean,BaseViewHolder>{

    public SmartDrinkAdapter(@LayoutRes int resource, @Nullable List<SmartDrinkBean> data) {
        super(resource, data);
    }

    @Override
    public void convert(@NonNull BaseViewHolder holder, SmartDrinkBean item, int position) {
        holder.setImageView(R.id.imageView,item.getImage());
    }

}
           

循環滾動

在AdvertisingCarouselLayoutManager中重寫smoothScrollToPosition方法

@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
    LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext());
    linearSmoothScroller.setTargetPosition(position);
    startSmoothScroll(linearSmoothScroller);
}
           

開啟線程調用以下代碼便可以實作循環滾動

int position = layoutManager.getLayoutPosition();
if (position == smartDrinkAdapter.data.size() - 1){
    position = 0;
}else {
    position = position + 1;
}
advertisingCarousel.smoothScrollToPosition(position);
RadioButtons.get(position).setChecked(true);
layoutManager.setLayoutPosition(position);
           

繼續閱讀