效果图:
该banner功能有自动切换图片,点击图片可以自定义事件,手动滑动切换,异步加载图片
代码说话:
布局文件:
<!-- 广告位 -->
<FrameLayout
android:id="@+id/new_recommend"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<com.cyou.cmall.ui.HorizontalViewPager
android:id="@+id/viewpager"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<LinearLayout
android:id="@+id/ll_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="horizontal"
android:paddingBottom="8dp"
android:paddingLeft="26dip" />
</FrameLayout>
布局文件中我自定义了一个HorizontalViewPager,它是在viewpager的基础上做了一些修改,之所以这样做是因为,项目中使用到了下拉刷新控件,该布局最外层还有一个scrollview,会导致scrollview将手势事件拦截,而viewpager得不到滑动手势
下面是HorizontalViewPager的代码,如果不需要处理滑动手势,完全可以使用系统的viewpager控件
/**
* 复写该控件是因为在PullToRefreshScrollView中,viewpager无法水平滑动
*
* @author wangwei_cs
*/
public class HorizontalViewPager extends ViewPager {
private GestureDetector mGestureDetector;
public HorizontalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public HorizontalViewPager(Context context) {
super(context);
init(context);
}
private void init(Context context) {
mGestureDetector = new GestureDetector(context, new YScrollDetector());
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean dispatchTouchEvent = super.dispatchTouchEvent(ev);
if (dispatchTouchEvent) {
if (mGestureDetector.onTouchEvent(ev)) {
//请求父类放弃事件的处理
requestDisallowInterceptTouchEvent(true);
}
}
return dispatchTouchEvent;
}
class YScrollDetector extends SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
// 如果我们滚动更接近水平方向,返回true,自己处理,否则,让出处理权限
return (Math.abs(distanceX) > Math.abs(distanceY));
}
}
}
</span>
言归正传:在程序oncreate方法中调用一下方法
/**
* 初始化推荐广告专区
*/
private void initRecommendAd() {
HorizontalViewPager mViewPager = (HorizontalViewPager) findViewById(R.id.viewpager);
mIndicatorLayout = (LinearLayout) findViewById(R.id.ll_indicator);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
android.widget.FrameLayout.LayoutParams.MATCH_PARENT, height * 24 / 100);
mViewPager.setLayoutParams(params);
AdImagePagerAdapter mPagerAdapter = new AdImagePagerAdapter(mCxt, imageUrls, new ViewPagerItemClickListener() {
@Override
public void OnViewPagerItemClick(int position) {
if (imageUrls.size() > 0) {
if (imageUrls.size() == 1) {
boolean enable = urlEnable(imageUrls.get(0));
if (enable) {
handleClickEvent(position);
}
} else {
handleClickEvent(position);
}
}
}
});
List<String> lastAdUrls = getLastAdUrls();
if (lastAdUrls == null || lastAdUrls.size() == 0) {
// 没有记录上一次广告url信息,添加一个无效url路径
imageUrls.add(Constants.INVALID_URL);
LogHelper.e(TAG, "no last ad record");
} else {
LogHelper.e(TAG, "show last ad record");
imageUrls.addAll(lastAdUrls);
}
mAdComponent = new RecommendAdComponent(mCxt, mViewPager, mPagerAdapter, imageUrls, 5000,
mIndicatorLayout);
mAdComponent.startUpAdComponent();
mPagerAdapter.notifyDataSetChanged();
}
下面是AdImagePagerAdapter的源码
import java.util.List;
import android.content.Context;
import android.graphics.Bitmap;
import android.support.v4.view.PagerAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
import com.nostra13.universalimageloader.core.assist.SimpleImageLoadingListener;
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;
/**
* 无限循环adapter
*
* @author wangwei_cs
*/
public class AdImagePagerAdapter extends PagerAdapter {
protected static final String TAG = "AdImagePagerAdapter";
private List<String> imageUrls;
private LayoutInflater inflater;
private Context mCxt;
private ViewPagerItemClickListener mListener;
protected ImageLoader imageLoader;
private DisplayImageOptions options;
public AdImagePagerAdapter(Context context, List<String> imageUrls, ViewPagerItemClickListener listener) {
this.imageUrls = imageUrls;
this.mCxt = context;
inflater = LayoutInflater.from(context);
this.mListener = listener;
imageLoader = ImageLoader.getInstance();
options = new DisplayImageOptions.Builder()
.showImageForEmptyUri(R.drawable.ad_default)
.showImageOnFail(R.drawable.ad_default)
.showImageOnLoading(R.drawable.ad_default)
.resetViewBeforeLoading(true)
.cacheOnDisc(true)
.imageScaleType(ImageScaleType.EXACTLY)
.bitmapConfig(Bitmap.Config.RGB_565)
.considerExifParams(true)
.displayer(new FadeInBitmapDisplayer(300))
.build();
}
@Override
public int getCount() {
if (imageUrls.size() < 2) {
return imageUrls.size();
}
return Integer.MAX_VALUE;
}
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == arg1;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
final int index = position % imageUrls.size();
View view = inflater.inflate(R.layout.item_pager_image, container, false);
assert view != null;
ImageView imageView = (ImageView) view.findViewById(R.id.image);
final ProgressBar spinner = (ProgressBar) view.findViewById(R.id.img_loading);
imageLoader.displayImage(imageUrls.get(index), imageView, options,
new SimpleImageLoadingListener() {
@Override
public void onLoadingStarted(String imageUri, View view) {
spinner.setVisibility(View.VISIBLE);
}
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
String message = null;
switch (failReason.getType()) {
case IO_ERROR:
message = "Input/Output error";
break;
case DECODING_ERROR:
message = "Image can't be decoded";
break;
case NETWORK_DENIED:
message = "Downloads are denied";
break;
case OUT_OF_MEMORY:
message = "Out Of Memory error";
break;
case UNKNOWN:
message = "Unknown error";
break;
}
LogHelper.e(TAG, message);
spinner.setVisibility(View.GONE);
}
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
spinner.setVisibility(View.GONE);
}
});
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mListener !=null) {
mListener.OnViewPagerItemClick(index);
}
}
});
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
下面是重点的RecommendAdComponent自定义的广告推荐组件
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.support.v4.view.ViewPager.PageTransformer;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.LinearLayout;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
/**
* 展示广告轮转的组件
*
* @author wangwei_cs
*/
public class RecommendAdComponent {
/**
* 无效的图片切换时间,如果为0 表示不自动切换
*/
public static final int SWITCH_TIME_INVALID = 0;
private Context mCxt;
// 图片url集合
private List<String> mImgUrls;
// 图片切换时间
private int mSwitchTime;
//自动滚动的定时器
private Timer mTimer;
// 显示圆点指示器的布局
private LinearLayout mIndicatorLayout;
private ViewPager mViewPager;
private PagerAdapter mPagerAdapter;
private int currentIndex; // 当前页面,在0和getSize()直接
private int pagerCurrent;//在viewpager中,的当前页面,取值在0和Integer.MAX_VALUE之间
private boolean timeRunning;
/**
* @param context
* @param viewpager viewPager组件
* @param pagerAdapter
* @param adUrls 装有图片url的集合
* @param switchTime 图片切换时间(ms) {@link RecommendAdComponent#SWITCH_TIME_INVALID}:不自动切换
* @param indicatorLayout 显示圆点指示器的布局
*/
public RecommendAdComponent(Context context, ViewPager viewpager, PagerAdapter pagerAdapter, List<String> adUrls, int switchTime,
LinearLayout indicatorLayout) {
this.mCxt = context;
this.mViewPager = viewpager;
this.mPagerAdapter = pagerAdapter;
this.mImgUrls = adUrls;
this.mSwitchTime = switchTime;
this.mIndicatorLayout = indicatorLayout;
initIndicatorLayout();
mViewPager.setOnPageChangeListener(new MyOnPageChangeListener());
// setViewpagerAnimator();
}
/**
* 初始化指示器
*/
private void initIndicatorLayout() {
ImageView iv = null;
if (mIndicatorLayout != null && getSize() < 2) {
// 如果只有一第图时不显示圆点容器
mIndicatorLayout.setVisibility(View.INVISIBLE);
} else if (mIndicatorLayout != null) {
mIndicatorLayout.setVisibility(View.VISIBLE);
for (int i = 0; i < getSize(); i++) {
iv = new ImageView(mCxt);
iv.setTag(i);
int padding = mCxt.getResources().getDimensionPixelSize(R.dimen.indicator_padding);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
params.setMargins(padding, 0, padding, 0);
mIndicatorLayout.addView(iv, params);
}
}
}
private void resetIndicatorLayout() {
mIndicatorLayout.removeAllViews();
initIndicatorLayout();
}
/**
* 给viewPager设置动画
*/
private void setViewpagerAnimator(){
if (mViewPager !=null) {
PageTransformer pageTransformer=new PageTransformer() {
@Override
public void transformPage(View view, float arg1) {
// view.setAnimation(AnimationUtils.loadAnimation(mCxt, android.R.anim.slide_out_right));
view.setAnimation(AnimationUtils.loadAnimation(mCxt, R.anim.right_in));
}
};
mViewPager.setPageTransformer(true, pageTransformer);
}
}
/**
* 获取图片集合的size
*
* @return
*/
private int getSize() {
return (mImgUrls == null ? 0 : mImgUrls.size());
}
public boolean isEmpty() {
return (mImgUrls == null);
}
/**
* 开启组件
*/
public void startUpAdComponent() {
currentIndex = 0;
pagerCurrent = 0;
mViewPager.setAdapter(mPagerAdapter);
mViewPager.setCurrentItem(currentIndex);
updateIndicator(currentIndex);
startTimer();
}
/**
* 更新组件中adapter数据
*/
public void updateAdComponent() {
stopTimer();
resetIndicatorLayout();
mPagerAdapter.notifyDataSetChanged();
startUpAdComponent();
}
/**
* 在页面销毁的时候调用该方法
*/
public void stopAdComponent() {
stopTimer();
}
/**
* 停止自动滚动的任务
*/
public void stopTimer() {
timeRunning = false;
if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
}
/**
* 开始自动滚动的任务,注意,只有图片个数大于1的时候才会自动滚动
*/
public void startTimer() {
timeRunning = true;
if (mTimer == null && getSize() > 1 && mSwitchTime > 0) {
mTimer = new Timer();
mTimer.schedule(new PagerTimerTask(), mSwitchTime, mSwitchTime);
}
}
private class PagerTimerTask extends TimerTask {
@Override
public void run() {
currentIndex++;
pagerCurrent++;
mHandler.sendEmptyMessage(0);
}
}
private class MyOnPageChangeListener implements OnPageChangeListener {
@Override
public void onPageScrollStateChanged(int arg0) {
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageSelected(int position) {
pagerCurrent = position;
currentIndex = position % getSize();// 更新当前页面
updateIndicator(currentIndex);
}
}
/**
* 更新圆点指示器
*/
private void updateIndicator(int position) {
if (!isEmpty() && position < getSize()) {
if (getSize() > 1) {
resetAllIndicator(getSize());// 重置所有的指示器为为选择状态
View v = mIndicatorLayout.findViewWithTag(position);
if (v != null) {
v.setBackgroundResource(R.drawable.circle_indicator_selected);// 点亮
}
}
}
}
/**
* 重置所有的指示器
*/
private void resetAllIndicator(int size) {
if (mIndicatorLayout != null) {
for (int i = 0; i < size; i++) {
View v = mIndicatorLayout.findViewWithTag(i);
if (v != null) {
v.setBackgroundResource(R.drawable.circle_indicator_normal);
}
}
}
}
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
mViewPager.setCurrentItem(pagerCurrent);
};
};
}
当网络加载图片url列表成功之后,只需调用mAdComponent.updateAdComponent();即可
例如我的代码
private void updateAdSources(List<HomeBannerDTO> list) {
imageUrls.clear();
for (HomeBannerDTO dto : list) {
imageUrls.add(dto.getUrl());
}
mBannerList.clear();
mBannerList.addAll(list);
//更新banner组件
mAdComponent.updateAdComponent();
LogHelper.d(TAG, "updateAdComponent .....");
SettingsMgr.setLastRecommendAdList(mCxt, mBannerList);
}