Android中仿淘宝首页顶部滚动自定义HorizontalScrollView定时水平自动切换图片
Posted WYH_Healer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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);//相应的点击事件
以上代码为全部代码,如有代码贴入不能运行或报错或缺少请在下方留言,作者会进行处理回复,O(∩_∩)O谢谢
以上是关于Android中仿淘宝首页顶部滚动自定义HorizontalScrollView定时水平自动切换图片的主要内容,如果未能解决你的问题,请参考以下文章
Android中仿淘宝首页顶部滚动自定义HorizontalScrollView定时水平自动切换图片
Android中仿淘宝商品详情ViewPager页面数据手动滑动
React Native之ViewPagerAndroid仿淘宝首页顶部分类布局效果实现
android 自定义scrollview 仿QQ空间效果 下拉伸缩顶部图片,上拉回弹 上拉滚动顶部title 颜色渐变