Android实现Banner轮播效果

一、动态布局

纯粹为了保持代码风格的一致性,也可以用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<Banner> 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<Banner> 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<Dot> 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<Banner> banners) {
	mBannerViewAdapter.setBanner(banners);
	((BannerView) ((LinearLayout) createBannerView()).getChildAt(0)).setBannerViewAdapter(mBannerViewAdapter);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值