UltraViewPager是一个封装多种特性的ViewPager,主要是为多页面切换场景提供统一解决方案。
版本请参考mvn repository上的最新版本(目前最新版本是1.0.4),最新的 aar 都会发布到 jcenter 和 MavenCentral 上,确保配置了这两个仓库源,然后引入aar依赖:
//gradle compile ('com.alibaba.android:ultraviewpager:1.0.4@aar') { transitive = true }或者maven
//pom.xml in maven <dependency> <groupId>com.alibaba.android</groupId> <artifactId>ultraviewpager</artifactId> <version>1.0.4</version> <type>aar</type> </dependency>在layout中使用UltraViewPager: activity_pager.xml
<com.tmall.ultraviewpager.UltraViewPager android:id="@+id/ultra_viewpager" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" android:background="@android:color/darker_gray" />整个UItraViewPage的类如下
阿里出品的开源框架和开源的类中,个人比较看好的则是阿里的代码规范,见文知意,所以阅读他的源码很容易理解。
其中transformer包中是几个滑动显示的效果,内容比较简单,查看相关类就可以。
下面分析他的IUltraIndicatorBuilder接口 在这个接口中给出了如下的方法:
设置选中/未选中状态下的指示器颜色 设置边距、圆角半径、方向和位置提供一个构造方法 public interface IUltraIndicatorBuilder { /** * Set focused color for indicator. * 设置指示器的颜色 * @param focusColor * @return */ IUltraIndicatorBuilder setFocusColor(int focusColor); /** * Set normal color for indicator. * 设置未选择时指示器的颜色 * @param normalColor * @return */ IUltraIndicatorBuilder setNormalColor(int normalColor); /** * Set stroke color for indicator. * 设置指示器表面颜色 * @param strokeColor * @return */ IUltraIndicatorBuilder setStrokeColor(int strokeColor); /** * Set stroke width for indicator. * 设置指示器的宽度 * @param strokeWidth * @return */ IUltraIndicatorBuilder setStrokeWidth(int strokeWidth); /** * Set spacing between indicator item ,the default value is item's height. * 设置间距指标项,默认值是项的高度。 * @param indicatorPadding * @return */ IUltraIndicatorBuilder setIndicatorPadding(int indicatorPadding); /** * Set the corner radius of the indicator item. * 设置指示器的圆角半径。 * @param radius * @return */ IUltraIndicatorBuilder setRadius(int radius); /** * Sets the orientation of the layout. * 设置布局的方向 * @param orientation * @return */ IUltraIndicatorBuilder setOrientation(UltraViewPager.Orientation orientation); /** * Set the location at which the indicator should appear on the screen. * 设置指示器应该出现在屏幕上的位置。 * @param gravity android.view.Gravity * @return */ IUltraIndicatorBuilder setGravity(int gravity); /** * Set focused resource ID for indicator. * 为指示器设置选中状态下的资源ID。 * @param focusResId * @return */ IUltraIndicatorBuilder setFocusResId(int focusResId); /** * Set normal resource ID for indicator. * 为指示器设置未选中状态下的资源ID。 * @param normalResId * @return */ IUltraIndicatorBuilder setNormalResId(int normalResId); /** * Set focused icon for indicator. * 为指示器设置聚焦图标。 * @param bitmap * @return */ IUltraIndicatorBuilder setFocusIcon(Bitmap bitmap); /** * Set normal icon for indicator. * 为指示器设置未选中的图标。 * @param bitmap * @return */ IUltraIndicatorBuilder setNormalIcon(Bitmap bitmap); /** * Set margins for indicator. * 为指示器设置边距 * @param left the left margin in pixels * @param top the top margin in pixels * @param right the right margin in pixels * @param bottom the bottom margin in pixels * @return */ IUltraIndicatorBuilder setMargin(int left, int top, int right, int bottom); /** * Combine all of the options that have been set and return a new IUltraIndicatorBuilder object. * 将所有已设置的选项组合在一起并返回 IUltraIndicatorBuilder */ void build(); }其中包含以下功能:
5种初始化的方法移除指示器启动/禁用自动滑动模式设置无限循环模式为viewpage提供最大的宽度/高度设置纵横比设置滚动模式禁用滚动方向滚动到下一页,当到达最后一个页面时返回第一个页面设置多屏幕模式将子view高度自动调整到ViewPager的高度根据纵横比调整子view的高度使用像素设置两个页面之间的空隙设置item边距包括上下左右和左右 interface IUltraViewPagerFeature { /** * Constructs a indicator with no options. this indicator support set-Method in chained mode. * 构造一个没有选项的指示器。这个指示器支持固定模式下的方法。 * meanwhile focusColor and normalColor are necessary,or the indicator won't be show. * 与此同时,焦点颜色和正常颜色是必要的,否则指示器就不会显示出来。 * */ IUltraIndicatorBuilder initIndicator(); /** * Set options for indicator * 指示器的设置选项 * * @param focusColor defines the color when indicator is focused. * @param normalColor defines the color when indicator is in the default state (not focused). * @param radiusInPixel defines the radius of indicator item. * @param gravity specifies how to align the indicator. for example, using Gravity.BOTTOM | Gravity.RIGHT */ IUltraIndicatorBuilder initIndicator(int focusColor, int normalColor, int radiusInPixel, int gravity); /** * Set options for indicator * * @param focusColor defines the color when indicator is focused. * @param normalColor defines the color when indicator is in the default state (not focused). * @param strokeColor stroke color * @param strokeWidth stroke width * @param radiusInPixel the radius of indicator item. * @param gravity specifies how to align the indicator. for example, using Gravity.BOTTOM | Gravity.RIGHT */ IUltraIndicatorBuilder initIndicator(int focusColor, int normalColor, int strokeColor, int strokeWidth, int radiusInPixel, int gravity); /** * Set options for indicator * * @param focusResId defines the resource id when indicator is focused. * @param normalResId defines the resource id when indicator is in the default state (not focused). * @param gravity specifies how to align the indicator. for example, using Gravity.BOTTOM | Gravity.RIGHT */ IUltraIndicatorBuilder initIndicator(int focusResId, int normalResId, int gravity); /** * @param focusBitmap defines the bitmap when indicator is focused * @param normalBitmap defines the bitmap when indicator is in the default state (not focused). * @param gravity specifies how to align the indicator. for example, using Gravity.BOTTOM | Gravity.RIGHT * @return */ IUltraIndicatorBuilder initIndicator(Bitmap focusBitmap, Bitmap normalBitmap, int gravity); /** * Remove indicator * 移除指示器 */ void disableIndicator(); /** * Enable auto-scroll mode * 启动自动滑动模式 * * @param intervalInMillis The interval time to scroll in milliseconds. * 以毫秒为轴滚动的间隔时间。 */ void setAutoScroll(int intervalInMillis); /** * Disable auto-scroll mode * 禁用自动滑动模式 * */ void disableAutoScroll(); /** * Set an infinite loop * 设置一个无限循环 * * @param enable enable or disable */ void setInfiniteLoop(boolean enable); /** * Supply a maximum width for this ViewPager. * 为viewpager提供最大宽度。 * * @param width width */ void setMaxWidth(int width); /** * Supply a maximum height for this ViewPager. * 为viewpager提供最大高度。 * * @param height height */ void setMaxHeight(int height); /** * Set the aspect ratio for UltraViewPager. * 设置纵横比。 * * @param ratio */ void setRatio(float ratio); /** * Set scroll mode for UltraViewPager. * 设置滚动模式 * * @param scrollMode UltraViewPager.ScrollMode.HORIZONTAL or UltraViewPager.ScrollMode.VERTICAL */ void setScrollMode(UltraViewPager.ScrollMode scrollMode); /** * Disable scroll direction. the default value is ScrollDirection.NONE * 禁用滚动方向 默认是ScrollDirection.NONE * * @param direction NONE, BACKWARD, FORWARD */ void disableScrollDirection(UltraViewPager.ScrollDirection direction); /** * Scroll to the next page, and return to the first page when the last page is reached. * 滚动到下一页,当到达最后一个页面时返回到第一个页面。 */ void scrollNextPage(); /** * Set multi-screen mode , the aspect ratio of PageViewer should less than or equal to 1.0f * 设置多屏幕模式,页面查看器的纵横比应该小于或等于1.0 f。 * */ void setMultiScreen(float ratio); /** * Adjust the height of the ViewPager to the height of child automatically. * 将子view高度自动调整到ViewPager的高度 */ void setAutoMeasureHeight(boolean status); /** * Adjust the height of child item view with aspect ratio. * 用纵横比调整子view的高度。 * * @param ratio aspect ratio */ void setItemRatio(double ratio); /** * Set the gap between two pages in pixel * 使用像素设置两个页面之间的空隙 * * @param pixel */ void setHGap(int pixel); /** * Set item margin * * @param left the left margin in pixels * @param top the top margin in pixels * @param right the right margin in pixels * @param bottom the bottom margin in pixels */ void setItemMargin(int left, int top, int right, int bottom); /** * Set margins for this ViewPager * * @param left the left margin in pixels * @param right the right margin in pixels */ void setScrollMargin(int left, int right); /** * The items.size() would be scale to item.size()*infiniteRatio in fact * size()将被扩展到item.size()无限迭代???? * * @param infiniteRatio */ void setInfiniteRatio(int infiniteRatio); }首先他是一个自定义View继承View实现了Viewpager监听的接口和IUltraIndicatorBuilder接口 首先在他的三个构造方法中添加了init() 初始化方法
public UltraViewPagerIndicator(Context context) { super(context); init(); } public UltraViewPagerIndicator(Context context, AttributeSet attrs) { super(context, attrs); init(); } public UltraViewPagerIndicator(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); }init();方法中初始化了两个画笔和默认指示器半径的代码
private void init() { paintStroke = new Paint(Paint.ANTI_ALIAS_FLAG); //负责画空心 paintStroke.setStyle(Paint.Style.STROKE); paintFill = new Paint(Paint.ANTI_ALIAS_FLAG); //负责画实心 paintFill.setStyle(Paint.Style.FILL); //默认的半径 defaultRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_RADIUS, getResources().getDisplayMetrics()); }关键方法来了onDraw(),代码如下,逻辑也是很清晰了
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (viewPager == null || viewPager.getAdapter() == null) return; //获得viewpager页面的的数量 final int count = ((UltraViewPagerAdapter) viewPager.getAdapter()).getRealCount(); if (count == 0) return; int longSize; int shortSize; int longPaddingBefore; int longPaddingAfter; int shortPaddingBefore; int shortPaddingAfter; //判断如果是水平方向的指示器 if (orientation == UltraViewPager.Orientation.HORIZONTAL) { //由于是水平方向上的所以longSize为宽 shortSize为高 longSize = viewPager.getWidth(); shortSize = viewPager.getHeight(); longPaddingBefore = getPaddingLeft() + marginLeft; longPaddingAfter = getPaddingRight() + marginRight; shortPaddingBefore = getPaddingTop() + marginTop; shortPaddingAfter = (int) paintStroke.getStrokeWidth() + getPaddingBottom() + marginBottom; } else { longSize = viewPager.getHeight(); shortSize = viewPager.getWidth(); longPaddingBefore = getPaddingTop() + marginTop; longPaddingAfter = (int) paintStroke.getStrokeWidth() + getPaddingBottom() + marginBottom; shortPaddingBefore = getPaddingLeft() + marginLeft; shortPaddingAfter = getPaddingRight() + marginRight; } final float itemWidth = getItemWidth(); final int widthRatio = isDrawResIndicator() ? 1 : 2; //bitmap resource X1 : circle X2 if (indicatorPadding == 0) { indicatorPadding = (int) itemWidth; } float shortOffset = shortPaddingBefore; float longOffset = longPaddingBefore; final float indicatorLength = (count - 1) * (itemWidth * widthRatio + indicatorPadding); final int horizontalGravityMask = gravity & Gravity.HORIZONTAL_GRAVITY_MASK; final int verticalGravityMask = gravity & Gravity.VERTICAL_GRAVITY_MASK; switch (horizontalGravityMask) { case Gravity.CENTER_HORIZONTAL: longOffset = (longSize - longPaddingBefore - longPaddingAfter - indicatorLength) / 2.0f; break; case Gravity.RIGHT: if (orientation == UltraViewPager.Orientation.HORIZONTAL) { longOffset = longSize - longPaddingAfter - indicatorLength - itemWidth; } if (orientation == UltraViewPager.Orientation.VERTICAL) { shortOffset = shortSize - shortPaddingAfter - itemWidth; } break; case Gravity.LEFT: longOffset += itemWidth; default: break; } switch (verticalGravityMask) { case Gravity.CENTER_VERTICAL: shortOffset = (shortSize - shortPaddingAfter - shortPaddingBefore - itemWidth) / 2; break; case Gravity.BOTTOM: if (orientation == UltraViewPager.Orientation.HORIZONTAL) { shortOffset = shortSize - shortPaddingAfter - getItemHeight(); } if (orientation == UltraViewPager.Orientation.VERTICAL) { longOffset = longSize - longPaddingAfter - indicatorLength; } break; case Gravity.TOP: shortOffset += itemWidth; default: break; } if (horizontalGravityMask == Gravity.CENTER_HORIZONTAL && verticalGravityMask == Gravity.CENTER_VERTICAL) { shortOffset = (shortSize - shortPaddingAfter - shortPaddingBefore - itemWidth) / 2; } float dX; float dY; float pageFillRadius = radius; if (paintStroke.getStrokeWidth() > 0) { pageFillRadius -= paintStroke.getStrokeWidth() / 2.0f; //TODO may not/2 } //Draw stroked circles for (int iLoop = 0; iLoop < count; iLoop++) { float drawLong = longOffset + (iLoop * (itemWidth * widthRatio + indicatorPadding)); if (orientation == UltraViewPager.Orientation.HORIZONTAL) { dX = drawLong; dY = shortOffset; } else { dX = shortOffset; dY = drawLong; } if (isDrawResIndicator()) { if (iLoop == viewPager.getCurrentItem()) continue; canvas.drawBitmap(normalBitmap, dX, dY, paintFill); } else { // Only paint fill if not completely transparent if (paintFill.getAlpha() > 0) { paintFill.setColor(normalColor); canvas.drawCircle(dX, dY, pageFillRadius, paintFill); } // Only paint stroke if a stroke width was non-zero if (pageFillRadius != radius) { canvas.drawCircle(dX, dY, radius, paintStroke); } } } //Draw the filled circle according to the current scroll float cx = (viewPager.getCurrentItem()) * (itemWidth * widthRatio + indicatorPadding); if (animateIndicator) cx += pageOffset * itemWidth; if (orientation == UltraViewPager.Orientation.HORIZONTAL) { dX = longOffset + cx; dY = shortOffset; } else { dX = shortOffset; dY = longOffset + cx; } if (isDrawResIndicator()) { canvas.drawBitmap(focusBitmap, dX, dY, paintStroke); } else { paintFill.setColor(focusColor); canvas.drawCircle(dX, dY, radius, paintFill); } }UltraViewPagerAdapter继承了PagerAdapter
//根据是否是无线循环的来返回当前的count @Override public int getCount() { int count; if (enableLoop) { if (adapter.getCount() == 0) { count = 0; } else { count = adapter.getCount() * infiniteRatio; } } else { count = adapter.getCount(); } return count; } @Override //此方法是实例化一个对象 position是要实例化的位置 public Object instantiateItem(ViewGroup container, int position) { int realPosition = position; //TODO if (enableLoop && adapter.getCount() != 0) { realPosition = position % adapter.getCount(); } Object item = adapter.instantiateItem(container, realPosition); //TODO View childView = null; if (item instanceof View) childView = (View) item; if (item instanceof RecyclerView.ViewHolder) childView = ((RecyclerView.ViewHolder) item).itemView; ViewPager viewPager = (ViewPager) container; int childCount = viewPager.getChildCount(); for (int i = 0; i < childCount; i++) { View child = viewPager.getChildAt(i); if (isViewFromObject(child, item)) { //判断是否已经关联上了 viewArray.put(realPosition, child); break; } } if (isEnableMultiScr()) { if (scrWidth == 0) { scrWidth = container.getResources().getDisplayMetrics().widthPixels; } RelativeLayout relativeLayout = new RelativeLayout(container.getContext()); if (childView.getLayoutParams() != null) { RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams( (int) (scrWidth * multiScrRatio), ViewGroup.LayoutParams.MATCH_PARENT); layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); childView.setLayoutParams(layoutParams); } container.removeView(childView); relativeLayout.addView(childView); container.addView(relativeLayout); return relativeLayout; } return item; }UltraViewPager继承了RelativeLayout 实现了IUltraViewPagerFeature的所有方法,也是在XML文件中添加控件的View
开始是依旧初始化画笔和初始化VIEW
public UltraViewPager(Context context) { super(context); size = new Point(); maxSize = new Point(); initView(); } public UltraViewPager(Context context, AttributeSet attrs) { super(context, attrs); size = new Point(); maxSize = new Point(); initView(); initView(context, attrs); } public UltraViewPager(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); size = new Point(); maxSize = new Point(); initView(); }初始化方法根据版本来设置viewpager的id的不同方法
private void initView() { viewPager = new UltraViewPagerView(getContext()); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { viewPager.setId(viewPager.hashCode()); } else { viewPager.setId(View.generateViewId()); } addView(viewPager, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); }UltraViewPagerView继承自ViewPager
private void init(Context context, AttributeSet attrs) { setClipChildren(false); setOverScrollMode(OVER_SCROLL_NEVER); //设置不可以滚出这个视图 }