Android广告轮播

Android广告轮播

广告轮播功能在很多项目中多用得到,最近看了一位大神的博客,仿照他的文章,自己来写一个广告轮播功能,写的过程中学到了很多东西,谢谢。

首先来看下核心的类BannerViewPager:

public class BannerViewPager extends ViewPager {

    private String TAG="BannerViewPager";
    Handler mHandler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            setCurrentItem(getCurrentItem()+1);
            startRoll();
        }
    };
    //消息
    private static final int SCROLL_MESSAGE_WHAT=0X0005;
    //轮播间隔
    private int SCROLL_INTERVAL_TIME=3500;

    //adapter
    private BannerAdapter mBannerAdapter;

    private Context mContext;

    //界面复用
    private List<View> mConvertViews;


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

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

        try {
            //利用发射,改变滑动时间
            BannerScroller mBannerScroller=new BannerScroller(context);
            Field field =
                    ViewPager.class.getDeclaredField("mScroller");

            if(!field.isAccessible())
                field.setAccessible(true);
            field.set(this,mBannerScroller);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //初始化界面复用列表
        mConvertViews=new ArrayList<>();

        //注册生命周期处理
        getActivity().getApplication().registerActivityLifecycleCallbacks(lifecycleCallbacks);
    }


    public Activity getActivity(){
        return (Activity) mContext;
    }


    /**
     * 开始轮播
     */
    public void startRoll(){
        mHandler.removeMessages(SCROLL_MESSAGE_WHAT);
        mHandler.sendEmptyMessageDelayed(SCROLL_MESSAGE_WHAT,SCROLL_INTERVAL_TIME);
        Log.e(TAG,"====>startRoll");
    }

    //销毁handler的发送
    @Override
    protected void onDetachedFromWindow() {
        //销毁handler的发送,防止内存泄漏
        mHandler.removeMessages(SCROLL_MESSAGE_WHAT);
        mHandler=null;
        //取消注册生命周期处理
        getActivity().getApplication().unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
        super.onDetachedFromWindow();
    }

    public void setAdapter(BannerAdapter adapter) {
        this.mBannerAdapter=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, int position) {
            if(mBannerAdapter.getCount()<=0){
                return null;
            }

            View view=mBannerAdapter.getView(position % mBannerAdapter.getCount(),getConvertView());
            container.addView(view);
            return view;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
            mConvertViews.add((View) object);
        }
    }

    /**
     * 获取复用界面
     * @return
     */
    private View getConvertView(){
        if(mConvertViews==null){
            return null;
        }
        for (View view : mConvertViews) {
            if(view.getParent()==null){
                return view;
            }

        }
        return  null;
    }

    /**
     * activty生命周期
     */
    Application.ActivityLifecycleCallbacks lifecycleCallbacks=new SimpleActivityLifecycleCallbacks(){
        @Override
        public void onActivityResumed(Activity activity) {
            super.onActivityResumed(activity);
            Log.e(TAG,"====>onActivityResumed");
            if(activity == getActivity()){
                mHandler.sendEmptyMessageDelayed(SCROLL_MESSAGE_WHAT,SCROLL_INTERVAL_TIME);
            }
        }

        @Override
        public void onActivityPaused(Activity activity) {
            super.onActivityPaused(activity);
            Log.e(TAG,"====>onActivityPaused");
            if(activity == getActivity()){
                mHandler.removeMessages(SCROLL_MESSAGE_WHAT);
            }
        }
    };


}

上面代码要主要几点:

1.handler 的处理,在onDetachedFromWindow中消耗handler,防止内存泄露

2.onActivityResumed和onActivityPaused中对轮播进行开启和停止,防止切换到别的页面,当前页还在继续播放,减少系统开销

3.反射改变mScroller对象,修改滑动时间

4.和listView相同,使用了view复用,减少系统开销

自定义适配器:

public abstract class BannerAdapter {

    /**
     * 创建BannerViewPager 的view
     * @param position
     * @param convertView  复用View
     * @return
     */
    public abstract View getView(int position, View convertView);

    /**
     * 播放条数
     * @return
     */
    public abstract int getCount();

    /**
     * 广告位
     * @param position
     * @return
     */
    public String getBannerDesc(int position) {
        return "";
    }
}

重写Scroller的startScroll方法,因为viewpager就是使用这个方法来进行滚动的

public class BannerScroller extends Scroller {

    private int mBannerDuration=850;

    public void setBannerDuration(int mBannerDuration) {
        this.mBannerDuration = mBannerDuration;
    }

    public BannerScroller(Context context) {
        super(context);
    }

    public BannerScroller(Context context, Interpolator interpolator) {
        super(context, interpolator);
    }

    @Override
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {

        super.startScroll(startX, startY, dx, dy, mBannerDuration);
    }
}

对BinnerViewPager 再次重装,添加了广告位和轮播点

public class BannerView extends RelativeLayout {

    //banner viewpager
    private BannerViewPager mBannerViewPager;
    //点指示器容器
    private LinearLayout mDotContainer;
    //适配器
    private BannerAdapter mAdapter=null;

    private Context mContext;
    //正常点的drawable
    private Drawable mIndicatorNormalDrawable;
    //被点击点的drawable
    private Drawable mIndicatorFocusDrawable;

    //当前选中指示器位置
    private int mCurrentPostion=0;
    private TextView mDescView;
    //点布局位置
    int mDotGravity=1;
    //点大小
    int mDotSize=8;
    //点之间的距离
    int mDotDistance=2;
    //宽高比
    float mWidthProportion=0f;
    float mHeightProportion=0f;

    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;
        inflate(context, R.layout.ui_bannerview_layout,this);

        init();


        initAttribute(attrs);

    }


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

        // 获取点的位置
        mDotGravity = array.getInt(R.styleable.BannerView_dotGravity, mDotGravity);

        // 获取点的颜色(默认、选中)
        mIndicatorFocusDrawable = array.getDrawable(R.styleable.BannerView_dotIndicatorFocus);
        if(mIndicatorFocusDrawable == null){
            //如果在布局文件中没有配置点的颜色  有一个默认值
            mIndicatorFocusDrawable = new ColorDrawable(Color.RED);
         }

         mIndicatorNormalDrawable = array.getDrawable(R.styleable.BannerView_dotIndicatorNormal);
         if(mIndicatorNormalDrawable == null){
             //如果在布局文件中没有配置点的颜色  有一个默认值
             mIndicatorNormalDrawable = new ColorDrawable(Color.WHITE);
         }

         //获取点的大小和距离
         mDotSize = (int) array.getDimension(R.styleable.BannerView_dotSize,dip2px(mDotSize));
         mDotDistance = (int) array.getDimension(R.styleable.BannerView_dotDistance,dip2px(mDotDistance));        // 获取底部的颜色        mBottomColor = array.getColor(R.styleable.BannerView_bottomColor,mBottomColor);


        //获取宽高比例
         mWidthProportion = array.getFloat(R.styleable.BannerView_withProportion,mWidthProportion);
         mHeightProportion = array.getFloat(R.styleable.BannerView_heightProportion,mHeightProportion);
        array.recycle();
    }

    private void init() {
        mBannerViewPager = (BannerViewPager) findViewById(R.id.banner_viewpager);
        mDotContainer = (LinearLayout) findViewById(R.id.dot_container);
        mDescView = (TextView) findViewById(R.id.tv_desc);
    }

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

        if(mHeightProportion != 0 && mWidthProportion != 0){
            int width = MeasureSpec.getSize(widthMeasureSpec);
            int height = (int) (width*mHeightProportion/mWidthProportion);
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
        }


        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    /**
     * 设置适配器
     * @param adapter
     */
    public void setAdapter(final BannerAdapter adapter){
        this.mAdapter=adapter;

        mBannerViewPager.setAdapter(adapter);

        initDotIndicator();
        initDesc();
        mBannerViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener(){
            @Override
            public void onPageSelected(int position) {
                pageSelected(position % adapter.getCount());
            }
        });
    }

    /**
     * 初始化广告位置
     */
    private void initDesc() {
        String desc = mAdapter.getBannerDesc(0);
        if(!TextUtils.isEmpty(desc)) {
            mDescView.setText(desc);
        }else {
            mDescView.setText("");
        }
    }

    /**
     * 轮播页切换
     * @param position
     */
    private void pageSelected(int position) {
        int oldPostion=mCurrentPostion;
        DotIndicatorView oldView = (DotIndicatorView) mDotContainer.getChildAt(oldPostion);
        oldView.setDrawable(mIndicatorNormalDrawable);

        DotIndicatorView curView = (DotIndicatorView) mDotContainer.getChildAt(position);
        curView.setDrawable(mIndicatorFocusDrawable);

        mCurrentPostion=position;

        //设置描述
        String desc = mAdapter.getBannerDesc(position);
        if(!TextUtils.isEmpty(desc)) {
            mDescView.setText(desc);
        }else {
            mDescView.setText("");
        }
    }

    /**
     * 初始化指示器
     */
    private void initDotIndicator() {
        mDotContainer.setGravity(obtainGravity());

        int count = mAdapter.getCount();
        for (int i = 0; i < count; i++) {
            DotIndicatorView dotView = new DotIndicatorView(mContext);

            LinearLayout.LayoutParams layoutParams=new LinearLayout.LayoutParams(mDotSize,mDotSize);
            layoutParams.leftMargin=layoutParams.rightMargin=mDotDistance;
            dotView.setLayoutParams(layoutParams);
            mCurrentPostion=0;
            if(i==0) {
                dotView.setDrawable(mIndicatorFocusDrawable);
            }else {
                dotView.setDrawable(mIndicatorNormalDrawable);
            }
            mDotContainer.addView(dotView);
        }
    }

    private int obtainGravity(){
        switch (mDotGravity){
            case 0:
                return Gravity.CENTER;

            case -1:
                return Gravity.LEFT;
            default:
                return Gravity.RIGHT;
        }
    }

    /**
     * dip 2 px
     * @param dip
     * @return
     */
    private int dip2px(int dip) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dip,getResources().getDisplayMetrics());
    }

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

}
注意点:根据给定的宽高比,对当前高度进行测量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    if(mHeightProportion != 0 && mWidthProportion != 0){
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = (int) (width*mHeightProportion/mWidthProportion);
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
    }


    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

轮播点的绘制

public class DotIndicatorView extends View {


    private Drawable drawable;

    public DotIndicatorView(Context context) {
        super(context);
    }

    public DotIndicatorView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public DotIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }



    @Override
    protected void onDraw(Canvas canvas) {

        Bitmap bitmap = drawable2Bitmap(drawable);

        Bitmap circleBitmap= getCircleBitmap(bitmap);

        canvas.drawBitmap(circleBitmap,0,0,null);
    }

    private Bitmap getCircleBitmap(Bitmap bitmap) {
        Bitmap bm = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);

        Canvas canvas=new Canvas(bm);
        Paint paint=new Paint();
        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setDither(true);


        canvas.drawCircle(getMeasuredWidth()/2,getMeasuredHeight()/2,getMeasuredWidth()/2,paint);

        //取交集,且是下面的颜色
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap,0,0,paint);

        return bm;
    }

    /**
     * drawble  转  bitmap
     * @param drawable
     * @return
     */
    private Bitmap drawable2Bitmap(Drawable drawable){
        if(drawable instanceof BitmapDrawable){
            return ((BitmapDrawable)drawable).getBitmap();
        }

        Bitmap bitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);

        Canvas canvas=new Canvas(bitmap);
        drawable.setBounds(0,0,getMeasuredWidth(),getMeasuredHeight());
        drawable.draw(canvas);
        return bitmap;
    }

    public void setDrawable(Drawable drawable) {
        this.drawable = drawable;
        invalidate();
    }
}   

将用户给的drawable(colordrawable ,bitmapdrawable,其他)绘制到画布上。

下面,我们来看使用:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical">
    <github.com.bannerviewlibrary.BannerView
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/banner_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:withProportion="8"
        app:heightProportion="4"
        app:dotSize="8dp"
        app:dotDistance="3dp"
        app:dotGravity="right"
        app:bottomColor="@color/banner_bottom_bar_bg_day"
        app:dotIndicatorFocus="@color/dot_select_color"
        app:dotIndicatorNormal="@color/dot_unselect_color">

    </github.com.bannerviewlibrary.BannerView>

    <Button
        android:id="@+id/btn_jump"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="跳转"/>

</LinearLayout>

mainactivity中:

int[] mData=new int[]{R.mipmap.banner_default,R.mipmap.splash_slogan};
String[] mDesc=new String[]{"哈哈哈哈哈哈哈","呵呵呵呵呵呵呵呵呵呵呵"};
private void initData() {
    mBannerView.setAdapter(new BannerAdapter() {
        @Override
        public View getView(int position, View convertView) {
            ImageView mImageView=null;
            if(convertView==null){
                mImageView=new ImageView(mActivity);
            }else {
                mImageView= (ImageView) convertView;
            }

            mImageView.setImageResource(mData[position]);
            return mImageView;
        }

        @Override
        public int getCount() {
            return mData.length;
        }

        @Override
        public String getBannerDesc(int position) {
            return mDesc[position];
        }
    });

    mBannerView.startRoll();
}

使用起来还是很简单的。

我们来看下效果:

这里写图片描述



下载地址:BannerView

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值