Android实现Banner轮播效果
16lz
2021-01-23
一、动态布局
纯粹为了保持代码风格的一致性,也可以用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);}
更多相关文章
- android 删除SD卡或者手机的缓存图片和目录
- android 视频图片混合轮播实现
- android用ImageView显示网络图片
- android通过BitmapFactory.decodeFile获取图片bitmap报内存溢出
- android 运用AsyncTask 获取图片并显示
- android中网络图片的显示
- Android 图片相关
- android 图片点击一下就放大到全屏,再点一下就回到原界面
- Android图片压缩、加水印