一、动态布局
纯粹为了保持代码风格的一致性,也可以用xml布局来实现。
private View createBannerView() {
LinearLayout bannerLayout = new LinearLayout(mContext);
bannerLayout.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
bannerLayout.setOrientation(LinearLayout.VERTICAL);
bannerLayout.setGravity(Gravity.CENTER);
BannerView bannerView = new BannerView(mContext);
bannerView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, DimenUtils.dip2px(mContext, 160)));
bannerLayout.addView(bannerView);
return bannerLayout;
}
二、实体Bean
存储Banner的相关信息:图片名、图片Url、图片TargetUrl。
public class Banner {
private String mName; // 图片名
private String mImgUrl; // 图片下载url
private String mTargetUrl; // 点击图片跳转url
public Banner(String name, String imgUrl, String targetUrl) {
this.mName = name;
this.mImgUrl = imgUrl;
this.mTargetUrl = targetUrl;
}
public String getName() {
return mName;
}
public String getImgUrl() {
return mImgUrl;
}
public String getTargetUrl() {
return mTargetUrl;
}
}
三、适配器PagerAdapter
没有实现Banner的左右循环效果,感觉意义不大。
public class BannerViewAdapter extends PagerAdapter {
private Context mContext;
private ArrayList mBanners;
public BannerViewAdapter(Context context) {
this.mContext = context;
}
@Override
public int getCount() {
return mBanners == null ? 0 : mBanners.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
/**
* @param container
* @param position
* @return 对position进行求模操作
* 因为当用户向左滑时position可能出现负值,所以必须进行处理
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
// 对Viewpager页号求模去除View列表中要显示的项
position %= mBanners.size();
if (position < 0) {
position = mBanners.size() + position;
}
ImageView view = new ImageView(mContext);
view.setScaleType(ImageView.ScaleType.FIT_XY);
Picasso.with(mContext).load(mBanners.get(position).getImgUrl()).placeholder(R.drawable.default_banner).error(R.drawable.default_banner).transform(new PicassoTransformation()).into(view);
view.setTag(position);
final int index = position;
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!NetworkManager.getInstance().isNetworkConnected()) {
Toast.makeText(mContext, "网络连接异常,请检查网络", Toast.LENGTH_SHORT).show();
} else {
// TODO mBanners.get(index).getTargetUrl()
}
}
});
// 如果View已经在之前添加到了一个父组件,则必须先remove,否则会抛出IllegalStateException。
ViewParent viewParent = view.getParent();
if (viewParent != null) {
ViewGroup parent = (ViewGroup) viewParent;
parent.removeView(view);
}
container.addView(view);
return view;
}
/**
* 由于我们在instantiateItem()方法中已经处理了remove的逻辑,
* 因此这里并不需要处理。实际上,实验表明这里如果加上了remove的调用,
* 则会出现ViewPager的内容为空的情况。
*
* @param container
* @param position
* @param object
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// 警告:不要在这里调用removeView
}
public void setBanner(ArrayList banners) {
this.mBanners = banners;
notifyDataSetChanged();
}
}
由于使用了Picasso异步加载图片,所以需要实现Transformation接口。
public class PicassoTransformation implements Transformation {
@Override
public Bitmap transform(Bitmap bitmap) {
return bitmap;
}
@Override
public String key() {
return null;
}
}
四、BannerView控件
自定义带圆点指示器的Banner控件,实现自动轮播、拖拽暂停、手动滑动效果。
public class BannerView extends RelativeLayout implements ViewPager.OnPageChangeListener {
private static final int UPDATE_BANNER = 0x100;
private Context mContext;
private ViewPager mViewPager;
private LinearLayout mIndicatorLayout;
private boolean isPauseRun;
private int mAutoCurrIndex;
private ArrayList mDots;
private BannerViewAdapter mBannerViewAdapter;
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_BANNER:
if (msg.arg1 != 0) {
mViewPager.setCurrentItem(msg.arg1);
} else {
// false: 当从末页跳到首页时,不显示翻页动画效果
mViewPager.setCurrentItem(msg.arg1, false);
}
break;
default:
break;
}
}
};
public BannerView(Context context) {
super(context);
this.mContext = context;
}
public void setBannerViewAdapter(BannerViewAdapter bannerViewAdapter) {
this.mBannerViewAdapter = bannerViewAdapter;
initView();
bindCarouselTask();
}
private void initView() {
// 动态添加ViewPager
mViewPager = new ViewPager(mContext);
mViewPager.setAdapter(mBannerViewAdapter);
mViewPager.addOnPageChangeListener(this);
addView(mViewPager);
// 动态添加一个LinearLayout
mIndicatorLayout = new LinearLayout(mContext);
RelativeLayout.LayoutParams RelativeParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
RelativeParams.setMargins(DimenUtils.dip2px(mContext, 5), DimenUtils.dip2px(mContext, 5), DimenUtils.dip2px(mContext, 5), DimenUtils.dip2px(mContext, 5));
RelativeParams.addRule(ALIGN_PARENT_BOTTOM);
RelativeParams.addRule(CENTER_HORIZONTAL);
addView(mIndicatorLayout, RelativeParams);
// 动态添加指示器
LinearLayout.LayoutParams LinearParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
LinearParams.setMargins(DimenUtils.dip2px(mContext, 2), DimenUtils.dip2px(mContext, 2), DimenUtils.dip2px(mContext, 2), DimenUtils.dip2px(mContext, 2));
mDots = new ArrayList<>();
int adapterSize = mBannerViewAdapter.getCount();
for (int i = 0; i < adapterSize; i++) {
Dot dot = new Dot(mContext);
mDots.add(dot);
mIndicatorLayout.addView(dot, LinearParams);
}
refreshDots(0);
}
/**
* 自动轮播图片,5s后第一次执行,周期是5s
*/
private void bindCarouselTask() {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
if (!isPauseRun) {
Message message = new Message();
message.what = UPDATE_BANNER;
if (mAutoCurrIndex == mBannerViewAdapter.getCount() - 1) {
mAutoCurrIndex = -1;
}
message.arg1 = mAutoCurrIndex + 1;
mHandler.sendMessage(message);
}
}
}, 5000, 5000);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
int adapterSize = mBannerViewAdapter.getCount();
if (adapterSize != 0) {
int positionInData = position % adapterSize;
refreshDots(positionInData);
}
// 设置当前图片的index
mAutoCurrIndex = position;
}
@Override
public void onPageScrollStateChanged(int state) {
switch (state) {
// 静止状态
case ViewPager.SCROLL_STATE_IDLE:
isPauseRun = false;
break;
// 拖拽中
case ViewPager.SCROLL_STATE_DRAGGING:
isPauseRun = true;
break;
// 拖拽后松手,自动回到最终位置的过程
case ViewPager.SCROLL_STATE_SETTLING:
isPauseRun = false;
break;
}
}
private void refreshDots(int positionInData) {
for (Dot dot : mDots) {
dot.setDotColor(Constants.UNSELECTED_DOT_COLOR);
}
mDots.get(positionInData).setDotColor(Constants.SELECTED_DOT_COLOR);
}
/**
* 指示当前viewpager处于哪个页面
*/
private class Dot extends View {
private int mDotColor;
private int mDotSize; // 指示器的圆点大小, 单位:dp
private Paint mDotPaint;
public Dot(Context context) {
super(context);
mDotColor = Constants.UNSELECTED_DOT_COLOR;
mDotSize = DimenUtils.dip2px(context, 5);
mDotPaint = new Paint();
mDotPaint.setAntiAlias(true);
mDotPaint.setDither(true);
mDotPaint.setStyle(Paint.Style.FILL);
}
@Override
protected void onDraw(Canvas canvas) {
mDotPaint.setColor(mDotColor);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, mDotPaint);
}
/**
* 将dot的宽度和高度定死
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
widthMeasureSpec = MeasureSpec.makeMeasureSpec(mDotSize, MeasureSpec.EXACTLY);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(mDotSize, MeasureSpec.EXACTLY);
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
/**
* 修改dot的颜色, 并且让整个dot重绘
*/
public void setDotColor(int color) {
mDotColor = color;
invalidate();
}
}
}
五、触发生效
public void setBannerViewDatas(ArrayList banners) {
mBannerViewAdapter.setBanner(banners);
((BannerView) ((LinearLayout) createBannerView()).getChildAt(0)).setBannerViewAdapter(mBannerViewAdapter);
}