自动轮播滚动且一页显示多个Item的ViewPager广告栏
对于我这个新手来说,这个还是有点难度的,需求是实现一个自动轮播切一页显示多个Item的广告栏,因为这个项目是一个只开发平板横版的项目。上网找了好久,大神们的框架都没法设置
属性,就不能一页显示多个Item,并且图片的传入方式也不符合我们的需求,所以只好自己来仿写了。android:clipChildren="false"
首先自定义一个ViewPager,如下:
public class AZViewPager extends ViewPager { private static AZPagerAdapter mAZPagerAdapter; @IntDef({RESUME, PAUSE, DESTROY}) @Retention(RetentionPolicy.SOURCE) public @interface LifeCycle { } //外部可调用的生命周期常量 public static final int RESUME = 0; public static final int PAUSE = 1; public static final int DESTROY = 2; /** * 生命周期状态,保证{@link #mCarouselTimer}在各生命周期选择执行策略 */ private int mLifeCycle = RESUME; /** * 是否正在触摸状态,用以防止触摸滑动和自动轮播冲突 */ private boolean mIsTouching = false; /** * 轮播定时器 */ private ScheduledExecutorService mCarouselTimer; public AZViewPager(Context context) { super(context); } public AZViewPager(Context context, AttributeSet attrs) { super(context, attrs); } // 外部可调用的设置生命周期的方法 public void setLifeCycle(@LifeCycle int lifeCycle) { this.mLifeCycle = lifeCycle; } //触摸停止 @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: Log.i("ccc", "运行到触摸判断了"); mIsTouching = true; break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: mIsTouching = false; break; } return super.onTouchEvent(ev); } //触发轮播 @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); shutdownTimer(); mCarouselTimer = Executors.newSingleThreadScheduledExecutor(); mCarouselTimer.scheduleAtFixedRate(new Runnable() { @Override public void run() { switch (mLifeCycle) { case RESUME: if (!mIsTouching&& getAdapter() != null&& getAdapter().getCount() > 1) { post(new Runnable() { @Override public void run() { setCurrentItem(getCurrentItem() + 1); } }); } break; case PAUSE: break; case DESTROY: shutdownTimer(); break; } } }, 1000 * 3, 1000 * 3, TimeUnit.MILLISECONDS); } //停止轮播 @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); shutdownTimer(); } //停止 private void shutdownTimer() { if (mCarouselTimer != null && mCarouselTimer.isShutdown() == false) { mCarouselTimer.shutdown(); } mCarouselTimer = null; } }
再写一个Adapter,实现数据的加载,和监听的设置
public class AZPagerAdapter extends PagerAdapter { private AZViewPager mViewPager; private Context mContext; private List<Banner> bannerList = new ArrayList<>(); View view = null; public AZPagerAdapter(Context context, List<Banner> bannerlist, AZViewPager viewPager) { Log.i("aaa", "Adapter中的图案片资源" + bannerlist.size()); mContext = context; this.bannerList = bannerlist; this.mViewPager = viewPager; } //获得图片的个数 @IntRange(from = 0) public int getCountOfVisual() { int a = 0; if (bannerList != null || bannerList.size() != 0) { a = bannerList.size(); } else { a = 0; } return a; } //获得总Item数量 @Override public int getCount() { return Integer.MAX_VALUE; } @Override public boolean isViewFromObject(View view, Object object) { return view == (View) object; } @Override public Object instantiateItem(ViewGroup container, final int position) { view = LayoutInflater.from(mContext).inflate(R.layout.fragment_1, null); setLayouts(position); container.addView(view); return view; } private void setLayouts(int position) { Banner banner = bannerList.get(position%getCountOfVisual()); final ImageView imageView = (ImageView) view.findViewById(R.id.iv_advert_img); Glide.with(mContext).load(banner.getBannerLocalPath()).centerCrop().crossFade().into(imageView); Log.i("bbb", "拿到image集合" + Glide.with(mContext).load(banner.getBannerLocalPath()).centerCrop().crossFade().into(imageView)); } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } @Override public void setPrimaryItem(ViewGroup container, int position, Object object) { super.setPrimaryItem(container, position, object); } @Override public final void finishUpdate(ViewGroup container) { // 数量为1,不做position替换 if (getCount() <= 1) { return; } int position = mViewPager.getCurrentItem(); // ViewPager的更新即将完成,替换position,以达到无限循环的效果 if (position == 0) { position = getCountOfVisual(); mViewPager.setCurrentItem(position, false); } else if (position == getCount() - 1) { position = getCountOfVisual() - 1; mViewPager.setCurrentItem(position, false); } } }
自定义一个Indicator指示器:
其中需要在Drawable下新建shape_indicator_dot_default.xml和shape_indicator_dot_selected.xmlpublic class AZIndicatorView extends LinearLayout { private int mCount; private int mDotDefaultResId; private int mDotSelectedResId; private int mSelectedPosition; public AZIndicatorView(Context context) { super(context); init(null); } public AZIndicatorView(Context context, AttributeSet attrs) { super(context, attrs); init(attrs); } public AZIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public AZIndicatorView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(attrs); } private void init(AttributeSet attrs) { if (attrs != null) { TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.AZIndicatorView); mCount = typedArray.getInt(R.styleable.AZIndicatorView_dotCount, 0); mDotDefaultResId = typedArray.getResourceId(R.styleable.AZIndicatorView_dotDefault, R.drawable.shape_indicator_dot_default); mDotSelectedResId = typedArray.getResourceId(R.styleable.AZIndicatorView_dotSelected, R.drawable.shape_indicator_dot_selected); mSelectedPosition = typedArray.getInt(R.styleable.AZIndicatorView_dotSelectedPosition, 0); typedArray.recycle(); } setOrientation(HORIZONTAL); setGravity(Gravity.CENTER); initDots(); } private void initDots() { int dotMargin = (int) getResources().getDimension(R.dimen.dot_margin); for (int i = 0; i < mCount; i++) { ImageView dot = new ImageView(getContext()); dot.setBackgroundResource(i == mSelectedPosition ? mDotSelectedResId : mDotDefaultResId);// 设置引导页默认圆点 LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); if (i > 0) { params.leftMargin = dotMargin; } dot.setLayoutParams(params);// 设置圆点的大小 addView(dot); } } public void setSelectPosition(int selectPosition) { if (mCount > 0 && selectPosition > mCount - 1) { return; } this.mSelectedPosition = selectPosition; for (int i = 0; i < mCount; i++) { getChildAt(i).setBackgroundResource(i == selectPosition ? mDotSelectedResId : mDotDefaultResId); } } public void setCount(int count, int selectPosition) { this.mCount = count >= 0 ? count : 0; this.mSelectedPosition = selectPosition >= 0 ? selectPosition : 0; removeAllViews(); initDots(); } }
shape_indicator_dot_default.xml代码入下:shape_indicator_dot_selected.xml代码如下:<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="#4cffffff" /> <size android:width="@dimen/dot_h" android:height="@dimen/dot_h" /> </shape>
attrs配置文件下添加如下代码:<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="@android:color/white" /> <size android:width="@dimen/dot_h" android:height="@dimen/dot_h" /> </shape>
<declare-styleable name="AZIndicatorView"> <attr name="dotCount" format="integer" /> <attr name="dotDefault" format="reference" /> <attr name="dotSelected" format="reference" /> <attr name="dotSelectedPosition" format="integer" /> </declare-styleable>
dimens文件下添加如下代码:写了这么多,准备工作终于结束了,现在开始写布局文件。<dimen name="dot_margin">4dp</dimen> <dimen name="dot_h">8dp</dimen>
主XML文件
<RelativeLayout android:id="@+id/rl_advert" android:layout_width="match_parent" android:layout_height="145dp" android:layout_below="@+id/rl_pao_ma_deng" android:layout_marginTop="14dp" android:clipChildren="false" > <game.com.mygameboxapp.tools.AZViewPager android:id="@+id/vp_advert" android:layout_width="585dp" android:layout_height="145dp" android:layout_centerHorizontal="true" android:layout_marginLeft="-400dp" android:clipChildren="false" ></game.com.mygameboxapp.tools.AZViewPager> <game.com.mygameboxapp.tools.AZIndicatorView android:id="@+id/banner_indicator" android:layout_width="match_parent" android:layout_height="15dp" android:layout_alignBottom="@id/vp_advert" android:layout_centerHorizontal="true" android:layout_alignParentBottom="true"/> </RelativeLayout>
Item的Xml文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:layout_marginLeft="50dp" android:layout_marginRight="50dp"> <ImageView android:id="@+id/iv_advert_img" android:layout_width="585dp" android:layout_height="145dp" android:background="#00ffffff" android:paddingLeft="2dp" android:paddingRight="2dp" /> </LinearLayout>
MainActivity中代码如下。private AZViewPager bannerViewPager; private AZIndicatorView bannerIndicatorView; private AZPagerAdapter bannerPagerAdapter; DBManager dbManager = new DBManager(getBaseContext()); List<Banner> bannerList = dbManager.queryBanner(); bannerPagerAdapter = new AZPagerAdapter(MainActivity.this, bannerList, bannerViewPager); bannerViewPager.setAdapter(bannerPagerAdapter); bannerIndicatorView.setCount(bannerList.size(), 0); //监听 //指示器监听 bannerViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { super.onPageSelected(position); bannerIndicatorView.setSelectPosition(position % bannerPagerAdapter.getCountOfVisual()); } }); //轮播点击事件 bannerViewPager.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return false; } }); }
@Override protected void onResume() { super.onResume(); bannerViewPager.setLifeCycle(AZViewPager.RESUME); } @Override protected void onPause() { super.onPause(); bannerViewPager.setLifeCycle(AZViewPager.PAUSE); } @Override protected void onDestroy() { super.onDestroy(); bannerViewPager.setLifeCycle(AZViewPager.DESTROY); }
其中用到的实体类是这样。public class Banner { public String bannerUrl; public String bannerId; public String bannerLocalPath; public String getBannerId() { return bannerId; } public void setBannerId(String bannerId) { this.bannerId = bannerId; } public String getBannerLocalPath() { return bannerLocalPath; } public void setBannerLocalPath(String bannerLocalPath) { this.bannerLocalPath = bannerLocalPath; } public String getBannerUrl() { return bannerUrl; } public void setBannerUrl(String bannerUrl) { this.bannerUrl = bannerUrl; } }
好了,就是这样了。此外,还为此ViewPager添加了调整速度和预加载功能。
下面是速度调节的类
public class ViewPagerScroller extends Scroller { private int mScrollDuration = 0; // 滑动速度 /** * 设置速度速度 * * @param duration */ public void setScrollDuration(int duration) { this.mScrollDuration = duration; } public ViewPagerScroller(Context context) { super(context); } public ViewPagerScroller(Context context, Interpolator interpolator) { super(context, interpolator); } public ViewPagerScroller(Context context, Interpolator interpolator, boolean flywheel) { super(context, interpolator, flywheel); } @Override public void startScroll(int startX, int startY, int dx, int dy, int duration) { super.startScroll(startX, startY, dx, dy, mScrollDuration); } @Override public void startScroll(int startX, int startY, int dx, int dy) { super.startScroll(startX, startY, dx, dy, mScrollDuration); } public void initViewPagerScroll(ViewPager viewPager) { try { Field mScroller = ViewPager.class.getDeclaredField("mScroller"); mScroller.setAccessible(true); mScroller.set(viewPager, this); } catch (Exception e) { e.printStackTrace(); } } }
在Mainactivity中添加如下代码实现速度调节和预加载。
private ViewPagerScroller bannerViewPagerScroller;
bannerViewPagerScroller = new ViewPagerScroller(this);
bannerViewPager.setOffscreenPageLimit(bannerList.size());//设置预加载数量 bannerViewPagerScroller.setScrollDuration(1500);//设置切换速度 bannerViewPagerScroller.initViewPagerScroll(bannerViewPager);