17年底刷知乎時發(fā)現(xiàn)的新型布局展示,感覺很有創(chuàng)意哟绊。知乎用此來投放廣告册赛,用正常列表Item的高度通過滑動展示整張手機屏幕大小的圖片臀叙。下面是仿做的Gif效果圖:
ad-image-view-demo.gif
實現(xiàn)分析
從效果上我們可以大致看出是一個兩種ItemType的RecyclerView笆豁,而且從知乎上來看這個特殊的Item只放了圖片厌丑,所以重點工作就是寫一個自定義ImageView,需要的功能包括展示圖片渔呵,滑動圖片等,現(xiàn)在砍鸠,我們詳細分析下滑動流程扩氢,下圖是滑動過程的邏輯分析:
知乎廣告欄滑動解析圖.png
代碼解讀
自定義View中的核心代碼:
private Context mContext;
/**
* 展示圖片的寬高和Item高度
*/
private int mDisplayWidth, mDisplayHeight, mItemHeight;
/**
* Item與背景圖片滑動距離的相對比例
*/
private float ratio = 1;
/**
* 圖片的滑動區(qū)域
*/
private int mDisplayDistance;
/**
* 圖片資源
*/
private Drawable imageDrawable;
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mItemHeight = h;
imageDrawable = getDrawable();
// 使用Item的寬度作為圖片的寬度
mDisplayWidth = getWidth();
// 可以直接用手機屏幕的高度(別忘了減去狀態(tài)欄高度),不過滑動起來會相對靜止爷辱,比較生硬录豺,可以稍微減少此值以添加相對滑動的感覺
mDisplayHeight = getScreenHeight(mContext) - getStatusBarHeight(mContext) - 100;
// 圖片滑動區(qū)域
mDisplayDistance = mDisplayHeight - mItemHeight;
}
在onDraw中進行狀態(tài)保存和位移
@Override
protected void onDraw(Canvas canvas) {
imageDrawable.setBounds(0, 0, mDisplayWidth, mDisplayHeight);
canvas.save();
canvas.translate(0, -mDisplayDistance * ratio);
Log.d("SwipeDisplayImageView", "translateY = " + -mDisplayDistance * ratio);
super.onDraw(canvas);
canvas.restore();
}
在RecyclerView滑動監(jiān)聽中調(diào)用朦肘,用來刷新Item在圖片的位置比例,核心邏輯
/**
* @param itemTop itemView.getTop()
* @param recyclerViewHeight recyclerView.getHeight()
*/
public void refreshRatio(int itemTop, int recyclerViewHeight) {
// 背景圖片位移的范圍
int scope = recyclerViewHeight - mItemHeight;
ratio = itemTop * 1.0f / scope;
Log.d("SwipeDisplayImageView", "ratio = " + ratio);
if (ratio < 0) {
ratio = 0;
}
if (ratio > 1) {
ratio = 1;
}
invalidate();
}
RecyclerView添加滑動監(jiān)聽
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Overrid
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int firstPosition = mLinearLayoutManager.findFirstCompletelyVisibleItemPosition();
int lastPosition = mLinearLayoutManager.findLastCompletelyVisibleItemPosition();
// 邏輯處理的范圍是從Item完全進入到完全離開
for (int i = firstPosition; i <= lastPosition; i++) {
RecyclerView.ViewHolder viewHolder = mRecyclerView.findViewHolderForAdapterPosition(i);
// 判斷ItemType
if (viewHolder.getItemViewType() == TYPE_AD) {
View itemView = viewHolder.itemView;
int top = itemView.getTop();
int height = recyclerView.getHeight();
SwipeDisplayImageView swipeDisplayImageView = itemView.findViewById(R.id.iv_swipe_display);
swipeDisplayImageView.refreshSwipeRatio(top, height);
}
}
}
});
xml代碼
<?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="180dp">
<com.example.zengsheng.advertisement.SwipeDisplayImageView
android:id="@+id/iv_swipe_display"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="matrix"
android:src="@drawable/pic1" />
</RelativeLayout>
總結(jié)
總體來說代碼量不多双饥,沒有自定義繪制的部分媒抠,主要是需要分析清楚效果的實現(xiàn)過程,完成位移的邏輯處理即可咏花,注意在xml中添加android:scaleType="matrix"這條屬性趴生。