首页 > 代码库 > Android无限广告轮播 - 自定义BannerView

Android无限广告轮播 - 自定义BannerView

1.概述


  这其实是我第一篇想写的博客,可能是因为我遇到了太多的坑,那个时候刚入行下了很多Demo发现怎么也改不动,可能是能力有限,这次就做一个具体的实现和彻底的封装。
  上次讲了Android无限广告轮播-ViewPager源码分析,有了源码分析我们对ViewPager就有了一个大概的了解,那么再来封装成自定义View,就会简单许多,附视频讲解地址:http://pan.baidu.com/s/1bpqqkGn
  
  技术分享

2.效果封装 


2.1 自定义BannerViewPager extends ViewPager:
  我们要利用Adapter设计模式,那么目前这个阶段,需要的方法就是根据PagerAdapter位置获取当前View,所以BannerAdapter里面就只需要一个方法那就是getView(int position);

/**
 * description:
 *      广告轮播的ViewPager
 * Created by 曾辉 on 2016/11/17.
 * QQ:240336124
 * Email: 240336124@qq.com
 * Version:1.0
 */
public class BannerViewPager extends ViewPager {

    private Context mContext;

    private BannerAdapter mAdapter;

    public BannerViewPager(Context context) {
        this(context, null);
    }

    public BannerViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
    }

    public void setAdapter(BannerAdapter adapter) {
        this.mAdapter = adapter;
        setAdapter(new BannerPagerAdapter());
    }

    private class BannerPagerAdapter extends PagerAdapter {

        @Override
        public int getCount() {
            // 返回一个很大的值,确保可以无限轮播
            return Integer.MAX_VALUE;
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            // 这么写就对了,看了源码应该就明白
            return view == object;
        }

        @Override
        public Object instantiateItem(ViewGroup container, final int position) {
            View bannerView = mAdapter.getView(position);
            container.addView(bannerView );
            return bannerView;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            // 销毁回调的方法  移除页面即可
            container.removeView((View) object);
        }
    }
}

  这样我们只要给他设置一个BannerAdapter就可以实现ViewPager的效果,可以手动切换,这里就先不看效果。
  
  
2.2. 实现自动轮播
  实现自动轮播比较简单,实现的方式有多种可以用定时器Timer、Handler发送消息、start Thread的行,这里我采用Handler发送消息的方法。

    // 2.发送延迟消息的MSG WHAT
    private final int ROLL_MSG = 0x0011;
    // 2.实现自动轮播 - Handler发送消息
    private Handler mRollHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // 切换到下一个页面
            setCurrentItem(getCurrentItem() + 1);
            // 循环执行
            startRoll();
        }
    };

    /**
     * 实现自动滚动
     */
    public void startRoll() {
        mRollHandler.removeMessages(ROLL_MSG);
        // 发送延迟的消息
        mRollHandler.sendEmptyMessageDelayed(ROLL_MSG, mCutDownTime);
    }

  我们看一下效果吧,但是发现Gif录制根本捕捉不到切换的效果,因为自动切换速度太快了,这里还是不贴效果了,下面我就需要改变切换的速度。
  
2.4. 改变切换速率

  如果看过上篇文章的源码就知道,我们会调用Scroller的mScroller.startScroll(sx, sy, dx, dy, duration)的这个方法,如果我们需要改变速率就只能改变duration执行切换页面动画的时间,可是我们根本拿不到这个值,那么就只能修改mScroller这个属性,可又发现他是private的有点头大,但是我们可以利用反射设置mScroller;

     // 自定义的继承子Scroller
     private BannerScroller mScroller;

     public BannerViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;

        try {
            // 3.改变切换页面动画的持续时间  利用反射获取mScroller属性
            Field field = ViewPager.class.getDeclaredField("mScroller");
            field.setAccessible(true);
            mScroller = new BannerScroller(context);
            // 给Scroller设置为我们自定义的Scroller类
            field.set(this, mScroller);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
    * 设置切换页面动画持续的时间
    **/
    public void setScrollerDuration(int scrollerDuration) {
        mScroller.setScrollDuration(scrollerDuration);
    }

  现在效果差不多了,可以看到能够无限轮播,能够自动轮播,并且页面切换的速度也可以了,接下来就只需要处理点的指示器和文字的描述:
  技术分享
 

  2.5. 自定义BannerView加入点指示和广告描述
  接下来我们又自定义一个BannerView里面包含当前自定义好的BannerViewPager和点的指示LinearLayout以及广告描述TextView。

 /**
 * description:
 *     广告轮播
 * Created by 曾辉 on 2016/11/17.
 * QQ:240336124
 * Email: 240336124@qq.com
 * Version:1.0
 */
public class BannerView extends RelativeLayout implements ViewPager.OnPageChangeListener {
    @ViewById(R.id.banner_desc_tv)
    private TextView mBannerDescTv;

    @ViewById(R.id.dots_ll)
    private LinearLayout mDotContainer;

    private BannerAdapter mAdapter;

    private Drawable mNormalDotDrawable, mFocusDotDrawable;

    private Context mContext;

    private int mCurrentPosition = 0;

    private int mDotLocation = 1;

    @ViewById(R.id.banner_vp)
    private BannerViewPager mViewPager;

    // 广告位的宽高比例
    private float mWidthProportion, mHeightProportion;

    private int mCalculateHeight = 0;

    public BannerView(Context context) {
        this(context, null);
    }

    public BannerView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BannerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;

        initAttribute(attrs);

        inflate(context, R.layout.ui_banner_layout, this);

        ViewUtils.inject(this);

    }

    private void initAttribute(AttributeSet attrs) {
        TypedArray array = mContext.obtainStyledAttributes(attrs, R.styleable.BannerView);
        mDotLocation = array.getInt(R.styleable.BannerView_dotLocation, mDotLocation);

        mFocusDotDrawable = array.getDrawable(R.styleable.BannerView_dotIndicatorFocus);

        if (mFocusDotDrawable == null) {
            mFocusDotDrawable = new ColorDrawable(Color.RED);
        }

        mNormalDotDrawable = array.getDrawable(R.styleable.BannerView_dotIndicatorNormal);

        if (mNormalDotDrawable == null) {
            mNormalDotDrawable = new ColorDrawable(Color.WHITE);
        }

        mWidthProportion = array.getFloat(R.styleable.BannerView_widthProportion, mWidthProportion);
        mHeightProportion = array.getFloat(R.styleable.BannerView_heightProportion, mHeightProportion);

        array.recycle();
    }

    public void setOnBannerItemClickListener(BannerViewPager.BannerItemClickListener listener) {
        mViewPager.setOnBannerItemClickListener(listener);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        if (mWidthProportion != 0 && mHeightProportion != 0 && mCalculateHeight == 0) {
            int width = getMeasuredWidth();

            if (mHeightProportion == 0 || mWidthProportion == 0) {
                return;
            }

            mCalculateHeight = (int) (width * mHeightProportion / mWidthProportion);

            setMeasuredDimension(width, mCalculateHeight);

            mViewPager.getLayoutParams().height = mCalculateHeight;
        }
    }

    public void setAdapter(BannerAdapter adapter) {
        this.mAdapter = adapter;

        mViewPager.setAdapter(adapter);
        initDotIndicator(mAdapter.getCount());

        mViewPager.addOnPageChangeListener(this);

        String textDesc = mAdapter.getBannerDesc(0);
        if (!TextUtils.isEmpty(textDesc)) {
            mBannerDescTv.setText(textDesc);
        }
    }

    private void initDotIndicator(int count) {
        mDotContainer.setGravity(getDotGravity());
        for (int i = 0; i < count; i++) {
            DotIndicatorView indicatorView = new DotIndicatorView(mContext);

            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(dip2px(8), dip2px(8));
            params.leftMargin = params.rightMargin = dip2px(2);
            indicatorView.setLayoutParams(params);

            if (i == mCurrentPosition) {
                indicatorView.setDrawable(mFocusDotDrawable);
            } else {
                indicatorView.setDrawable(mNormalDotDrawable);
            }

            mDotContainer.addView(indicatorView);
        }
    }

    private int dip2px(int dip) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                dip, getResources().getDisplayMetrics());
    }

    public void startRoll() {
        mViewPager.startRoll();
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {
        DotIndicatorView oldIndicatorView = (DotIndicatorView) mDotContainer.getChildAt(mCurrentPosition);
        oldIndicatorView.setDrawable(mNormalDotDrawable);

        mCurrentPosition = position % mAdapter.getCount();
        DotIndicatorView currentIndicatorView = (DotIndicatorView) mDotContainer.getChildAt(mCurrentPosition);
        currentIndicatorView.setDrawable(mFocusDotDrawable);

        mBannerDescTv.setText(mAdapter.getBannerDesc(mCurrentPosition));
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }

    public int getDotGravity() {
        switch (mDotLocation) {
            case -1:
                return Gravity.LEFT;
            case 0:
                return Gravity.CENTER;
            case 1:
                return Gravity.RIGHT;
        }
        return Gravity.LEFT;
    }
}

 
技术分享
  
  如果实在还是看不太懂,可以看一下我录的频,也可以了解一下整个项目的其他东西:http://pan.baidu.com/s/1bpqqkGn 。
  
附代码地址:
http://download.csdn.net/detail/z240336124/9683704

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    Android无限广告轮播 - 自定义BannerView