代码地址如下:
http://www.demodashi.com/demo/14904.html

###一、概述
貌似前段时间刷知乎看到的一种非常有特色的广告展现方式,即在列表页,某一个Item显示背后部分广告图,随着列表滚动,会逐渐展示全部图片。

刚看到的时候就想实现一哈,一直比较懒,公众号后台也有人问如何实现,今天来给大家讲解下,当然了,目前一些自定义View已经不算难题,所以本文的讲解会做一些实现思路引导,相信不会是那么枯燥的文章,希望对大家有一定的帮助。

恩,现在知乎上已经找不到该效果了,试了多个历史版本也没找到,那只能贴实现的效果图了~

效果图如下:

二、思路

实现在列表中展示某张图片:

往上滚动:在图片刚出现时展示顶部部分,随着滚动部分展示全部
往下滚动:在图片刚出现时展示底部部分,随着滚动部分展示全部

换句话说,我们需要在列表滚动时,改变图片显示的部分。

两个点:
捕获列表滚动的dy,不管是ListView还是RecyclerView相信这一点都能做到
图片显示部分变化,我们可以利用canvas.translate
结合一下,就是,监听列表的滚动dy,传给我们的图片控件,设置translate,然后绘制。

到这里,思路非常清晰,这个东西肯定能做了。

初步方案:自定义一个View,自己去绘制bitmap,对外暴露setDy(dy),然后根据dy做canvas偏移重绘即可。

有了初步方案,基本不慌了,那么再想想?

能否利用已有的控件,比如ImageView呢?

肯定可以,这样省去了我们去声明一个接受图片的属性,我们编写一个子类,依然是通过设置src去使用。

那继承ImageView实现一波再说。

####三、实现
####布局
主布局文件,一个RecyclerView即可:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerViewxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/id_recyclerview"android:layout_width="match_parent"android:layout_height="match_parent"/>

item布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@drawable/item_bg"android:gravity="center"><com.zhang.demo_rvadimage.AdImageViewVersion1android:id="@+id/id_iv_ad"android:layout_width="match_parent"android:layout_height="180dp"android:scaleType="matrix"android:src="@mipmap/grsm"android:visibility="gone" /><TextViewandroid:layout_margin="12dp"android:id="@+id/id_tv_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="这是title"android:textSize="16dp"android:textStyle="bold" /><TextViewandroid:id="@+id/id_tv_desc"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/id_tv_title"android:layout_marginLeft="12dp"android:layout_marginRight="12dp"android:layout_marginBottom="12dp"android:text="这是描述" />
</RelativeLayout>

很简单,先不用管AdImageViewVersion1类,这将是我们具体的实现类。
通过布局文件,可以看到,我们只使用了一个item布局文件,然后通过visible,gone控制展示不同形态。

Activity

public class MainActivity extends AppCompatActivity {private RecyclerView mRecyclerView;private LinearLayoutManager mLinearLayoutManager;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mRecyclerView = findViewById(R.id.id_recyclerview);List<String> mockDatas = new ArrayList<>();for (int i = 0; i < 100; i++) {mockDatas.add(i + "");}mRecyclerView.setLayoutManager(mLinearLayoutManager = new LinearLayoutManager(this));mRecyclerView.setAdapter(new CommonAdapter<String>(MainActivity.this,R.layout.item,mockDatas) {@Overrideprotected void convert(ViewHolder holder, String o, int position) {if (position > 0 && position % 6 == 0) {holder.setVisible(R.id.id_tv_title, false);holder.setVisible(R.id.id_tv_desc, false);holder.setVisible(R.id.id_iv_ad, true);} else {holder.setVisible(R.id.id_tv_title, true);holder.setVisible(R.id.id_tv_desc, true);holder.setVisible(R.id.id_iv_ad, false);}}});
}

仅仅是设置数据了,Adapter这里用了

implementation 'com.zhy:base-rvadapter:3.0.3'

你可以随便用一个你自己喜欢的Adapter封装类。

到这里,一个列表页就显示出来了,并且每隔6个会显示成图片。

现在才正式开始实现。

自定义AdImageView

public class AdImageViewVersion1 extends AppCompatImageView {public AdImageViewVersion1(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}private RectF mBitmapRectF;private Bitmap mBitmap;private int mMinDy;@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mMinDy = h;Drawable drawable = getDrawable();if (drawable == null) {return;}mBitmap = drawableToBitamp(drawable);mBitmapRectF = new RectF(0, 0,w,mBitmap.getHeight() * w / mBitmap.getWidth());}private Bitmap drawableToBitamp(Drawable drawable) {if (drawable instanceof BitmapDrawable) {BitmapDrawable bd = (BitmapDrawable) drawable;return bd.getBitmap();}int w = drawable.getIntrinsicWidth();int h = drawable.getIntrinsicHeight();Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(bitmap);drawable.setBounds(0, 0, w, h);drawable.draw(canvas);return bitmap;}// ... 省略一些代码
}

因为我们要绘制,所以这里我们把drawable转成bitmap,然后我们默认要显示最底部,所以需要一个最小的偏移,即控件高度。

这些事情,我们都在onSizeChanged做了。

并且我们根据当前控件宽度,对bitmap进行了缩放,并将缩放后的尺寸存在了mBitmapRectF中,以便于绘制。

那么接下来就是绘制了,还记得绘制过程中,我们主要利用translate来控制绘制的区域,所以我们还要对外暴露一个setDy方法,so,我们的代码大致是这样的:

private int mDy;public void setDy(int dy) {if (getDrawable() == null) {return;}mDy = dy - mMinDy;if (mDy <= 0) {mDy = 0;}if (mDy > mBitmapRectF.height() - mMinDy) {mDy = (int) (mBitmapRectF.height() - mMinDy);}invalidate();
}@Override
protected void onDraw(Canvas canvas) {if (mBitmap == null) {return;}canvas.save();canvas.translate(0, -mDy);canvas.drawBitmap(mBitmap, null, mBitmapRectF, null);canvas.restore();
}

setDy的时候,我们做了一个边界判断,最小的情况,我们偏移-mMinDy,显示图片的底部。
最大的时候,我们便宜图片高度-mMinDy,显示顶部部分。

所以我们对传入的值做了最小与最大值判断。

那么在绘制的时候,就简单了,先translate dy距离,然后绘制即可。

到这里我们的自定义View部分就结束了,代码很少~

结合RecyclerView

接下来就是在RecyclerView滚动时,给我们传入dy即可。

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {@Overridepublic void onScrolled(RecyclerView recyclerView, int dx, int dy) {super.onScrolled(recyclerView, dx, dy);int fPos = mLinearLayoutManager.findFirstVisibleItemPosition();int lPos = mLinearLayoutManager.findLastCompletelyVisibleItemPosition();for (int i = fPos; i <= lPos; i++) {View view = mLinearLayoutManager.findViewByPosition(i);AdImageViewVersion1 adImageView = view.findViewById(R.id.id_iv_ad);if (adImageView.getVisibility() == View.VISIBLE) {adImageView.setDy(mLinearLayoutManager.getHeight() - view.getTop());}}}
});

通过addOnScrollListener监听,当滚动时,拿到所有可见的Item,找出正在显示图片的Item。然后调用setDy,dy的值为mLinearLayoutManager.getHeight() - view.getTop(),当View从最底部出现的时候为0,当View到达最顶部的时候为当前rv的高度。

你可以合理的利用setDy传入的值,做移动差,显示区域从上到下等,都可以。

这样就完成了~~

一句话实现:即滚动时不断改变dy,然后translate绘制即可。

####四、再想想
看着这个代码,好像drawableToBitamp看起来非常不爽,也是比较耗内存的部分。我们再想想:

本身Drawable就是能绘制的,为什么我们要转成bitmap呢?本身Drawable就是能绘制的,为什么我们要转成bitmap呢?

好像有道理,ImageView本身绘制的就是Drawable,我们需要控制的就是这个Drawable的绘制范围要足够大,不能被控件本身的宽高所影响,导致图片被压扁。

好像有那么一个方法:

drawable.setBounds();

那就简单了,去除drawable2bitmap的代码,直接利用原本的绘制即可,我们唯一要做的就是设置bounds,做一个translate dy即可。

完整代码:

public class AdImageView extends AppCompatImageView {// 删除构造方法private int mDx;private int mMinDx;public void setDx(int dx) {if (getDrawable() == null) {return;}mDx = dx - mMinDx;if (mDx <= 0) {mDx = 0;}if (mDx > getDrawable().getBounds().height() - mMinDx) {mDx = getDrawable().getBounds().height() - mMinDx;}invalidate();}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mMinDx = h;}public int getDx() {return mDx;}@Overrideprotected void onDraw(Canvas canvas) {Drawable drawable = getDrawable();int w = getWidth();int h = (int) (getWidth() * 1.0f / drawable.getIntrinsicWidth() * drawable.getIntrinsicHeight());drawable.setBounds(0, 0, w, h);canvas.save();canvas.translate(0, -getDx());super.onDraw(canvas);canvas.restore();}
}

####五、本篇总结

看到当看一个效果,可以先对它进行拆分,找出关键点,针对每个关键点,考虑可行性。

如果确定每个点都可行,那么基本的方案就出来了。

有了基本的方案,不要着急写,再想想还有无改善空间。

####六、参考文章
博客:
https://blog.csdn.net/lmj623565791/article/details/78714705
Android 仿知乎创意广告

代码地址如下:
http://www.demodashi.com/demo/14904.html

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

Android 仿知乎创意广告相关推荐

  1. Android 仿知乎创意广告 广告还能这么玩?

    本文已在我的公众号hongyangAndroid原创首发. 转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/78714705 本文 ...

  2. android仿知乎按钮动效,Android仿知乎客户端关注和取消关注的按钮点击特效实现思路详解...

    先说明一下,项目代码已上传至github,不想看长篇大论的也可以先去下代码,对照代码,哪里不懂点哪里. 代码在这https://github.com/zgzczzw/ZHFollowButton 前几 ...

  3. android仿知乎工作内容,Android仿知乎日报开屏页效果

    先看看知乎日报开屏页的效果,非常漂亮的开屏效果 ezgif.com-resize (2).gif 然后我来一个 ezgif.com-resize (1).gif 也不错~感觉可以以假乱真了~ 很简单, ...

  4. Android仿制知乎滑动广告条

    开篇废话 前些天看到知乎和网易在APP中加了一个滑动的广告,所以就仿做了一个,其实没什么技术含量,也没什么难度,滑动广告之GitHub地址,帮我点个Star. 滑动广告条 大概效果就是下图这样. 思路 ...

  5. android广告拦截 知乎,Android仿制知乎滑动广告条

    开篇废话 前些天看到知乎和网易在APP中加了一个滑动的广告,所以就仿做了一个,其实没什么技术含量,也没什么难度,滑动广告之GitHub地址,帮我点个Star. 滑动广告条 大概效果就是下图这样. 滑动 ...

  6. Android仿知乎图片墙

    公司项目中有一个需求,需要在一个游戏列表顶部显示一个由此列表中游戏图标拼接而成的图片. 就是类似知乎的这种效果. 初步思路:1.获得图片地址并下载图片 2.根据下载的图片拼接一张大图片 3.图片旋转4 ...

  7. 高仿知乎android,Android高仿知乎首页Behavior

    Android自定义Behavior实现跟随手势滑动,显示隐藏标题栏.底部导航栏及悬浮按钮 Android Design包下的CoordinatorLayout是相当重要的一个控件,它让许多动画的实现 ...

  8. github android 开源,Android github开源项目学习之—仿知乎APP(一)

    前言 (本人菜鸟,如有错误请指正) 一直奇怪网上说的Recycler让自己定制点击事件是怎么回事,看了这个大牛的仿知乎 Android客户端源码才知道,他是这样做的-- 写了一个RecyclerVie ...

  9. android 如何学习开源项目,Android github开源项目学习之—仿知乎APP(一)

    前言 (本人菜鸟,如有错误请指正) 一直奇怪网上说的Recycler让自己定制点击事件是怎么回事,看了这个大牛的仿知乎 Android客户端源码才知道,他是这样做的-- 写了一个RecyclerVie ...

最新文章

  1. 再见 Python,Hello Julia!
  2. redis设置主从复制-slave Replication--解决报错:(error) READONLY You can't write against a read only slave.
  3. java定义全局变量_矮油,你知道什么是 Java变量的作用域 嘛?
  4. iOS深入探索直播推拉流实现流程(二:推流权限判断 )
  5. 单片机GPIO软件模拟I2C通讯程序
  6. 软件测试报告重点审核点有哪些,软件测试-测试报告.doc
  7. sql select...for update是锁行还是锁表
  8. python学习之字符串函数用法
  9. InstallSield更新包快速入门文档
  10. 【React】绑定this的5种方法总结
  11. java并发编程实战
  12. Android仿新浪微博弹出界面动画,Android仿新浪微博个人信息界面及其他效果
  13. 斯皮尔曼相关系数的解读
  14. Brocade博科光纤交换机之 常用命令
  15. JQ6500语音模块
  16. docker和k8s的常见命令
  17. 人工神经网络的论文及算法代码
  18. 2023年厦门大学应用统计专硕考研上岸经验分享
  19. PHP 发送电子邮件
  20. 003_JS实现图片轮播与点击播放

热门文章

  1. STM32F103:一.(2)STLINK的配置
  2. android9的手机,可防手机上瘾?安卓9.0首批升级的机型都在这里
  3. java flink项目_IDEA上运行Flink任务的实战教程
  4. linux下的网桥介绍
  5. git报错 ssh: Could not resolve hostname gitee.com:xxxxxx: Name or service not known fatal
  6. 菜鸟学习笔记:Java提升篇5(IO流1——IO流的概念、字节流、字符流、缓冲流、转换流)
  7. Spring Data Elasticsearch案例详解
  8. Serializable序列化
  9. 失业状态,整理一下近期的面试问题 -- 直面自我
  10. 浏览器对同一IP的最大并发请求数记录