1. 程式人生 > >Android 仿知乎廣告控制元件,廣告圖隨滑動控制元件滑動

Android 仿知乎廣告控制元件,廣告圖隨滑動控制元件滑動

仿知乎廣告模組,效果:RecyclerView其中的一個item是廣告圖片

- 知乎的效果圖如下:

  • 從下到上

  • 這裡寫圖片描述

  • 從上到下

  • 這裡寫圖片描述

- 仿的效果圖:

  • 兩種情況,一種是廣告圖片比滑動控制元件長,另外一種是廣告圖片比滑動控制元件短,效果如下:

  • 廣告圖片比滑動控制元件短:
    這裡寫圖片描述

  • 廣告圖片比滑動控制元件長:
    這裡寫圖片描述

實現思路

1.通過給RecyclerView設定addOnScrollListener監聽監聽廣告框是否出現在視野中
2.通過這個方法獲取讀取對應區域的bitmap物件,其中inputStream是圖片的資料流
BitmapRegionDecoder mDecoder = BitmapRegionDecoder.newInstance(inputStream, true);
3.通過方法1判定需要讀取的區域,用方法2中的mDecoder物件讀取對應區域
mDecoder.decodeRegion(mRect, null);
其中mRect用來控制讀取範圍
private final Rect mRect = new Rect();

- 程式碼解析

  • 1.通過給RecyclerView設定了addOnScrollListener()監控滑動


    private class OnScrollLisrener extends RecyclerView.OnScrollListener {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            //獲取廣告的itemView
View ggView = linearLayoutManager.findViewByPosition(ggPosition); if (ggView == null) { return; } if (ggImageView == null) { return; } //獲取滑動控制元件的高 parentHeight = mRecyclerView.getHeight(); //圖片距離滑動控制元件的上下距離
int topOrBottomPadding; int top = ggView.getTop(); int left = 0; int right = imageWidth; int bottom = ggView.getBottom(); //如果圖片比滑動控制元件短 if (parentHeight > imageHeight) { //計算圖片距離頂部的距離和圖片距離底部的距離 topOrBottomPadding = (parentHeight - imageHeight) / 2; //獲取item的高 int itemHeight = ggView.getHeight(); if (top >= parentHeight - itemHeight - topOrBottomPadding) { //如果超出底部,就一直顯示圖片的底部 bottom = imageHeight; top = bottom - itemHeight; } else if (top <= topOrBottomPadding) { //如果超出頂部,就一直顯示圖片的頂部 top = 0; bottom = top + itemHeight; } else { //處於圖片中的時候,自由滑動 top -= topOrBottomPadding; bottom = top + itemHeight; } } mRect.set(left, top, right, bottom); //非同步(非同步時會卡...貌似是因為執行緒延遲的問題) // executorService.execute(bitmapRunnable); //同步→ ggBitmap = mDecoder.decodeRegion(mRect, null); if (ggBitmap != null) { ggImageView.setImageBitmap(ggBitmap); } //←同步 } }
  • 2.activity中setGGViewPosition()方法是為了讓adapter把廣告item的position,最好還有廣告的ImageView丟擲(省的在onScroll裡重複去尋找)
 //用來從adapter裡設定廣告item的位置和ImageView(擴充套件:從這裡傳廣告的圖片地址,然後去載入)
    public void setGGViewPosition(int ggPosition, ImageView imageView) {
        this.ggPosition = ggPosition;
        this.ggImageView = imageView;
    }
  • 3.adapter中onBindViewHolder()方法中把位置和ImageView設定給Activity,這裡可以在方法中再加一個網路圖片的連結,在activity中載入圖片,廣告圖片就可以隨時變動了
 @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        int itemViewType = getItemViewType(position);
        switch (itemViewType) {
            case ItemBean.ITEM_GG:
                GgViewHolder ggHolder = (GgViewHolder) holder;

                mainActivity.setGGViewPosition(position, ggHolder.ggView);
                break;
            case ItemBean.ITEM_PT:

                break;
        }
    }

- activity程式碼,裡面有詳細註釋

裡面有一些非同步任務程式碼,這裡其實現在不能載入過大的圖片,不然可能會卡頓,如果有人優化好了,貼評論裡或者給我私信,我會共享給大家


public class MainActivity extends AppCompatActivity {

    //adapter中廣告控制元件裡的ImageView
    private ImageView ggImageView;

    //根據位置讀取的廣告圖片
    private Bitmap ggBitmap;

    //廣告item所在的位置
    private int ggPosition = -1;

    //滑動控制元件
    private RecyclerView mRecyclerView;

    //adapter
    private CopyZhiHuAdapter adapter;

    //線性佈局
    private LinearLayoutManager linearLayoutManager;

    //用來控制讀取範圍
    private final Rect mRect = new Rect();

    //讀取區域bitmap的類
    private BitmapRegionDecoder mDecoder;

    //需要顯示的廣告圖片的高
    private int imageHeight;

    //需要顯示的廣告的寬
    private int imageWidth;

    //滑動控制元件的高
    private int parentHeight;

    //忽略這些,我想非同步的處理資料,結果有明顯的卡頓
    private ExecutorService executorService;
    //忽略這些,我想非同步的處理資料,結果有明顯的卡頓
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (ggBitmap != null) {
                ggImageView.setImageBitmap(ggBitmap);
            }
        }
    };
    //忽略這些,我想非同步的處理資料,結果有明顯的卡頓
    private Runnable bitmapRunnable = new Runnable() {
        @Override
        public void run() {
            ggBitmap = mDecoder.decodeRegion(mRect, null);
            mHandler.sendEmptyMessage(1);
        }
    };


    //用來從adapter裡設定廣告item的位置和ImageView(擴充套件:從這裡傳廣告的圖片地址,然後去載入)
    public void setGGViewPosition(int ggPosition, ImageView imageView) {
        this.ggPosition = ggPosition;
        this.ggImageView = imageView;
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        adapter = new CopyZhiHuAdapter(this);
        mRecyclerView = (RecyclerView) findViewById(R.id.start);
        linearLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(linearLayoutManager);
        mRecyclerView.setAdapter(adapter);
        mRecyclerView.addOnScrollListener(new OnScrollLisrener());

        //用執行緒池更新圖片
        executorService = Executors.newSingleThreadExecutor();
        addJia();
        addImageResources();

    }

    //假裝這裡是非同步載入的網路圖片
    private void addImageResources() {
        try {
            //gg_image2,gg_image3,gg_image4,三張圖片的高度不一樣,順序為:從長到短
            @SuppressWarnings("ResourceType")
            InputStream inputStream = getResources().openRawResource(R.mipmap.gg_image4);
            mDecoder = BitmapRegionDecoder.newInstance(inputStream, true);
            imageHeight = mDecoder.getHeight();
            imageWidth = mDecoder.getWidth();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private class OnScrollLisrener extends RecyclerView.OnScrollListener {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            //獲取廣告的itemView
            View ggView = linearLayoutManager.findViewByPosition(ggPosition);
            if (ggView == null) {
                return;
            }
            if (ggImageView == null) {
                return;
            }
            //獲取滑動控制元件的高
            parentHeight = mRecyclerView.getHeight();

            //圖片距離滑動控制元件的上下距離
            int topOrBottomPadding;

            int top = ggView.getTop();
            int left = 0;
            int right = imageWidth;
            int bottom = ggView.getBottom();

            //如果圖片比滑動控制元件短
            if (parentHeight > imageHeight) {
                topOrBottomPadding = (parentHeight - imageHeight) / 2;
                //獲取item的高
                int itemHeight = ggView.getHeight();
                if (top >= parentHeight - itemHeight - topOrBottomPadding) {
                    //如果超出底部,就一直顯示圖片的底部
                    bottom = imageHeight;
                    top = bottom - itemHeight;
                } else if (top <= topOrBottomPadding) {
                    //如果超出頂部,就一直顯示圖片的頂部
                    top = 0;
                    bottom = top + itemHeight;
                } else {
                    //處於圖片中的時候,自由滑動
                    top -= topOrBottomPadding;
                    bottom = top + itemHeight;
                }
            }
            mRect.set(left, top, right, bottom);
            //非同步(非同步時會卡...貌似是因為執行緒延遲的問題)
//            executorService.execute(bitmapRunnable);

            //同步→
            ggBitmap = mDecoder.decodeRegion(mRect, null);
            if (ggBitmap != null) {
                ggImageView.setImageBitmap(ggBitmap);
            }
            //←同步
        }
    }

    //新增假資料
    private void addJia() {
        List<ItemBean> list = new ArrayList<>();

        for (int i = 0; i < 12; i++) {
            list.add(new ItemBean(ItemBean.ITEM_PT, "aaa" + i));
        }
        list.add(new ItemBean(ItemBean.ITEM_GG, "ggg"));
        for (int i = 0; i < 12; i++) {
            list.add(new ItemBean(ItemBean.ITEM_PT, "bbb" + i));
        }
        adapter.setData(list);
    }
}

- adapter程式碼

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

import java.util.List;

/**
 * Created by 郭輝 on 2017/12/18 16:37.
 * TODO:
 */

public class CopyZhiHuAdapter extends RecyclerView.Adapter<CopyZhiHuAdapter.ViewHolder> {

    private List<ItemBean> mList = null;
    private MainActivity mainActivity = null;

    public CopyZhiHuAdapter(MainActivity mainActivity) {
        this.mainActivity = mainActivity;
    }

    public void setData(List<ItemBean> list) {
        this.mList = list;
        notifyDataSetChanged();
    }

    @Override
    public int getItemViewType(int position) {
        ItemBean itemBean = mList.get(position);
        return itemBean.getType();
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        ViewHolder holder = null;
        if (viewType == ItemBean.ITEM_PT) {
            holder = new PtViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout_pt, parent, false));
        } else if (viewType == ItemBean.ITEM_GG) {
            holder = new GgViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout_gg, parent, false));
        }
        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        int itemViewType = getItemViewType(position);
        switch (itemViewType) {
            case ItemBean.ITEM_GG:
                GgViewHolder ggHolder = (GgViewHolder) holder;

                mainActivity.setGGViewPosition(position, ggHolder.ggView);
                break;
            case ItemBean.ITEM_PT:

                break;
        }
    }

    @Override
    public int getItemCount() {
        return mList != null ? mList.size() : 0;
    }

    class ViewHolder extends RecyclerView.ViewHolder {

        public ViewHolder(View itemView) {
            super(itemView);
        }
    }

    /**
     * 普通
     */
    class PtViewHolder extends ViewHolder {

        public PtViewHolder(View itemView) {
            super(itemView);
        }
    }

    /**
     * 廣告
     */
    class GgViewHolder extends ViewHolder {
        ImageView ggView;
        View itemView;

        public GgViewHolder(View itemView) {
            super(itemView);
            this.itemView = itemView;
            ggView = itemView.findViewById(R.id.item_imageview);
        }
    }
}

-普通item佈局:item_layout_pt

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#ffffff"
    android:orientation="vertical">

    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="#dddddd" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="20dp"
        android:text="普通的itemView" />

    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="#dddddd" />
</LinearLayout>
  • 廣告item佈局:item_layout_gg
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/item_imageview"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:src="@mipmap/default_loading"
        app:layout_constraintDimensionRatio="W,1.5:3"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>
  • ItemBean
/**
 * Created by 郭輝 on 2017/12/18 16:49.
 * TODO:
 */

public class ItemBean {
    public static final int ITEM_PT = 0;
    public static final int ITEM_GG = 1;

    public ItemBean(int type, String name) {
        this.type = type;
        this.name = name;
    }

    private int type;
    private String name;

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

-專案地址: