首页 > 代码库 > Android中仿淘宝首页顶部滚动自定义HorizontalScrollView定时水平自动切换图片

Android中仿淘宝首页顶部滚动自定义HorizontalScrollView定时水平自动切换图片

Android中仿淘宝首页顶部滚动自定义HorizontalScrollView定时水平自动切换图片

  • 自定义ADPager

自定义水平滚动的ScrollView效仿ViewPager

当遇到要在ViewPager中添加多张网络请求图片的情况下,不能进行复用,导致每次都要重新去求情已经请求过的数据致使流量数据过大

自定义的数据结构解决了这个问题,固定传递的图片数据之后进行统一请求,完成后进行页面切换数据复用

代码中涉及网络请求是用的Volley网络请求框架
PicCarousel是网络数据请求的URL相关数据(使用者自己需要的URL)

public class ADPager extends HorizontalScrollView implements View.OnClickListener{

    private final int VELOCITY_SLOT = 1000;
    private final int DEFAULT_AUTOPLAY_DURATION = 5000;
    private List<PicCarousel> noticeList;
    private LinearLayout container;
    private LinearLayout.LayoutParams linearLayoutParams;
    private ImageLoader mImageLoader;
//    private DisplayImageOptions imageOptions;
    private VelocityTracker velocityTracker;
    private OnADPageClickListener mADPageClickListener;
    private int mCurrPage = 0;

    private long mDuration = DEFAULT_AUTOPLAY_DURATION;
    private boolean mIsAutoPlaying = false;
    private AutoPlayRunnable mAutoPlayRunnable = new AutoPlayRunnable();

    private int mMaximumVelocity;

    private float mCircleRadius;

    private Paint mStrokePaint;
    private Paint mFillPaint;

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

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

    public ADPager(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        Context ctx = getContext();
        this.container = new LinearLayout(ctx);
        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        this.container.setOrientation(LinearLayout.HORIZONTAL);
        this.container.setLayoutParams(params);
        this.addView(this.container);
        this.linearLayoutParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT);
        this.linearLayoutParams.weight = 1; // 平等分
        this.noticeList = new ArrayList<>();
        this.setHorizontalScrollBarEnabled(false);
//        this.imageLoader = ImageLoader.getInstance();

        mImageLoader = new com.android.volley.toolbox.ImageLoader(SingleRequestQueue.getRequestQueue(Utils.getContext()), new BitmapCache());

        this.setSmoothScrollingEnabled(true);

        final Resources res = getResources();
        this.mCircleRadius = 8;

        /** 默认图 **/
        NetworkImageView imgView = makeImageView();
        this.container.addView(imgView);
        /** 默认图结束 **/

        /**
         * 设置松手时velocity的追踪
         */
        final ViewConfiguration configuration = ViewConfiguration.get(ctx);
        this.mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();

//        DisplayImageOptions.Builder builder = new DisplayImageOptions.Builder();
//        builder.cacheInMemory(true).cacheOnDisc(true)
//                .showImageForEmptyUri(R.mipmap.def_pic)
//                .showImageOnLoading(R.mipmap.def_pic)
//                .showImageOnFail(R.mipmap.def_pic);
//        imageOptions = builder.build();

        initPaint();
    }

    private void initPaint() {
        mStrokePaint = new Paint();
        mStrokePaint.setStrokeWidth(1.0f);
        mStrokePaint.setStyle(Paint.Style.STROKE);
        mStrokePaint.setColor(Color.WHITE);
        mStrokePaint.setAntiAlias(true);

        mFillPaint = new Paint();
        mFillPaint.setColor(Color.WHITE);
        mFillPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mFillPaint.setAntiAlias(true);
    }

    private NetworkImageView makeImageView() {
        Context ctx = getContext();
        NetworkImageView imgView = new NetworkImageView(ctx);
        imgView.setLayoutParams(this.linearLayoutParams);
        imgView.setDefaultImageResId(R.mipmap.def_pic);
        imgView.setScaleType(NetworkImageView.ScaleType.CENTER_CROP);
        return imgView;
    }

    /**
     * 设置 image的url
     *
     * @param noticeList
     */
    public void setImageUrl(List<PicCarousel> noticeList) {
        this.noticeList = noticeList;
        int size = noticeList.size();
        this.container.removeAllViews();
        NetworkImageView imgView;
        int position;
        if (size == 0) {
            imgView = makeImageView();
            imgView.setImageResource(R.mipmap.def_pic);
            this.container.addView(imgView);
            return;
        }
        if (size > 1) {
            imgView = makeImageView();
            position = size - 1;
            String lastUrl = noticeList.get(position).getPicUrl();
            imgView.setTag(position);
            imgView.setOnClickListener(this);
            /**
             * 使用Volley框架加载图片更加快捷,使缓存在一处用于初始化显示
             * */
//            imageLoader.displayImage(lastUrl, imgView, imageOptions);
            imgView.setImageUrl(lastUrl, mImageLoader);
            this.container.addView(imgView);
        }
        position = 0;
        for (PicCarousel notice : noticeList) {
            imgView = makeImageView();
            imgView.setTag(position);
            imgView.setOnClickListener(this);
//            imageLoader.displayImage(notice.getPicUrl(), imgView, imageOptions);
            imgView.setImageUrl(notice.getPicUrl(), mImageLoader);
            this.container.addView(imgView);
            position ++;
        }
        if (size > 1) {
            String firstUrl = noticeList.get(0).getPicUrl();
            imgView = makeImageView();
            imgView.setTag(0);
            imgView.setOnClickListener(this);
//            imageLoader.displayImage(firstUrl, imgView, imageOptions);
            imgView.setImageUrl(firstUrl, mImageLoader);
            this.container.addView(imgView);
        }
        this.requestLayout();
        this.scrollToPage(0, false);
    }

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

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int size = this.noticeList.size();
        int imageLength;
        if (size > 1) {
            imageLength = size + 2;
        } else {
            imageLength = 1;
        }
        int containerSize = widthSize * imageLength;
        switch (widthMode) {
            case MeasureSpec.AT_MOST:
            case MeasureSpec.EXACTLY: {
                int childWidthSpec = MeasureSpec.makeMeasureSpec(containerSize, MeasureSpec.EXACTLY);
                this.container.measure(childWidthSpec, heightMeasureSpec);
                break;
            }
            case MeasureSpec.UNSPECIFIED: {
                throw new RuntimeException("Can not be unspecified");
            }
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getActionMasked();
        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                if (mIsAutoPlaying) {
                    removeCallbacks(mAutoPlayRunnable);
                }
                break;
            }
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        int action = ev.getActionMasked();
        initVelocityTrackerIfNeed();
        velocityTracker.addMovement(ev);
        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                break;
            }
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL: {
                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                float velocityX = velocityTracker.getXVelocity();
                int scrollX = this.getScrollX();
                int width = this.container.getChildAt(0).getWidth();
                int page;
                if (Math.abs(velocityX) > VELOCITY_SLOT) {
                    page = scrollX / width;
                    if (velocityX > 0) {
                        page = page - 1;
                    }
                } else {
                    page = (int)Math.round(scrollX * 1.0 / width) - 1;
                }
                this.scrollToPage(page, true);

                recycleVelocityTracker();
                if (mIsAutoPlaying) {
                    postDelayed(mAutoPlayRunnable, mDuration);
                }
                return true;
            }
        }
        return super.onTouchEvent(ev);
    }

    private void initVelocityTrackerIfNeed() {
        if (velocityTracker == null) {
            velocityTracker = VelocityTracker.obtain();
        }
    }

    private void recycleVelocityTracker() {
        if (velocityTracker != null) {
            velocityTracker.recycle();
            velocityTracker = null;
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        this.scrollToPage(0, false);
        super.onLayout(changed, l, t, r, b);
    }

    /**
     * 滚动到某个页面
     *
     * @param page 页面第n页
     * @param smooth 滑动
     */
    public void scrollToPage(int page, boolean smooth) {
        int size = this.noticeList.size();
        if (page < 0) {
            page = size - 1;
        }
        if (size > 1) {
            int width = this.container.getChildAt(0).getWidth();
            mCurrPage = page;
            if (mCurrPage == size) {
                mCurrPage = 0;
            }
            if (!smooth) {
                this.scrollTo(width * (page + 1), 0);
            } else {
                this.smoothScrollTo(width * (page + 1), 0);
            }
        } else {
            mCurrPage = size - 1;
            this.scrollTo(0, 0);
        }
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        postInvalidate();
    }

    private class AutoPlayRunnable implements Runnable {
        @Override
        public void run() {
            if(mIsAutoPlaying){
                int size = noticeList.size();
                int targetPage = mCurrPage + 1;
                if (targetPage >= size) {
                    targetPage = 0;
                }
                scrollToPage(targetPage, true);
                postDelayed(mAutoPlayRunnable, mDuration);
            }

        }
    }

    public void setAutoPlay(boolean autoPlay) {
        this.setAutoPlay(autoPlay, DEFAULT_AUTOPLAY_DURATION);
    }

    public void setAutoPlay(boolean autoPlay, long duration) {
        mIsAutoPlaying = autoPlay;
        mDuration = duration;
        removeCallbacks(mAutoPlayRunnable);
        if (autoPlay) {
            postDelayed(mAutoPlayRunnable, duration);
        }
    }


    @TargetApi(Build.VERSION_CODES.GINGERBREAD)
    @Override
    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
        int width = this.container.getChildAt(0).getWidth();
        int size = this.noticeList.size();
        if (clampedX) {
            if (scrollX > 0) {
                mCurrPage = 0;
                scrollTo(width, 0);
            } else {
                mCurrPage = size - 1;
                scrollTo(width * size, 0);
            }
        }
    }

    public void setOnPageClickListener(OnADPageClickListener l) {
        this.mADPageClickListener = l;
    }

    public OnADPageClickListener getOnPageClickListener() {
        return this.mADPageClickListener;
    }

    @Override
    public void onClick(View v) {
        Integer position = (Integer) v.getTag();
        if (this.mADPageClickListener != null) {
            this.mADPageClickListener.onPageClick(position);
        }
    }

    public static interface OnADPageClickListener {
        public void onPageClick(int page);
    }


    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        drawCircle(canvas);
    }

    private void drawCircle(Canvas canvas) {
        int width = this.getWidth();
        int height = this.getHeight();

        float threeRadius = 3 * mCircleRadius;

        int size = this.noticeList.size();
        int circleLayoutWidth = (int)(threeRadius * size - mCircleRadius);

        int offsetX = (int)((width - circleLayoutWidth) / 2 + mCircleRadius) + this.getScrollX();   // start pos
        int offsetY = (int)(height - 15 - mCircleRadius);                       // padding Bottom 10px

        int iLoop;
        for (iLoop = 0; iLoop < size; iLoop ++) {
            canvas.drawCircle(offsetX, offsetY, mCircleRadius, mStrokePaint);

            if (iLoop == mCurrPage) {
                canvas.drawCircle(offsetX, offsetY, mCircleRadius, mFillPaint);
            }

            offsetX += threeRadius;

        }
    }
}

使用Volley框架首先要在项目中导入Volley.jar包进行依赖,然后就要编写相应的BitmapCache

public class BitmapCache implements ImageLoader.ImageCache {
    private String TAG=BitmapCache.class.getSimpleName();
    private LruCache<String, Bitmap> mCache;

    public BitmapCache() {
        /** 1.缓存区大小10M 单位是byte */
        int maxSize =(int) (Runtime.getRuntime().maxMemory() / 1024);
        mCache = new LruCache<String, Bitmap>(maxSize) {
            /** 2.重写sizeOf方法 返回条目的大小*/
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                /** 返回bitmap这个entry的大小,统一计算单位   */
              //  Log.e(TAG,"大小+"+bitmap.getRowBytes() * bitmap.getHeight());
                return bitmap.getRowBytes() * bitmap.getHeight();
            }
        };
    }

    /**
     * 从缓存中取数据
     *
     * @param url
     * @return
     */
    @Override
    public Bitmap getBitmap(String url) {
       // LogUtil.i("BitmapCache", "从内存中取出 -------->");
        return mCache.get(url);
    }

    /**
     * 往缓存中写数据
     *
     * @param url
     * @param bitmap
     */
    @Override
    public void putBitmap(String url, Bitmap bitmap) {
       // LogUtil.i("BitmapCache", "存放到内存 <------");
        mCache.put(url, bitmap);
    }
}

这里为了避免过多的Volley请求发出导致OOM异常这里需要对Volley的请求做一个单例设计模式操作

public class SingleRequestQueue {
    private static RequestQueue mQueue;

    private SingleRequestQueue(Context context) {
        mQueue = Volley.newRequestQueue(context);
    }

    public static synchronized RequestQueue getRequestQueue(Context context){
        if (mQueue == null){
            new SingleRequestQueue(context.getApplicationContext());
        }
        return mQueue;
    }
}

 

在要实现的功能代码中添加


home_pager.setImageUrl(mPicList);//添加URL进行数据获取
home_pager.setAutoPlay(true, 3000);//自动轮播开始并设置多少秒滚动一次
home_pager.setOnPageClickListener(this);//相应的点击事件

Android中仿淘宝首页顶部滚动自定义HorizontalScrollView定时水平自动切换图片