RecyclerView滑動窗口顯示大尺寸廣告位

翻看騰訊新聞App時,發現有個廣告UI交互挺好玩的。先看圖


gif圖片可能有點卡,從視覺上看,有點像底部放了張海報,上層列表有個窗口透視過去,隨着滾動窗口看到底部海報不同部分。
擼起袖子開幹。
一般自定義View相關的,第一步就是做拆解。首先,如果從排版看,最外層View不是RecyclerView就是ListView

方案一

如圖:底部是一個ImageView,上層是一個列表RecyclerView,設置列表背景爲透明的,根據item type加載不同ViewHolder,當需要展示底部廣告時,其ViewHolder itemView背景爲透明的,即可滿足UI的效果。

不足:佈局不夠簡化,數據填充不靈活,如果屏幕內出現多個相關類型的窗口時,不滿足加載不同的海報。方案二

方案二

如果廣告窗口是ImageView,按照大圖的局部加載顯示思想,當窗口ViewHolder向上滑,圖片顯示下部分區域,反之,窗口ViewHolder往下滑時,圖片顯示上部分。

代碼實現: 

    public class ScrollWindowImageView extends AppCompatImageView {
         private boolean initialized;
         private Matrix matrix;
         private boolean isScrollDown = true;
         private float translate;

         public ScrollWindowImageView(Context context) {
              this(context, null);
         }

         public ScrollWindowImageView(Context context, AttributeSet attrs) {
              this(context, attrs, 0);
         }

         public ScrollWindowImageView(Context context, AttributeSet attrs, int defStyleAttr) {
             super(context, attrs, defStyleAttr);
             setScaleType(ScaleType.MATRIX);
         }

         @Override
         protected void onSizeChanged(int w, int h, int oldw, int oldh) {
             super.onSizeChanged(w, h, oldw, oldh);
             initialized = true;
             initImage();
         }

         private void initImage() {
             final Drawable drawable = getDrawable();
             if (!initialized || drawable == null) {
                 return;
             }
             final int viewWidth = getWidth();
             final int viewHeight = getHeight();
             final int drawableWidth = drawable.getIntrinsicWidth();
             final int drawableHeight = drawable.getIntrinsicHeight();
             float scaleX = (float) viewWidth / drawableWidth;
             matrix = getImageMatrix();
             matrix.setScale(scaleX, scaleX);
             matrix.postTranslate(0, -(drawableHeight * scaleX - viewHeight));
             setImageMatrix(matrix);
         }

         public void scrollWindow(float scrollY) {
             final Drawable drawable = getDrawable();
             if (!initialized || drawable == null) {
                 return;
             }
             float[] imageMatrixValues = new float[9];
             matrix.getValues(imageMatrixValues);
             float scale = Math.abs(imageMatrixValues[0]) + Math.abs(imageMatrixValues[1]);
             matrix.setScale(scale, scale);
             float translate = scrollY - (drawable.getIntrinsicHeight() * scale - getHeight());
             if (translate > 0) {
                 translate = 0;
             }
             matrix.postTranslate(0, translate);
             setImageMatrix(matrix);
         }

         private final Runnable runnable = new Runnable() {
             @Override
             public void run() {
                 if (translate > getDrawable().getIntrinsicHeight() - DensityUtil.dp2px(getContext(), 200)) {
                     isScrollDown = false;
                 } else if (translate < -DensityUtil.dp2px(getContext(), 200)) {
                     isScrollDown = true;
                 }
                 if (isScrollDown) {
                     translate += 10;
                 } else {
                     translate -= 10;
                 }
                 LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) getLayoutParams();
                 params.topMargin = (int) translate;
                 setLayoutParams(params);
                 scrollWindow(-translate);
                 HandlerUtil.runOnUiThreadDelay(this, 16);
             }
         };

        public void show() {
            HandlerUtil.runOnUiThreadDelay(runnable, 16);
       }
    }


接下來就是拼接了,ScrollWindowImageView作爲RecyclerView ViewHolder的ItemView,當滑動時,將滑動響應傳遞給ScrollWindowImageView,若向上滑,ImageView圖像Bitmap往下移動   

    public class ScrollWindowImageView extends AppCompatImageView {
        private boolean initialized;
        private Matrix matrix;
        public ScrollWindowImageView(Context context) {
            this(context, null);
        }

        public ScrollWindowImageView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }

        public ScrollWindowImageView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            setScaleType(ScaleType.MATRIX);
        }

        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            initialized = true;
            initImage();
        }

        private void initImage() {
            final Drawable drawable = getDrawable();
            if (!initialized || drawable == null) {
                return;
            }
            final int viewWidth = getWidth();
            final int viewHeight = getHeight();
            final int drawableWidth = drawable.getIntrinsicWidth();
            final int drawableHeight = drawable.getIntrinsicHeight();
            float scaleX = (float) viewWidth / drawableWidth;
            matrix = getImageMatrix();
            matrix.setScale(scaleX, scaleX);
            matrix.postTranslate(0, -(drawableHeight * scaleX - viewHeight));
            setImageMatrix(matrix);
        }

        public void scrollWindow(float scrollY) {
            final Drawable drawable = getDrawable();
            if (!initialized || drawable == null) {
                return;
            }
            float[] imageMatrixValues = new float[9];
            matrix.getValues(imageMatrixValues);
            float scale = Math.abs(imageMatrixValues[0]) + Math.abs(imageMatrixValues[1]);
            matrix.setScale(scale, scale);
            float translate = scrollY - (drawable.getIntrinsicHeight() * scale - getHeight());
            if (translate > 0) {
                translate = 0;
            }
            matrix.postTranslate(0, translate);
            setImageMatrix(matrix);
        }
    }
    

    public class ScrollWindowActivity extends BaseActivity {

        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_scroll_window);
            RecyclerView recyclerView = findViewById(R.id.scroll_window_recycler_view);
            recyclerView.setLayoutManager(new LinearLayoutManager(this));
            ScrollWindowAdapter adapter = new ScrollWindowAdapter(this);
            recyclerView.setAdapter(adapter);
            recyclerView.addOnScrollListener(scrollListener);
            adapter.notifyDataSetChanged();
        }

        private RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
                int childCount = layoutManager.getChildCount();
                for (int i = 0; i < childCount; i++) {
                    View view = layoutManager.getChildAt(i);
                    Object tag = view.getTag();
                    if (tag instanceof ScrollWindowViewHolder) {
                        ScrollWindowViewHolder holder = (ScrollWindowViewHolder) tag;
                        holder.onScrolled(recyclerView, view, dx, dy);
                    }
                }
            }
        };

        static class ScrollWindowAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
            private LayoutInflater inflater;

            ScrollWindowAdapter(Context context) {
                inflater = LayoutInflater.from(context);
            }

            @NonNull
            @Override
            public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
                if (viewType == 1) {
                    return new ScrollWindowViewHolder(inflater.inflate(R.layout.item_scroll_ad_window, parent, false));
                }
                return new NormalViewHolder(inflater.inflate(R.layout.item_scroll_normal, parent, false));
            }

            @Override
            public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
                if (position == 6) {
                    ((ScrollWindowViewHolder) holder).imageView.setImageResource(R.mipmap.car);
                }
            }

            @Override
            public int getItemCount() {
                return 21;
            }

            @Override
            public int getItemViewType(int position) {
                if (position == 6) {
                    return 1;
                }
                return 0;
            }
        }

        static class NormalViewHolder extends RecyclerView.ViewHolder {
            NormalViewHolder(@NonNull View itemView) {
                super(itemView);
            }
        }

        static class ScrollWindowViewHolder extends RecyclerView.ViewHolder {
            private ScrollWindowImageView imageView;

            ScrollWindowViewHolder(@NonNull View itemView) {
                super(itemView);
                itemView.setTag(this);
                imageView = itemView.findViewById(R.id.ad_window_image_view);
            }

            void onScrolled(RecyclerView recyclerView, View itemView, int dx, int dy) {
                int parentBottom = recyclerView.getBottom();
                int itemBottom = itemView.getBottom();
                if (itemBottom < parentBottom) {
                    int scrollY = parentBottom - itemBottom;
                    imageView.scrollWindow(scrollY);
                }
            }
        }
    }


此方案支持屏幕內有多個item爲廣告位,數據配置填充靈活

方案和實現都比較簡單,不過這種交互體驗還是挺好的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章