最近在做开发时,发现一个问题:凡是涉及广告banner的地方都是一份代码拷贝来拷贝去,完全未封装,将所有的实现都暴露在activity和fragment中。这样子导致以后想在某个页面引入一个banner非常的繁琐麻烦,且不利于代码的维护。而且产品又特别喜欢在页面上加banner。最近时间较为充裕,我就尝试了一下把banner封装成一个自定义控件,供以后在项目中使用。
banner封装的原理已经不新鲜了,我把一些要点列在下面:
1.handler.postdelay形成循环(也有其他方案)。
2.因为viewpager忽然从17个位置切换回第0个位置效果非常糟糕,因此需要使用一些假象来解决这个问题:让viewpager很大(图片0,图片1,图片2,图片0,图片1,图片2,图片0,图片1,图片2,图片0,图片1,图片2…….)这样循环,初始的时候在中间(且是从图片0位置开始的),底部的指示器(点0,点1,点2)只有三个,底部循环变动,viewpager不断向后滑动(不循环,除非到了最大的位置)。
3.为解决手指放上去的时候,viewpager不自动滑动,需要拦截监听事件。
代码:
public class BannerView extends RelativeLayout {
private ViewPager mViewPager;
private LinearLayout mLinearLayout;
private Context mContext;
private ImageView[] mIndicator;
private Handler mHandler = new Handler();
final List mList = new ArrayList<>();
private OnBannerItemClickListener mOnBannerItemClickListener;
private Runnable mRunnable = new Runnable() {
@Override
public void run() {
mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1);
mHandler.postDelayed(mRunnable, 5000);
}
};
private int mItemCount;
public interface OnBannerItemClickListener {
void onClick(int position);
}
public BannerView(Context context) {
this(context, null);
}
public BannerView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
init();
}
private void init() {
View.inflate(mContext, R.layout.view_bannerview, this);
// 取到布局中的控件
mViewPager = (ViewPager) findViewById(R.id.viewpager);
mLinearLayout = (LinearLayout) findViewById(R.id.ll_points);
}
/**
* 给banner中的viewpager设置数据
*
* @param list
*/
public void setList(List list) {
if (mList.size() == 0) {
mList.addAll(list);
mItemCount = mList.size();
initView();
}
}
/**
* banner item的点击监听
*
* @param onBannerItemClickListener
*/
public void setOnBannerItemClickListener(OnBannerItemClickListener onBannerItemClickListener) {
mOnBannerItemClickListener = onBannerItemClickListener;
}
private void initView() {
// 给viewpager设置adapter
BannerPagerAdapter bannerPagerAdapter = new BannerPagerAdapter(mList, mContext);
mViewPager.setAdapter(bannerPagerAdapter);
// 初始化底部点指示器
initIndicator(mList, mContext);
mViewPager.setCurrentItem(500 * mItemCount);
// 给viewpager设置滑动监听
mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
switchIndicator(position % mItemCount);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
cancelRecycle();
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
startRecycle();
break;
}
return super.dispatchTouchEvent(ev);
}
private void initIndicator(List list, Context context) {
mIndicator = new ImageView[mItemCount];
for (int i = 0; i < mIndicator.length; i++) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(12, 12);
params.setMargins(6, 0, 6, 0);
ImageView imageView = new ImageView(context);
mIndicator[i] = imageView;
if (i == 0) {
mIndicator[i].setBackgroundResource(R.drawable.ptt_banner_dian_focus);
} else {
mIndicator[i].setBackgroundResource(R.drawable.ptt_banner_dian_white);
}
mLinearLayout.addView(imageView, params);
}
if (mItemCount == 1) {
mLinearLayout.setVisibility(View.GONE);
} else {
mLinearLayout.setVisibility(View.VISIBLE);
}
}
private void switchIndicator(int selectItems) {
for (int i = 0; i < mIndicator.length; i++) {
if (i == selectItems) {
mIndicator[i].setBackgroundResource(R.drawable.ptt_banner_dian_focus);
} else {
mIndicator[i].setBackgroundResource(R.drawable.ptt_banner_dian_white);
}
}
}
private void startRecycle() {
mHandler.postDelayed(mRunnable, 5000);
}
private void cancelRecycle() {
mHandler.removeCallbacks(mRunnable);
}
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
if (visibility == VISIBLE) {
startRecycle();
} else {
cancelRecycle();
}
}
private class BannerPagerAdapter extends PagerAdapter {
private List imagesUrl;
private Context context;
public BannerPagerAdapter(List imagesUrl, Context context) {
this.imagesUrl = imagesUrl;
this.context = context;
}
@Override
public int getCount() {
return mItemCount == 1 ? 1 : Integer.MAX_VALUE;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, final int position) {
View ret = null;
ImageView imageView = new ImageView(context);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
// 联网取图片,根据自己的情况修改
xxxxxxxxxxxxxxxxxxx
ret = imageView;
container.addView(ret);
ret.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mOnBannerItemClickListener.onClick(position);
}
});
return ret;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
}
布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" >
android.support.v4.view.ViewPager>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/viewpager"
android:background="#33000000"
android:orientation="vertical"
android:padding="5dip" >
<LinearLayout
android:id="@+id/ll_points"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dip"
android:layout_gravity="center_horizontal"
android:orientation="horizontal" >
LinearLayout>
LinearLayout>
RelativeLayout>
应用的时候直接作为自定义控件引入布局中,在代码中只需要setList,同时也可以对每个item添加点击事件,根据positon决定跳转到哪个位置。
bannerview.setOnBannerItemClickListener。