Android自定义View(七)--很low的bannerView

最近公司的项目需要实现类似淘宝、京东首页的广告banner,作为一个专业的“拷贝型”程序猿,在github上搜了一下,还是挺多的,其中还有两个比较好的,扩展性和特效都不错,看得我很是羡慕啊,有兴趣的童鞋请移步:

1.FlycoBanner_Master

2.Android-ConvenientBanner

作为一个长期“拷贝”的程序猿,偶尔还是有点小失落的,偶尔也会有大神梦,即使那么遥不可及。。。
于是乎,在一个月黑风高的夜晚,有史以来最low的广告banner诞生了。

先谈谈整体的思路吧,看到有些大神用AdapterView实现的,而我这种渣,首先想到的就是viewpager,虽然viewpager没有那么完美,不过能勉强将就也还是极好的。

当然只有viewpager是不够的,还有viewpager上面的那几个小点点,这个我就更简单粗暴的用一个Layout装载了结果imageview,然后viewpager改变item的时候,改变小点点的图片就可以啦。

还有一个问题就是循环播放啦,这个就要从viewpager的adapter入手了,上网查了很多,得到的结论就是在adapter的getCount方法上做手脚,即返回一个很大的数,使viewpager的item数量大于你看到的实际数量,而多的item中显示与前面item相同的内容,这样看起来就是循环的啦。

剩下的要解决自动循环就更简单啦,管你用timer还是handler还是thread都可以简单搞定。最后要解决的就是使这个东西用起来比较简单,所以我就简单的对它进行了一层小包装。

主要的视图类

package com.drivingassisstantHouse.library.widget.bannerview;

import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Scroller;

import com.drivingassisstantHouse.library.R;
import com.drivingassisstantHouse.library.tools.SLog;
import com.drivingassisstantHouse.library.tools.ToolUnit;

import java.lang.reflect.Field;
import java.util.ArrayList;

/**
 * 包名:com.drivingassisstantHouse.library.widget.bannerview
 * 描述:广告banner
 * 创建者:yankebin
 * 日期:2016/2/27
 */
public class BannerView extends RelativeLayout {
    public static final int BANNER_TYPE_GUIDE = 1;
    public static final int BANNER_TYPE_ADVERT = BANNER_TYPE_GUIDE + 1;

    protected int type = BANNER_TYPE_GUIDE;
    private BaseBannerViewpager viewPager;
    private LinearLayout indicator;
    private boolean isAutoLoop = false;
    private long loopDelayTime = 4000;
    private long scrollDuration=1200;
    private ArrayList dots = new ArrayList<>();

    public BannerView(Context context) {
        super(context);
        init();
    }

    public BannerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.BannerView);
        type = typedArray.getInt(R.styleable.BannerView_bannerType, BANNER_TYPE_GUIDE);
        isAutoLoop = typedArray.getBoolean(R.styleable.BannerView_autoPlay, false);
        loopDelayTime = typedArray.getInt(R.styleable.BannerView_loopTime, 4000);
        scrollDuration=typedArray.getInt(R.styleable.BannerView_scrollDuration, 1200);
        if (null != typedArray) {
            typedArray.recycle();
        }
        init();
    }

    @Override
    protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
        super.onVisibilityChanged(changedView, visibility);
        if (visibility != VISIBLE) {
            removeCallbacks(autoRunner);
        } else {
            if (isAutoLoop) {
                startAutoPlay();
            }
        }
    }

    private Runnable autoRunner = new Runnable() {
        @Override
        public void run() {
            if (!isAutoLoop) {
                return;
            }
            int position=viewPager.getCurrentItem();
            viewPager.setCurrentItem(++position, true);
            postDelayed(this, loopDelayTime);
        }
    };

    private Runnable indicatorRunner=new Runnable() {
        @Override
        public void run() {
            int currentPosition = viewPager.getCurrentItem();
            for (int i = 0; i < dots.size(); i++) {
                ImageView imageView = dots.get(i);
                if (currentPosition % dots.size() == i) {
                    imageView.setImageResource(R.drawable.banner_dot_shape_selected);
                } else {
                    imageView.setImageResource(R.drawable.banner_dot_shape_normal);
                }
            }
        }
    };

    public void setIsAutoLoop(boolean isLoop) {
        this.isAutoLoop = isLoop;
    }

    public boolean isAutoLoop() {
        return isAutoLoop;
    }

    public void setBannerAdapter(PagerAdapter adapter) {
        viewPager.setAdapter(adapter);
    }

    private void init() {
        viewPager = new BaseBannerViewpager(getContext());
        viewPager.setLayoutParams(new LayoutParams(-1, -1));

        indicator = new LinearLayout(getContext());
        LayoutParams params = new LayoutParams(-2, -2);
        params.addRule(ALIGN_PARENT_BOTTOM);
        params.addRule(CENTER_HORIZONTAL);
        params.bottomMargin = 10;
        indicator.setLayoutParams(params);
        indicator.setOrientation(LinearLayout.HORIZONTAL);

        addView(viewPager);
        addView(indicator);

        initPagerListener();
    }

    private void initPagerListener() {
        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }

            @Override
            public void onPageSelected(int position) {
                changeDotsColors();
            }

            @Override
            public void onPageScrollStateChanged(int state) {
            }
        });
    }

    private void initDots(int size) {
        if (size <= 0) {
            throw new IllegalArgumentException("size必须大于0");
        }
        removeCallbacks(autoRunner);
        dots.clear();
        LinearLayout.LayoutParams pointViewParams = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        pointViewParams.setMargins(ToolUnit.dipTopx(8), 0,
                ToolUnit.dipTopx(8), ToolUnit.dipTopx(8));

        for (int i = 0; i < size; i++) {
            ImageView pointView = new ImageView(getContext());
            pointView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
            pointView.setLayoutParams(pointViewParams);
            dots.add(pointView);
            indicator.addView(pointView);
        }
        changeDotsColors();
        if (isAutoLoop) {
            startAutoPlay();
        }
    }

    private void startAutoPlay() {
        removeCallbacks(autoRunner);
        postDelayed(autoRunner, loopDelayTime);
    }

    public void changeDotsColors() {
       postDelayed(indicatorRunner,scrollDuration+500);
    }

    private class BaseBannerViewpager extends ViewPager {
        private int childId;

        public class FixedSpeedScroller extends Scroller {
            private int mDuration = (int)scrollDuration;

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

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

            @Override
            public void startScroll(int startX, int startY, int dx, int dy, int duration) {
                // Ignore received duration, use fixed one instead
                super.startScroll(startX, startY, dx, dy, mDuration);
            }

            @Override
            public void startScroll(int startX, int startY, int dx, int dy) {
                // Ignore received duration, use fixed one instead
                super.startScroll(startX, startY, dx, dy, mDuration);
            }

            public void setmDuration(int time) {
                mDuration = time;
            }

            public int getmDuration() {
                return mDuration;
            }
        }

        public BaseBannerViewpager(Context context) {
            super(context);
            resetScroller(context);
        }

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

        private void resetScroller(Context context){
            try {
                Field field = ViewPager.class.getDeclaredField("mScroller");
                field.setAccessible(true);
                FixedSpeedScroller scroller = new FixedSpeedScroller(context,
                        new AccelerateInterpolator());
                field.set(this, scroller);
            } catch (Exception e) {
                SLog.e(e);
            }
        }

        @Override
        public boolean onInterceptTouchEvent(MotionEvent event) {
            if (childId > 0) {
                ViewPager pager = (ViewPager) findViewById(childId);

                if (pager != null) {
                    pager.requestDisallowInterceptTouchEvent(true);
                }

            }
            return super.onInterceptTouchEvent(event);
        }

        public void setAllowChildMovement(int id) {
            this.childId = id;
        }

        @Override
        public void setAdapter(PagerAdapter adapter) {
            initDots(((BaseBannerPagerAdapter)adapter).getRealCount());
            super.setAdapter(adapter);
        }
    }
}

viewpager的adapter

package com.drivingassisstantHouse.library.widget.bannerview;

import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;

/**
 * 包名:com.simpletour.client.activity
 * 描述:banner适配器
 * 创建者:yankebin
 * 日期:2016/2/27
 */
public abstract class BaseBannerPagerAdapter extends android.support.v4.view.PagerAdapter {
    protected SparseArray views = new SparseArray<>();
    private boolean isLoop;
    protected ArrayList datas;

    public BaseBannerPagerAdapter() {
        datas = new ArrayList<>();
    }

    public BaseBannerPagerAdapter(ArrayList datas) {
        this.datas = datas;
    }

    @Override
    public final int getCount() {
        return isLoop ? Integer.MAX_VALUE : datas.size();
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        position = position % datas.size();
        View view = views.get(position);
        if (view == null) {
            view = newView(position % getCount());
            views.put(position, view);
        }
        container.addView(view);
        return view;
    }

    public void notifyUpdateView(int position) {
        View view = updateView(views.get(position), position);
        views.put(position, view);
        notifyDataSetChanged();
    }

    public View updateView(View view, int position) {
        return view;
    }

    public abstract View newView(int position);

    public final int getRealCount() {
        return datas.size();
    }

    public void subscribe(BannerView bannerView) {
        isLoop =bannerView.isAutoLoop()|| bannerView.type == BannerView.BANNER_TYPE_ADVERT;
        bannerView.setBannerAdapter(this);
    }
}

一些自定义属性

<declare-styleable name="BannerView">
        <attr name="autoPlay" format="boolean"/>
        <attr name="bannerType" format="integer">
            <flag name="typeGuide" value="1"/>
            <flag name="typeAdvert" value="2"/>
        attr>
        <attr name="loopTime" format="integer"/>
        <attr name="scrollDuration" format="integer"/>
    declare-styleable>

代码里调用

首先定义好xml文件

"http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.drivingassisstantHouse.library.widget.bannerview.BannerView
        xmlns:banner="http://schemas.android.com/apk/res-auto"
        android:layout_weight="1"
        banner:autoPlay="true"
        banner:loopTime="3000"
        banner:bannerType="typeAdvert"
        banner:scrollDuration="800"
        android:id="@+id/bannerView"
        android:layout_width="match_parent"
        android:layout_height="0dp">

    com.drivingassisstantHouse.library.widget.bannerview.BannerView>

其次实现BaseBannerPagerAdapter

 private class MBannerPagerAdapter extends BaseBannerPagerAdapter {
        public MBannerPagerAdapter() {
            super(imageUrlList);
            initImageLoader();
        }

        private void initImageLoader() {
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return null != object && object == view;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView(views.get(position % views.size()));
        }

        @Override
        public View newView(final int position) {
            ImageView imageView = new ImageView(GuideActivity.this);
            imageView.setLayoutParams(new ViewGroup.LayoutParams(-1, -1));
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);

            ImageLoader.getInstance().displayImage(
                    datas.get(position),
                    imageView);

            imageView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent intent=new Intent(GuideActivity.this,FloatActivity.class);
                    intent.putExtra("uri", imageUrlList.get(position));
                    Intent newIntent=new Intent(GuideActivity.this,PayDemoActivity.class);
                    intent.putExtra("intent",newIntent);
                    startActivity(intent);
                }
            });

            return imageView;
        }
    }

最后

 new MBannerPagerAdapter().subscribe(bannerView);

这个效果和淘宝、京东的还有差距:

1.对于用户干预滑动的处理,我所能想到就是在touch事件中控制autorunner的执行,至于实现的细节,我还没有想好怎么去弄得更完美。

2.圆点指示器的实现方式,要是着色的圆点可以随viewpager相同的效果滑动而不是瞬间改变颜色就更好了。这个效果我也在viewpager的listener的onpagescrolled方法里尝试过了,但是当人为干预后,圆点就会错位,目前还没想到办法解决,希望有大神可以指点一二。

你可能感兴趣的:(android,View,Android开发)