如何打造一个简单,强大的自动轮播图控件

GitHub地址:https://github.com/xuerui1993/AutoViewPager

自动轮播图是安卓开发常用控件,如果在需要开发时,每个地方去写,这样就比较耗时、费力,不妨可以封装成一个自定义控件,在需要使用使只去设置数据就可以了,这样在后续开发中不仅省时,而且可以更好的排错。上面github地址中提供了源码,需要更详细的了解可以去阅读源码一下。

1507193217(1).jpg

一、实现自定义属性

虽然这个并不难,但也是自定义控件常用的特点

  1. 首先将控件继承RelativeLayout,并在values文件夹attrs.xml文件中声明
    <declare-styleable name="AutoViewpager">
        <attr name="dotSize" format="dimension"/>
        <attr name="dotSrc" format="reference"/>
        <attr name="duration" format="integer"/>
        <attr name="isAuto" format="boolean"/>
        <attr name="dotPosition" format="enum">
            <enum name="left" value="0"/>
            <enum name="center" value="1"/>
            <enum name="right" value="2"/>
        </attr>
    </declare-styleable>
  1. 读取属性
    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.AutoViewpager);
    mDuration = ta.getInteger(R.styleable.AutoViewpager_duration, NORMAL_DURATION);
    mDotSize = ta.getDimensionPixelSize(R.styleable.AutoViewpager_dotSize, DOT_NORAML_SIZE);
    mIsAuto = ta.getBoolean(R.styleable.AutoViewpager_isAuto, true);
    mDrawable = ta.getDrawable(R.styleable.AutoViewpager_dotSrc);
    mDotPosition = ta.getInteger(R.styleable.AutoViewpager_dotPosition, 2);
    //释放资源
    ta.recycle();
  1. 在代码使用读取出来的属性
注意:在小圆点使用Drawable图片时,由于涉及到图片的selected属性,所以每一个小圆点需要使用到一个新的Drawable,所以需要使用的图片的克隆,否则一个Drawable对象如果使用selected,会引起所有的图片selected,无法实现小圆点的选中状态。
Drawable newDrawable = mDrawable.getConstantState().newDrawable();
imageView.setImageDrawable(newDrawable);

二、设置Viewpager的适配器

  1. 为了能无限轮播,将getCount()方法返回Integer的最大值

     @Override
     public int getCount() {
         if (mList != null) {
             return Integer.MAX_VALUE;
         }
         return 0;
     }
    
  2. 做出ImageLoader接口让外部去实现这个接口去加载图片,减少对第三方库的依赖

     public interface ImageLoader extends Serializable {
         void displayImage(Context context, String url, ImageView imageView);
         void clearMemoryCache();
     }
    

3.在adapter的instantiateItem方法中设置图片和图片的点击事件,为了无限轮播需要将图片的position % mList.size();

    @Override
    public Object instantiateItem(ViewGroup container, final int position) {
        final int picPosition = position % mList.size();//为了无限轮播,需要将positon % mList.size();
        ImageView imageView = new ImageView(mContext);
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        String url = mList.get(picPosition);
        if (mImageloader==null){
            throw new IllegalArgumentException("图片加载类为null,需实现ImageLoader接口并赋值");
        }
        mImageloader.displayImage(mContext,url,imageView);
        imageView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mItemClickListener != null) {
                    mItemClickListener.OnItemClickListener(picPosition);
                }
            }
        });
        container.addView(imageView);
        return imageView;
    }
注意:为了实现无限轮播效果,需要新创建ImageView对象,否则ViewpagerAdapter会抛出异常
ImageView imageView = new ImageView(mContext);

三、初始化小圆点

private void initDotContaner(List<String> list) {
    //设置小圆点在控件中的位置
    RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) mLlDot.getLayoutParams();
    switch (mDotPosition) {
        case 0:
            layoutParams.addRule(RelativeLayout.ALIGN_LEFT);
            break;
        case 1:
            layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
            break;
        case 2:
            layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
            break;
    }
    mLlDot.removeAllViews();//将控件清楚干净
    ArrayList<ImageView> imageList = new ArrayList<>();
    for (int i = 0; i < list.size(); i++) {
        ImageView imageView = new ImageView(getContext());
        if (mDotDrawableRes != 0) {
            //代码中设置了mDotDrawableRes则优先设置
            imageView.setImageResource(R.drawable.dot_selector);
        } else {
            if (mDrawable != null) {
                //在布局中设置了,则显示布局中的
                Drawable newDrawable = mDrawable.getConstantState().newDrawable();  
                imageView.setImageDrawable(newDrawable);//克隆小圆点并设置
            } else {
                //若都没设置则,设置默认
                imageView.setImageResource(R.drawable.dot_selector);
            }
        }
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mDotSize, mDotSize);
        imageList.add(imageView);
        if (i != 0) {
            params.leftMargin = mDotSize;
        } else {
            //默认第一个小圆点被选中
            mCurrentDot = imageView;
            imageView.setSelected(true);
        }
        imageView.setLayoutParams(params);
        mLlDot.addView(imageView);  //添加小圆点
    }
}

四、让图片开始轮播

  1. 为了开始就能够向左滑动和向右滑动,将图片选择为Integer最大值的中间值

     //选中中间的第一个
     int middle = Integer.MAX_VALUE / 2;
     int extra = middle % mCount;
     mViewPager.setCurrentItem(middle-extra);
    
  2. 做一个定时任务,让图片开始轮播

     class SwitchPagerTask extends Handler implements Runnable {
    
         @Override
         public void run() {
             int currentItem = mViewPager.getCurrentItem();
             if (currentItem == mAdvertAdapter.getCount() - 1) {
                 mViewPager.setCurrentItem(0);
             } else {
                 mViewPager.setCurrentItem(currentItem + 1);
             }
             //在执行一次post , 循环执行
             postDelayed(this, mDuration);  //使用读取出来的轮播切换时间duration
         }
    
         /**
          * 开始切换
          */
         public void start() {
             //停掉以前的任务
             removeCallbacks(this);
             //在执行一次post , 循环执行
             postDelayed(this, START_DURATION);
         }
    
         /**
          * 停止切换
          */
         public void stop() {
             //停掉以前的任务
             removeCallbacks(this);
         }
     }
    

3.设置小圆点的被选中状态,需要设置监听viewpager的滑动状态,mViewPager.addOnPageChangeListener(this);

@Override
public void onPageSelected(int position) {
    position = position % mLabelList.size();
    if (mLabelList != null && mLabelList.size() > position) {
        mTvLabel.setText(mLabelList.get(position) + "");
    }
    mCurrentDot.setSelected(false);
    ImageView imageView = (ImageView) mLlDot.getChildAt(position);
    imageView.setSelected(true);
    mCurrentDot = imageView;
}

五、细节完善

  1. 在滑动触摸式重新即时viewpager的轮播,监听viewpager的触摸事件,mViewPager.setOnTouchListener(this);

     @Override
     public boolean onTouch(View v, MotionEvent event) {
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
                 if (mSwitchPagerTask != null) {
                     mSwitchPagerTask.stop();
                 }
                 break;
             case MotionEvent.ACTION_MOVE:
                 if (mSwitchPagerTask != null) {
                     mSwitchPagerTask.stop();
                 }
                 break;
             case MotionEvent.ACTION_UP:
                 if (mSwitchPagerTask != null) {
                     mSwitchPagerTask.start();
                 }
                 break;
         }
         return false;
     }
    
  2. 防止内存泄漏,添加AutoViewPager的Ondestroy()方法

     public void onDestory() {
         mSwitchPagerTask.stop();
         mSwitchPagerTask = null;
         mViewPager.removeOnPageChangeListener(this);
         mItemClickListener = null;
     }
    

六、如果想可以在AndroidStudio的gradle配置中添加依赖使用

这里有我写的一篇如何发布你的GitHub开源库的文章。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容