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;
    }
}

-项目地址:

链接:https://pan.baidu.com/s/1bOonaY 密码:g1k8

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值