Android无限广告轮播

来源:互联网 发布:手机解压密码破解软件 编辑:程序博客网 时间:2024/05/17 07:58

1.概述


点击打开链接  

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


这里写图片描述

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.实现自动轮播 - 发送消息的msgWhat    private final int SCROLL_MSG = 0x0011;    // 2.实现自动轮播 - 页面切换间隔时间    private int mCutDownTime = 3500;    // 2.实现自动轮播 - 发送消息Handler    private Handler mHandler = new Handler(){        @Override        public void handleMessage(Message msg) {            // 每隔*s后切换到下一页            setCurrentItem(getCurrentItem() + 1);            // 不断循环执行            startRoll();        }    };    /**     * 2.实现自动轮播     */    public void startRoll(){        // 清除消息        mHandler.removeMessages(SCROLL_MSG);        // 消息  延迟时间  让用户自定义  有一个默认  3500        mHandler.sendEmptyMessageDelayed(SCROLL_MSG,mCutDownTime);        Log.e(TAG,"startRoll");    }    /**     * 2.销毁Handler停止发送  解决内存泄漏     */    @Override    protected void onDetachedFromWindow() {        super.onDetachedFromWindow();        mHandler.removeMessages(SCROLL_MSG);        mHandler = null;    }

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

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

    // 3.改变ViewPager切换的速率 - 自定义的页面切换的Scroller    private BannerScroller mScroller;    public BannerViewPager(Context context, AttributeSet attrs) {        super(context, attrs);        try {            // 3.改变ViewPager切换的速率            // 3.1 duration 持续的时间  局部变量            // 3.2.改变 mScroller private 通过反射设置            Field field = ViewPager.class.getDeclaredField("mScroller");            // 设置参数  第一个object当前属性在哪个类  第二个参数代表要设置的值            mScroller = new BannerScroller(context);            // 设置为强制改变private            field.setAccessible(true);            field.set(this,mScroller);        } catch (Exception e) {            e.printStackTrace();        }    }    /**     * 3.设置切换页面动画持续的时间     */    public void setScrollerDuration(int scrollerDuration){        mScroller.setScrollerDuration(scrollerDuration);    }

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


这里写图片描述


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

package com.example.hui.androidtemplate.banner;import android.content.Context;import android.util.AttributeSet;import android.widget.LinearLayout;import android.widget.RelativeLayout;import android.widget.TextView;import com.example.hui.androidtemplate.R;/** * description: * <p/> * Created by 曾辉 on 2016/11/18. * QQ:240336124 * Email: 240336124@qq.com * Version:1.0 */public class BannerView extends RelativeLayout{    // 4.自定义BannerView - 轮播的ViewPager    private BannerViewPager mBannerVp;    // 4.自定义BannerView - 轮播的描述    private TextView mBannerDescTv;    // 4.自定义BannerView - 点的容器    private LinearLayout mDotContainerView;    // 4.自定义BannerView - 自定义的BannerAdapter    private BannerAdapter mAdapter;    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);        // 把布局加载到这个View里面        inflate(context, R.layout.ui_banner_layout,this);        initView();    }    /**     * 初始化View     */    private void initView() {        mBannerVp = (BannerViewPager) findViewById(R.id.banner_vp);        mBannerDescTv = (TextView) findViewById(R.id.banner_desc_tv);        mDotContainerView = (LinearLayout) findViewById(R.id.dot_container);    }    /**     * 4.设置适配器     */    public void setAdapter(BannerAdapter adapter){        mBannerVp.setAdapter(adapter);    }    /**     * 4.开始滚动     */    public void startRoll() {        mBannerVp.startRoll();    }}

2.5. 初始化点的指示器

    /**     * 5.初始化点的指示器     */    private void initDotIndicator() {        // 获取广告的数量        int count = mAdapter.getCount();        // 让点的位置在右边        mDotContainerView.setGravity(Gravity.RIGHT);        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 == 0) {                // 选中位置                indicatorView.setDrawable(mIndicatorFocusDrawable);            }else{                // 未选中的                indicatorView.setDrawable(mIndicatorNormalDrawable);            }            mDotContainerView.addView(indicatorView);        }    }    /**     * 5.把dip转成px     */    private int dip2px(int dip) {        return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,                dip,getResources().getDisplayMetrics());    }

2.6. 阶段性的Bug修复

     /**     * 4.设置适配器     */    public void setAdapter(BannerAdapter adapter){        mAdapter = adapter;        mBannerVp.setAdapter(adapter);        // 5.初始化点的指示器        initDotIndicator();        // 6.Bug修复        mBannerVp.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener(){            @Override            public void onPageSelected(int position) {                // 监听当前选中的位置                pageSelect(position);            }        });        // 6.初始化的时候获取第一条的描述        String firstDesc = mAdapter.getBannerDesc(0);        mBannerDescTv.setText(firstDesc);    }    /**     * 6.页面切换的回调     * @param position     */    private void pageSelect(int position) {        // 6.1 把之前亮着的点 设置为默认        DotIndicatorView oldIndicatorView = (DotIndicatorView)                mDotContainerView.getChildAt(mCurrentPosition);        oldIndicatorView.setDrawable(mIndicatorNormalDrawable);        // 6.2 把当前位置的点 点亮  position 0 --> 2的31次方        mCurrentPosition = position%mAdapter.getCount();        DotIndicatorView currentIndicatorView = (DotIndicatorView)                mDotContainerView.getChildAt(mCurrentPosition);        currentIndicatorView.setDrawable(mIndicatorFocusDrawable);        // 6.3设置广告描述        String bannerDesc = mAdapter.getBannerDesc(mCurrentPosition);        mBannerDescTv.setText(bannerDesc);    }

2.7. 把指示器的点绘制成圆

/** * description:  圆的指示器 *    圆点指示器 * Created by 曾辉 on 2016/11/18. * QQ:240336124 * Email: 240336124@qq.com * Version:1.0 */public class DotIndicatorView extends View {    private Drawable drawable;    public DotIndicatorView(Context context) {        this(context, null);    }    public DotIndicatorView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public DotIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    protected void onDraw(Canvas canvas) {        if(drawable != null){            /*drawable.setBounds(0,0,getMeasuredWidth(),getMeasuredHeight());            drawable.draw(canvas);*/            // 7.把指示器变成圆形            // 画圆            Bitmap bitmap = drawableToBitmap(drawable);            // 把Bitmap变为圆的            Bitmap circleBitmap = getCircleBitmap(bitmap);            // 把圆形的Bitmap绘制到画布上            canvas.drawBitmap(circleBitmap,0,0,null);        }    }    /**     * 7.获取圆形bitmap     */    private Bitmap getCircleBitmap(Bitmap bitmap) {        // 创建一个Bitmap        Bitmap circleBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);        Canvas canvas = new Canvas(circleBitmap);        Paint paint = new Paint();        // 设置抗锯齿        paint.setAntiAlias(true);        paint.setFilterBitmap(true);        // 设置仿抖动        paint.setDither(true);        // 在画布上面画个圆        canvas.drawCircle(getMeasuredWidth()/2,getMeasuredHeight()/2,getMeasuredWidth()/2,paint);        // 取圆和Bitmap矩形的交集        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));        // 再把原来的Bitmap绘制到新的圆上面        canvas.drawBitmap(bitmap,0,0,paint);        return circleBitmap;    }    /**     * 7.从drawable中得到Bitmap     * @param drawable     * @return     */    private Bitmap drawableToBitmap(Drawable drawable) {        // 如果是BitmapDrawable类型        if(drawable instanceof BitmapDrawable){            return((BitmapDrawable)drawable).getBitmap();        }        // 其他类型 ColorDrawable        // 创建一个什么也没有的bitmap        Bitmap outBitmap = Bitmap.createBitmap(getMeasuredWidth(),getMeasuredHeight(), Bitmap.Config.ARGB_8888);        // 创建一个画布        Canvas canvas = new Canvas(outBitmap);        // 把drawable化到Bitmap上        drawable.setBounds(0,0,getMeasuredWidth(),getMeasuredHeight());        drawable.draw(canvas);        return outBitmap;    }    /**     * 5.设置Drawable     */    public void setDrawable(Drawable drawable) {        this.drawable = drawable;        // 重新绘制View        invalidate();    }}

2.8. 设置自定义属性

    /**     * 8.初始化自定义属性     */    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));        array.recycle();    }

2.9. 自适应高度

    // 8.自适应高度 动态指定高度    if(mHeightProportion == 0 || mWidthProportion == 0){         return;    }    // 动态指定宽高  计算高度    int width = getMeasuredWidth();    // 计算高度    int height = (int) (width*mHeightProportion/mWidthProportion);    // 指定宽高    getLayoutParams().height = height;

2.10. 内存优化
  写完之后,可以了就大功告成但是这个时候我们要去优化,还不好用不?容易扩展不?内存优化好没?在这里就不多写了,如回收Bitmap,界面复用,管理Activity生命周期等等,一切都在视频里面。


这里写图片描述


  
  如果实在还是看不太懂,可以看一下我录的频,可以看一下整个系统架构也可以了解一下整个项目的其他东西:http://pan.baidu.com/s/1skOdHzn。

0 0
原创粉丝点击