循环广告位也是一种非常常见的组件,网上有各种各样的实现,那天看了singwhatiwanna的一种实现,非常简单,然后结合之前见过的一种,稍微整理了一下。
转载请标明出处:http://blog.csdn.net/goldenfish1919/article/details/46811889
先看下使用方式:
<com.xjs.demo.view.BannerView android:id="@+id/bannerView" android:layout_width="match_parent" android:layout_height="150dp" android:paddingLeft="10dp" android:paddingRight="10dp"> <android.support.v4.view.ViewPager android:id="@+id/banner_viewpager" android:layout_width="match_parent" android:layout_height="match_parent" /> <com.xjs.demo.view.DotView android:id="@+id/banner_dotview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|center_horizontal" android:layout_marginBottom="10dp" app:dot_number="5" app:dot_radius="4dp" app:dot_selected_color="0xffffffff" app:dot_span="8dp" app:dot_unselected_color="0x80ffffff" /> </com.xjs.demo.view.BannerView>
BannerView banner = (BannerView) this.findViewById(R.id.bannerView); banner.setOnBannerClickListener(new OnBannerClickListener(){ @Override public void OnBannerClicked(int pos) { Toast.makeText(MainActivity.this, "OnBannerClickListener:" + pos,Toast.LENGTH_SHORT).show(); } }); int[] imagesSrc = new int[] { R.mipmap.img1, R.mipmap.img2, R.mipmap.img3, R.mipmap.img4, R.mipmap.img5 }; banner.update(imagesSrc);可以给Banner添加点击事件,然后传递图片id,调用banner的update()方法就可以了。
下面重点来看下BannerView:
public class BannerView extends FrameLayout{ private DotView mBannerDotView; private ViewPager mBannerViewPager; private BannerAdapter mBannerAdapter; /**当前的position*/ private int mBannerPosition = 0; /**Banner点击后的回调*/ private OnBannerClickListener mBannerClickListener; /**自动播放相关*/ private Handler mHandler = new Handler(); private Runnable task = new Runnable(){ @Override public void run() { mBannerPosition = (mBannerPosition + 1) % mBannerAdapter.getCount(); mBannerViewPager.setCurrentItem(mBannerPosition); Log.d(TAG, "tname:" + Thread.currentThread().getName()); mHandler.postDelayed(task, 3000); } }; private static final String TAG = BannerView.class.getSimpleName(); public BannerView(Context context) { this(context, null); } public BannerView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public BannerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } private void init(Context context) { } @Override protected void onFinishInflate() { super.onFinishInflate(); //注意这里的潜规则 mBannerViewPager = (ViewPager)getChildAt(0); mBannerDotView = (DotView)getChildAt(1); mBannerDotView.setDotNumber(0); mBannerAdapter = new BannerAdapter(getContext(), new int[0]); mBannerViewPager.setAdapter(mBannerAdapter); mBannerViewPager.setOnPageChangeListener(mBannerAdapter); } public void update(int[] imagesSrc){ if(imagesSrc == null || imagesSrc.length <= 0){ return; } mBannerDotView.setDotNumber(imagesSrc.length); mBannerDotView.setSelected(0); mBannerAdapter.update(imagesSrc); mHandler.postDelayed(task, 3000); } private void setIndicator(int position) { position %= mBannerAdapter.getSize(); mBannerDotView.setSelected(position); } private int mDownX; private int mDownY; private long mDownTime; @Override public boolean dispatchTouchEvent(MotionEvent event) { int action = event.getAction(); if (action == MotionEvent.ACTION_DOWN) { mHandler.removeCallbacks(task); mDownX = (int)event.getX(); mDownY = (int)event.getY(); mDownTime = System.currentTimeMillis(); } else if (action == MotionEvent.ACTION_UP) { if (System.currentTimeMillis() - mDownTime < 500 && Math.abs(mDownX - event.getX()) < 5 && Math.abs(mDownY - event.getY()) < 5) { // 接口回调 if (mBannerClickListener != null) { mBannerClickListener.OnBannerClicked(mBannerPosition%mBannerAdapter.getSize()); } } mHandler.postDelayed(task, 3000); } else if(action == MotionEvent.ACTION_CANCEL){ mHandler.postDelayed(task, 3000); } else if(action == MotionEvent.ACTION_MOVE){ // do nothing } return super.dispatchTouchEvent(event); } private class BannerAdapter extends PagerAdapter implements ViewPager.OnPageChangeListener { private Context mContext; private int[] mImagesSrc; private int mSize; private int mFakeSize; public BannerAdapter(Context context, int[] imagesSrc) { mContext = context; update(mImagesSrc); } public void update(int[] imagesSrc) { if(imagesSrc == null || imagesSrc.length <= 0){ return; } this.mImagesSrc = imagesSrc; this.mSize = imagesSrc.length; this.mFakeSize = mSize * 10; notifyDataSetChanged(); } @Override public int getCount() { return mFakeSize; } public int getSize() { return mSize; } @Override public boolean isViewFromObject(View view, Object o) { return view == o; } @Override public Object instantiateItem(ViewGroup container, int position) { position %= mSize; ImageView imageView = new ImageView(mContext); imageView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); imageView.setScaleType(ScaleType.CENTER_CROP); imageView.setImageResource(mImagesSrc[position]); container.addView(imageView); return imageView; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } @Override public void finishUpdate(ViewGroup container) { int position = mBannerViewPager.getCurrentItem(); Log.d(TAG, "finish update before, position=" + position); if (position == 0) { position = mSize; mBannerViewPager.setCurrentItem(position, false); } else if (position == getCount() - 1) { position = mSize - 1; mBannerViewPager.setCurrentItem(position, false); } Log.d(TAG, "finish update after, position=" + position); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { mBannerPosition = position; setIndicator(position); } @Override public void onPageScrollStateChanged(int state) { } } public interface OnBannerClickListener{ public void OnBannerClicked(int pos); } public void setOnBannerClickListener(OnBannerClickListener bannerClickListener) { this.mBannerClickListener = bannerClickListener; } @Override public void onDetachedFromWindow(){ mHandler.removeCallbacksAndMessages(null); this.removeAllViews(); this.mBannerClickListener = null; super.onDetachedFromWindow(); } }然后看下DotView:
public class DotView extends LinearLayout { private int mLittleDotWidth; private int mDotSpan = 36; private float mDotRadius = 6f; private int mDotNumber = 5; private int mCurrent = 0; private int mSelectedColor = 0xFF377BEE; private int mUnSelectedColor = 0xFFC5CEDB; public DotView(Context context) { super(context); } public DotView(Context context, AttributeSet attrs) { super(context, attrs); //获取自定义的属性 TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.DotView, 0, 0); if (arr != null) { if (arr.hasValue(R.styleable.DotView_dot_radius)) { mDotRadius = arr.getDimension(R.styleable.DotView_dot_radius, mDotRadius); } if (arr.hasValue(R.styleable.DotView_dot_span)) { mDotSpan = (int) arr.getDimension(R.styleable.DotView_dot_span, mDotSpan); } if(arr.hasValue(R.styleable.DotView_dot_number)){ mDotNumber = (int) arr.getInt(R.styleable.DotView_dot_number, mDotNumber); } mSelectedColor = arr.getColor(R.styleable.DotView_dot_selected_color, mSelectedColor); mUnSelectedColor = arr.getColor(R.styleable.DotView_dot_unselected_color, mUnSelectedColor); arr.recycle(); } mLittleDotWidth = (int) (mDotSpan / 2 + mDotRadius * 2); //把小点画出来 addDotViews(); } public void setDotNumber(int dotNumber){ this.mDotNumber = dotNumber; addDotViews(); } private void addDotViews(){ setGravity(Gravity.CENTER_HORIZONTAL); setOrientation(HORIZONTAL); removeAllViews(); for (int i = 0; i < mDotNumber; i++) { LittleDot dot = new LittleDot(getContext(), i); if (i == 0) { dot.setColor(mSelectedColor); } else { dot.setColor(mUnSelectedColor); } dot.setLayoutParams(new LayoutParams((int) mLittleDotWidth, (int) mDotRadius * 2, 1)); addView(dot); } } public final void setSelected(int index) { if (index >= getChildCount() || index < 0 || mCurrent == index) { return; } if (mCurrent < getChildCount() && mCurrent >= 0) { ((LittleDot) getChildAt(mCurrent)).setColor(mUnSelectedColor); } ((LittleDot) getChildAt(index)).setColor(mSelectedColor); mCurrent = index; } private class LittleDot extends View { private int mColor; private Paint mPaint; public LittleDot(Context context, int index) { super(context); mPaint = new Paint(); mPaint.setAntiAlias(true); } public void setColor(int color) { if (color == mColor){ return; } mColor = color; invalidate(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(mColor); canvas.drawCircle(mLittleDotWidth / 2, mDotRadius, mDotRadius, mPaint); } } public void setSelectedColor(int color) { if (mSelectedColor != color) { mSelectedColor = color; invalidate(); } } public void setUnSelectedColor(int color) { if (mUnSelectedColor != color) { mSelectedColor = color; invalidate(); } } public void setColor(int selectedColor, int unSelectedColor) { if (mSelectedColor != selectedColor || mUnSelectedColor != unSelectedColor) { mSelectedColor = selectedColor; mUnSelectedColor = unSelectedColor; invalidate(); } } }最后是attrs_dotview.xml:
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="DotView"> <attr name="dot_number" format="integer" /> <attr name="dot_radius" format="dimension" /> <attr name="dot_span" format="dimension" /> <attr name="dot_unselected_color" format="integer" /> <attr name="dot_selected_color" format="integer" /> </declare-styleable> </resources>
这个控件的优点是:使用起来非常简单,只需要把图片传递到BannerView的update方法就可以了,而且源码修改起来也非常简单。
另外,DotView的实现非常巧妙,非常值得借鉴!
我们不生产代码,我们只是代码的搬运工,感谢这些无私奉献的人:
BannerView参考:http://blog.csdn.net/singwhatiwanna/article/details/46541225
DotView参考:https://github.com/etao-open-source/cube-sdk/tree/master/core/src/in/srain/cube/views/banner
touch拦截:http://blog.csdn.net/wuseyukui/article/details/46627961