Viewpager實現 輪播廣告欄(BannerPager)
實現這個功能的目前已經有很多種方式了,譬如說繼承ViewGroup仿ViewPager通過adapter迴圈add、dstory指定的item,使用fragment新增移除view的方式,設定viewpager的索引條目達到上限==。以上幾種方式屬於Banner的常用選擇,其中動效做的非常出色的如程式碼家的ImageSlider,曾經在專案中也引用過,不過在使用的過程中出現了記憶體開銷過大的問題,1張圖片也可以左右輪迴切換的問題,不得不說是個硬傷。後來遇到了一種新的方式,於是分享出來給大家。
Banner和viewpgaer的最大區別是Banner可以在itemPosition=0和itemPosition=size-1之間來回無縫切換,因此我們理想狀態是作如下處理的:當滑動到itemPosition=0的時候,左側有對應itemPosition=0到itemPosition=size-1的view;當滑動到itemPosition=size-1的時候,右側有對應itemPosition=0到itemPosition=size-1的view。這是我們一般直接的做法:設定viewpager的索引條目達到上限。然而開銷的問題讓我們會想到複用,結合listview的adapter的viewholder形式,可能會採取第i屏的第itemPosition=position的view複用第1屏的itemPosition=position的view。這樣的做法看似不錯,但是對於擁有著程式設計師思維的我們覺得這僅僅是一個邏輯不夠嚴密,過程不夠完美,存在潛藏的隱患的idea。於是兩條道路,優化or另闢蹊徑。
其實再做上述過程中的考慮時候,我們想到了複用,想到了設定size=Integer.MAX_VALUE實現itemPosition=0和itemPosition=size-1之間的切換,但是仔細考慮一下,我們也僅僅需要實現itemPosition=0和itemPosition=size-1之間的切換,就可以交給viewpager本身的機制來完成剩下頁卡之間的輪播。假設一共需要展示4個view,於是畫圖如下:
... item=2 -> item=3 -> item=0 -> item=1 -> item=2 -> item=3 -> item=0 -> item=1 ...
也就是說我們發現可以考慮當itemPosition=3的時候下一屏itemPosition=0的view就是當前屏被滑動過去itemPosition=0的View,用來代替無限size=Integer.MAX_VALUE的實現方式,然而這樣難點又回到瞭如和實現兩者之間的無動畫切換。於是我們想到實現size=Integer.MAX_VALUE的本質其實就是對viewpager不停的addView和destoryView,當滑動到itemPosition=0的時候在左側新增一個itemPosition=3的view,當滑動到itemPosition=3的時候在右側新增一個itemPosition=0的view,但是後續的itemPosition=1和itemPosition=2的view就可以不用我們手動add進去,因為viewpager的本質會自動預設itemPosition=3左側、itemPosition=0右側存在itemPosition=1、2的view,因此上述圖我們可以簡化成:
|<—————————————|
itemPosition 3 -> 0 -> 1 -> 2 -> 3 -> 0
|—————————————>|
於是我們把addView簡化成了addView(0,item3View)和addView(5,item0View),而當載入到這兩個view的時候我們認為它載入的其實就是itemPosition=3和itemPosition=0的view。認為這個詞語在程式碼中的體現就是set,因此當我們在首尾各添加了一個view之後可以寫出如下程式碼:
if(getCurrentPosition() == 0){
setCurrentPosition(4);
}
if(getCurrentPosition() == 5){
setCurrentPosition(1);
}
注:getCurrentPosition() == 0的時候其實相當於把item4的佈局加到了item0的前面,此時資料就是item4的資料,所以把頁面設定到item4;getCurrentPosition() == 5的時候其實相當於把item0的佈局加到了item4的後面,此時資料就是item0的資料,所以把頁面設定到item0
完整程式碼:
package com.systoon.business.home.holder;
import android.app.Activity;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.systoon.business.home.bean.HomeActivityBean;
import com.systoon.business.home.utils.CommonUtils;
import com.systoon.business.home.view.HomeNewsListActivity;
import com.systoon.toon.R;
import com.systoon.toon.common.utils.DateUtils;
import com.systoon.toon.common.utils.ScreenUtil;
import java.util.ArrayList;
import java.util.List;
public class QlLHomeNewsViewHolder extends BaseMainAppViewHolder {
private final Activity mActivity;
private ViewPager mViewpager;
private LinearLayout mIndicateRoot;
private LinearLayout mRootEmpty;
private LinearLayout contentRoot;
private TextView mTitle;
private TextView mTime;
private int currentItem;
private TextView mMore;
private List<HomeActivityBean> datas;
private ArrayList<View> viewList;
private int count;
private long delayTime = 3000;
private ViewPager.OnPageChangeListener mOnPageChangeListener;
private WeakHandler handler = new WeakHandler();
public QlLHomeNewsViewHolder(View itemView, ViewGroup parent, List<HomeActivityBean> mList,
Activity activity) {
super(itemView);
mViewpager = (ViewPager) itemView.findViewById(R.id.news_banner);
mIndicateRoot = (LinearLayout) itemView.findViewById(R.id.dot_indicate_root);
contentRoot = (LinearLayout) itemView.findViewById(R.id.content_root);
mTitle = (TextView) itemView.findViewById(R.id.news_title);
mTime = (TextView) itemView.findViewById(R.id.time);
mMore = (TextView) itemView.findViewById(R.id.btn_more);
mActivity = activity;
datas = mList;
}
@Override
public void bindHolder(HomeActivityBean bean, int position) {
super.bindHolder(bean, position);
viewList = new ArrayList<>();
final List<HomeActivityBean.ExtrasData> list = bean.getDataList();
if (list != null) {
count = list.size();
mIndicateRoot.removeAllViews();
View dot;
if (count > 1) {// 多於一個輪播
for (int i = 0; i < count + 2; i++) {
viewList.add(LayoutInflater.from(itemView.getContext()).inflate(R.layout
.ql_home_show_type_banner_item_img,
null, false));
if (i < count) {
dot = new View(itemView.getContext());
mIndicateRoot.addView(dot, ScreenUtil.dp2px(6), ScreenUtil.dp2px(6));
setMargins(dot, ScreenUtil.dp2px(3), 0, ScreenUtil.dp2px(3), 0);
}
}
} else if (count == 1) {
viewList.add(LayoutInflater.from(itemView.getContext()).inflate(R.layout
.ql_home_show_type_banner_item_img,
null, false));
} else {// TODO 空介面處理
}
/**
* banner
*/
if (count > 0)
mViewpager.setAdapter(new PagerAdapter() {
@Override
public int getCount() {
return count > 1 ? count + 2 : count;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(final ViewGroup container, int position)
{//必須實現,例項化
View view = viewList.get(position);
final ImageView img = (ImageView) view.findViewById(R.id.img_content);
final HomeActivityBean.ExtrasData items;
if (count > 1) {
if (position == 0) {
items = list.get(count - 1);
} else if (position == count + 1) {
items = list.get(0);
} else {
items = list.get(position - 1);
}
} else {
items = list.get(position);
}
showImgRoundCrop("", img, img.getContext());
img.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (TextUtils.isEmpty(items.getDescription())) {
CommonUtils.goActivityH5(mActivity, items.getActivityId());
} else {
CommonUtils.toNewsDetail(mActivity, items.getNewsId());
}
// 跳轉
}
});
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object)
{//必須實現,銷燬
container.removeView(viewList.get(position));
}
});
mViewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int
positionOffsetPixels) {
if (mOnPageChangeListener != null) {
mOnPageChangeListener.onPageScrolled(toRealPosition(position, count),
positionOffset, positionOffsetPixels);
}
}
@Override
public void onPageSelected(int position) {
currentItem = position;
if (mOnPageChangeListener != null) {
mOnPageChangeListener.onPageSelected(toRealPosition(position, count));
}
// mPageIndex.setText(new StringBuilder().append(toRealPosition(position,
// count) + 1).append("/").append(count).toString());
if (mIndicateRoot.getChildCount() > 1) {// 多餘一個
HomeActivityBean.ExtrasData items = list.get(toRealPosition(position,
count));
for (int i = 0; i < mIndicateRoot.getChildCount(); i++) {
if (i == toRealPosition(position, count)) {
mIndicateRoot.getChildAt(i).setBackgroundResource(R.drawable
.icon_interact_news_banner_white_dot);
mTitle.setText(TextUtils.isEmpty(items.getTitle()) ? "" : items
.getTitle());
mTime.setText(TextUtils.isEmpty(items.getUpdateTime()) ? "" :
DateUtils.convertToTime(Long.parseLong(items
.getUpdateTime())
, "yyyy年MM月dd日"));
} else {
mIndicateRoot.getChildAt(i).setBackgroundResource(R.drawable
.icon_interact_news_banner_gray_dot);
}
}
}
}
@Override
public void onPageScrollStateChanged(int state) {
if (mOnPageChangeListener != null) {
mOnPageChangeListener.onPageScrollStateChanged(state);
}
switch (state) {
case 0://No operation
if (currentItem == 0) {
// viewPager.setCurrentItem(count+1, false);
mViewpager.setCurrentItem(count, false);
} else if (currentItem == count + 1) {
// viewPager.setCurrentItem(0, false);
mViewpager.setCurrentItem(1, false);
}
break;
case 1://start Sliding
if (currentItem == count + 1) {
// viewPager.setCurrentItem(0, false);
mViewpager.setCurrentItem(1, false);
} else if (currentItem == 0) {
// viewPager.setCurrentItem(count+1, false);
mViewpager.setCurrentItem(count, false);
}
break;
case 2://end Sliding
break;
}
}
});
/* if (datas.size() - 1 == position) {
mLine.setVisibility(View.GONE);
} else {
mLine.setVisibility(View.VISIBLE);
}
*/
mViewpager.setCurrentItem(1);
//TODO 設定偏移 mViewpager.setOffscreenPageLimit(count + 2);
mMore.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
HomeNewsListActivity.launcher(mMore.getContext());
}
});
if (count > 1)
startAutoPlay();
}
}
public void setOnPageChangeListener(ViewPager.OnPageChangeListener onPageChangeListener) {
mOnPageChangeListener = onPageChangeListener;
}
private int toRealPosition(int position, int count) {
int realPosition = (position - 1) % count;
if (realPosition < 0)
realPosition += count;
return realPosition;
}
public void startAutoPlay() {
handler.removeCallbacks(task);
handler.postDelayed(task, delayTime);
}
private final Runnable task = new Runnable() {
@Override
public void run() {
if (count > 1) {
currentItem = currentItem % (count + 1) + 1;
if (currentItem == 1) {
mViewpager.setCurrentItem(currentItem, false);
handler.postDelayed(task, delayTime);
} else {
mViewpager.setCurrentItem(currentItem);
handler.postDelayed(task, delayTime);
}
}
}
};
public void stopAutoPlay() {
handler.removeCallbacks(task);
}
}