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 10pxint 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; } }

 
在要实现的功能代码中添加
【Android中仿淘宝首页顶部滚动自定义HorizontalScrollView定时水平自动切换图片】
home_pager.setImageUrl(mPicList); //添加URL进行数据获取
home_pager.setAutoPlay(true, 3000); //自动轮播开始并设置多少秒滚动一次
home_pager.setOnPageClickListener(this); //相应的点击事件







    推荐阅读