简介
UltraViewPager是一个封装多种特性的ViewPager,主要是为多页面切换场景提供统一解决方案。
主要功能
- 支持横向滑动/纵向滑动
- 支持一屏内显示多页
- 支持循环滚动
- 支持定时滚动,计时器使用Handler实现
- 支持设置ViewPager的最大宽高
- setRatio按比例显示UltraviewPager
- 内置indicator,只需简单设置几个属性就可以完成展示,支持圆点和Icon
- 内置两种页面切换动效
使用方法
版本请参考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" />
可以参考以下步骤使用UltraViewPager:
//绑定控件
UltraViewPager ultraViewPager = (UltraViewPager)findViewById(R.id.ultra_viewpager);
//设置滑动的方向
ultraViewPager.setScrollMode(UltraViewPager.ScrollMode.HORIZONTAL);
//UltraPagerAdapter 绑定子view到UltraViewPager
PagerAdapter adapter = new UltraPagerAdapter(false);
ultraViewPager.setAdapter(adapter);
//内置indicator初始化
ultraViewPager.initIndicator();
//设置indicator样式
ultraViewPager.getIndicator()
.setOrientation(UltraViewPager.Orientation.HORIZONTAL)
.setFocusColor(Color.GREEN) //设置选择时为绿色
.setNormalColor(Color.WHITE) //设置未选择时为白色
.setRadius((int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics()));//设置大小
//设置indicator对齐方式
ultraViewPager.getIndicator().setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
//构造indicator,绑定到UltraViewPager
ultraViewPager.getIndicator().build();
//设定页面循环播放
ultraViewPager.setInfiniteLoop(true);
//设定页面自动切换 间隔2秒
ultraViewPager.setAutoScroll(2000);
//取消页面自动切换
ultraViewPager.disableAutoScroll();
其他方法
//设定多银幕的显示比例 1f全占满整个横屏 0.6f合适
ultraViewPager.setMultiScreen(0.9f);
//设定比例 childHeigh =childWidth / itemRatio
//例: 1f时宽度=高度 0.8f时,高度略大于宽度
ultraViewPager.setItemRatio(0.8f);
//设定最大宽度和高度
ultraViewPager.setMaxHeight(100);
ultraViewPager.setMaxWidth(80);
//设置三种模式的滑动变压效果
ultraViewPager.setPageTransformer(false, new UltraScaleTransformer());
ultraViewPager.setPageTransformer(false, new UltraDepthScaleTransformer());
UltraVerticalTransformer //垂直不常用的
//设置不使用内置Indicator
ultraViewPager.disableIndicator();
源码分析
整个UItraViewPage的类如下
阿里出品的开源框架和开源的类中,个人比较看好的则是阿里的代码规范,见文知意,所以阅读他的源码很容易理解。
其中transformer包中是几个滑动显示的效果,内容比较简单,查看相关类就可以。
IUltraIndicatorBuilder接口
下面分析他的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();
}
IUltraViewPagerFeature接口
其中包含以下功能:
- 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);
}
UltraViewPagerIndicator 指示器View
首先他是一个自定义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
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
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
UltraViewPagerView继承自ViewPager
private void init(Context context, AttributeSet attrs) {
setClipChildren(false);
setOverScrollMode(OVER_SCROLL_NEVER); //设置不可以滚出这个视图
}