如上图所示效果我们应该都不陌生, 这是一个简单的 banner 轮播效果, 网上也有很多的开源项目, 但有时候可能我们仅仅只需要一些简单的效果, 并不需要其他过多的东西. 这里简单的对 banner 进行一下封装, 随时调用和添加一些新的功能.
代码展示
调用方式
/**
* Created by xiaolong on 2018/1/23.
*/
public class BannerView extends FrameLayout implements ViewPager.OnPageChangeListener {
private ViewPager viewPager;
// 网络图片地址
private List < String > imageUrls;
// 指示点的容器
private LinearLayout pointLayout;
// 当前页面位置
private int currentItem;
// 自动播放时间
private int autoPlayTime = 2000;
// 是否自动播放
private boolean isAutoPlay;
// 是否是一张图片
private boolean isOneImage;
// 监听事件
private OnBannerItemClick onBannerItemClick;
// 这里利用 handler 实现循环播放
private Handler handler = new Handler(new Handler.Callback() {@Override public boolean handleMessage(Message msg) {
currentItem++;
currentItem = currentItem % (imageUrls.size() + 2);
viewPager.setCurrentItem(currentItem);
handler.sendEmptyMessageDelayed(0, autoPlayTime);
return false;
}
});
public BannerView(@NonNull Context context) {
this(context, null);
}
public BannerView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BannerView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.BannerView, 0, 0);
// 默认自动播放
isAutoPlay = typedArray.getBoolean(R.styleable.BannerView_isAutoPlay, true);
typedArray.recycle();
viewPager = new ViewPager(getContext());
pointLayout = new LinearLayout(getContext());
// 添加监听事件
viewPager.addOnPageChangeListener(this);
// 利用布局属性将指示器容器放置底部并居中
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
params.bottomMargin = 60;
addView(viewPager);
addView(pointLayout, params);
}@Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}@Override public void onPageSelected(int position) {
currentItem = position;
if (!isOneImage) {
switchToPoint(toRealPosition(position));
}
}@Override public void onPageScrollStateChanged(int state) {
// 根据滑动松开后的状态, 去判断当前的 current 并跳转到指定 current
if (state == ViewPager.SCROLL_STATE_IDLE) {
int current = viewPager.getCurrentItem();
int lastReal = viewPager.getAdapter().getCount() - 2;
if (current == 0) {
viewPager.setCurrentItem(lastReal, false);
} else if (current == lastReal + 1) {
viewPager.setCurrentItem(1, false);
}
}
}
// 配置 viewpager 适配器
private class BannerAdapter extends PagerAdapter {@Override public int getCount() {
return imageUrls.size() + 2;
}@Override public boolean isViewFromObject(View view, Object object) {
return view == object;
}@Override public Object instantiateItem(ViewGroup container, final int position) {
ImageView imageView = new ImageView(getContext());
imageView.setOnClickListener(new OnClickListener() {@Override public void onClick(View v) {
if (onBannerItemClick != null) {
onBannerItemClick.onItemClick(toRealPosition(position));
}
}
});
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
Glide.with(getContext()).load(imageUrls.get(toRealPosition(position))).into(imageView);
container.addView(imageView);
return imageView;
}@Override public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
// 添加网络图片
public void setImageUrls(List < String > imageUrls) {
this.imageUrls = imageUrls;
if (imageUrls.size() <= 1) {
isOneImage = true;
} else {
isOneImage = false;
}
initViewPager();
}
// 加载 viewPager
private void initViewPager() {
if (!isOneImage) {
// 添加指示点
addPoints();
}
BannerAdapter adapter = new BannerAdapter();
viewPager.setAdapter(adapter);
// 默认当前图片
viewPager.setCurrentItem(1);
// 判断是否自动播放和是否是一张图片的情况
if (isAutoPlay && !isOneImage) {
handler.sendEmptyMessageDelayed(0, autoPlayTime);
}
}
// 添加指示点
private void addPoints() {
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lp.setMargins(10, 10, 10, 10);
ImageView imageView;
int length = imageUrls.size();
for (int i = 0; i < length; i++) {
imageView = new ImageView(getContext());
imageView.setLayoutParams(lp);
imageView.setImageResource(R.drawable.select_point_bg);
pointLayout.addView(imageView);
}
switchToPoint(0);
}
// 切换指示器
private void switchToPoint(int currentPoint) {
for (int i = 0; i < pointLayout.getChildCount(); i++) {
pointLayout.getChildAt(i).setEnabled(false);
}
pointLayout.getChildAt(currentPoint).setEnabled(true);
}
// 返回真实的位置
private int toRealPosition(int position) {
int realPosition;
if (imageUrls.size() > 0) {
realPosition = (position - 1) % imageUrls.size();
if (realPosition < 0) realPosition += imageUrls.size();
} else {
realPosition = 0;
}
return realPosition;
}
public void setAutoPlay(boolean autoPlay) {
isAutoPlay = autoPlay;
}
public void setOnBannerItemClick(OnBannerItemClick onBannerItemClick) {
this.onBannerItemClick = onBannerItemClick;
}
// 添加监听事件回调
public interface OnBannerItemClick {
void onItemClick(int position);
}
}
代码分析
// 设置是否自动播放
bannerView.setAutoPlay(true);
// 添加网络图片
bannerView.setImageUrls(data);
//banner 的点击事件
bannerView.setOnBannerItemClick(new BannerView.OnBannerItemClick() {
@Override
public void onItemClick(int position) {
Toast.makeText(MainActivity.this,"图片" + position,Toast.LENGTH_LONG).show();
}
});
这里我只考虑了使用网络图片进行展示, 使用本地图片原理是一样的, 在里面多加个方法即可. 此效果代码不是很复杂, 通俗易懂. 唯一值得一说的是我在实现 viewpager 无限滑动时采用的是
return imageUrls.size() + 2
的方法. 这里我们添加两条数据分别为第一条和最后一条. 第一条代表着最后一张图片而最后一条则代表着第一张图片, 这里手动画个图:
/**
* Created by xiaolong on 2018/1/23.
*/banner_theory.JPG
通过此图我们应该不难发现, 当我们滑动到第二个 0 的时候则跳转到第一个 0, 滑动到第一个 2 的时候则跳转到第二个 2. 这里 0 代表第一张图片, 2 代表第二张图片. 这样就可以实现无限滑动的效果了, 可能有些人会觉得这样做有些复杂还要判断真实的位置不如直接将 getConut() 返回一个最大值然后取其中间值即可. 但这样做其实代码性能并不是很高.
结束
这样一个常用的 banner 就写好了, 当我们需要实现一些滑动动画的时候, 可以在里面实现 viewpager 的动画属性即可. 先从简单的入手, 慢慢的就可以了解更多的原理和知识点, 哈哈! 这里贴出自己的 Demo 源码以便参考.
来源: http://www.jianshu.com/p/3c0bfa883206