Android 仿知乎廣告控制元件,廣告圖隨滑動控制元件滑動
阿新 • • 發佈:2019-02-16
仿知乎廣告模組,效果: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;
}
}