1.简介
- 平板模式使用的taskbar,我们改成了navBar,并且隐藏了hotseat,那么一些间距啥的需要调整
- 所以学下workspace的布局参数,布局在父类8.2
2.Workspace
2.1.容器结构
public class Workspace<T extends View & PageIndicator> extends PagedView<T>
PagedView,自定义的容器,参考小节8
public abstract class PagedView<T extends View & PageIndicator> extends ViewGroup {
2.2.bindAndInitFirstWorkspaceScreen
- launcher.java启动的时候会调用这个方法
public void bindAndInitFirstWorkspaceScreen() {
if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
//第一页不需要搜索框,那么不用初始化第一页
return;
}
//添加第一个页面,补充1
CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, getChildCount());
//这个是搜索框
if (mFirstPagePinnedItem == null) {
mFirstPagePinnedItem = LayoutInflater.from(getContext())
.inflate(R.layout.search_container_workspace, firstPage, false);
}
//搜索框的跨度
int cellHSpan = mLauncher.getDeviceProfile().inv.numSearchContainerColumns;
CellLayoutLayoutParams lp = new CellLayoutLayoutParams(0, 0, cellHSpan, 1);
lp.canReorder = false;
//把搜索框添加到CellLayout里,参考3.2
if (!firstPage.addViewToCellLayout(
mFirstPagePinnedItem, 0, R.id.search_container_workspace, lp, true)) {
mFirstPagePinnedItem = null;
}
}
>1.insertNewWorkspaceScreen
public CellLayout insertNewWorkspaceScreen(int screenId, int insertIndex) {
DeviceProfile dp = mLauncher.getDeviceProfile();
CellLayout newScreen;
if (FOLDABLE_SINGLE_PAGE.get() && dp.isTwoPanels) {
newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
R.layout.workspace_screen_foldable, this, false /* attachToRoot */);
} else {
newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
R.layout.workspace_screen, this, false /* attachToRoot */);
}
//放入map
mWorkspaceScreens.put(screenId, newScreen);
mScreenOrder.add(insertIndex, screenId);
//添加到容器里,也就是workspace里
addView(newScreen, insertIndex);
mStateTransitionAnimation.applyChildState(
mLauncher.getStateManager().getState(), newScreen, insertIndex);
updatePageScrollValues();
//补充2
updateCellLayoutPadding();
return newScreen;
}
>2.updateCellLayoutPadding
给所有的CellLayout设置padding
private void updateCellLayoutPadding() {
//数据参考4.1
Rect padding = mLauncher.getDeviceProfile().cellLayoutPaddingPx;
mWorkspaceScreens.forEach(
s -> s.setPadding(padding.left, padding.top, padding.right, padding.bottom));
}
2.3.insertNewWorkspaceScreenBeforeEmptyScreen
添加新的页面的时候走这里
public void insertNewWorkspaceScreenBeforeEmptyScreen(int screenId) {
//先获取要添加的索引
int insertIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
if (insertIndex < 0) {
insertIndex = mScreenOrder.size();
}
//参考2.2.1
insertNewWorkspaceScreen(screenId, insertIndex);
}
2.4.setInsets
- edgeMarginPx的值参考4.1.1,读取的是配置里的值
public void setInsets(Rect insets) {
DeviceProfile grid = mLauncher.getDeviceProfile();
mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
Rect padding = grid.workspacePadding;//数据参考4.1.2
setPadding(padding.left, padding.top, padding.right, padding.bottom);
mInsets.set(insets);
if (mWorkspaceFadeInAdjacentScreens) {
//横屏手机模式下(就是导航栏在两侧的),页面间距设置为默认值
setPageSpacing(grid.edgeMarginPx);//参考8.5
} else {
//insets的值参考4.1.2
int maxInsets = Math.max(insets.left, insets.right);
int maxPadding = Math.max(grid.edgeMarginPx, padding.left + 1);
setPageSpacing(Math.max(maxInsets, maxPadding));
}
updateCellLayoutPadding();
updateWorkspaceWidgetsSizes();
setPageIndicatorInset();
}
2.5.onLayout
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (mUnlockWallpaperFromDefaultPageOnLayout) {
mWallpaperOffset.setLockToDefaultPage(false);
mUnlockWallpaperFromDefaultPageOnLayout = false;
}
if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
mWallpaperOffset.syncWithScroll();
mWallpaperOffset.jumpToFinal();
}
super.onLayout(changed, left, top, right, bottom);
updatePageAlphaValues();//补充1
}
>1.updatePageAlphaValues
更新各个页面的透明度
private void updatePageAlphaValues() {
//
if (!workspaceInModalState() && !mIsSwitchingState && !mDragController.isDragging()) {
//已滚动的距离加上屏幕宽度的一半
int screenCenter = getScrollX() + getMeasuredWidth() / 2;
for (int i = 0; i < getChildCount(); i++) {
CellLayout child = (CellLayout) getChildAt(i);
if (child != null) {
float scrollProgress = getScrollProgress(screenCenter, child, i);
float alpha = 1 - Math.abs(scrollProgress);
if (mWorkspaceFadeInAdjacentScreens) {
child.getShortcutsAndWidgets().setAlpha(alpha);
} else {
// Pages that are off-screen aren't important for accessibility.
child.getShortcutsAndWidgets().setImportantForAccessibility(
alpha > 0 ? IMPORTANT_FOR_ACCESSIBILITY_AUTO
: IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
}
}
}
}
}
3.CellLayout
public class CellLayout extends ViewGroup {
3.1.构造方法
默认添加了一个容器ShortcutAndWidgetContainer,最终所有的图标都是添加到这个容器里的,参考3.2
//..
mShortcutsAndWidgets = new ShortcutAndWidgetContainer(context, mContainerType);
mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY,
mBorderSpace);
addView(mShortcutsAndWidgets);
}
3.2.addViewToCellLayout
public boolean addViewToCellLayout(View child, int index, int childId,
CellLayoutLayoutParams params, boolean markCells) {
final CellLayoutLayoutParams lp = params;
// Hotseat icons - remove text
if (child instanceof BubbleTextView) {
BubbleTextView bubbleChild = (BubbleTextView) child;
bubbleChild.setTextVisibility(mContainerType != HOTSEAT);
}
child.setScaleX(mChildScale);
child.setScaleY(mChildScale);
if (lp.getCellX() >= 0 && lp.getCellX() <= mCountX - 1
&& lp.getCellY() >= 0 && lp.getCellY() <= mCountY - 1) {
if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
child.setId(childId);
//可以看到,child都是添加到ShortcutAndWidgetContainer里的
mShortcutsAndWidgets.addView(child, index, lp);
if (markCells) markCellsAsOccupiedForView(child);
return true;
}
return false;
}
4.DeviceProfile.java
4.1.cellLayoutPaddingPx
public Rect cellLayoutPaddingPx = new Rect();
>1.构造方法
默认的padding,读取的是配置文件的值,
int cellLayoutPadding =
isTwoPanels ? cellLayoutBorderSpacePx.x / 2 : res.getDimensionPixelSize(
R.dimen.cell_layout_padding);
cellLayoutPaddingPx = new Rect(cellLayoutPadding, cellLayoutPadding, cellLayoutPadding,
cellLayoutPadding);
//..
edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
>2.updateWorkspacePadding
这里修改了cellLayoutPaddingPx的值
private void updateWorkspacePadding() {
Rect padding = workspacePadding;
if (isVerticalBarLayout()) {//横屏,导航栏在两侧的情况
padding.top = 0;
padding.bottom = edgeMarginPx;
if (isSeascape()) {
padding.left = hotseatBarSizePx;
padding.right = hotseatBarSidePaddingStartPx;
} else {
padding.left = hotseatBarSidePaddingStartPx;
padding.right = hotseatBarSizePx;
}
} else {
//正常平板,taskbar固定在底部的,会走这里
int paddingBottom = hotseatBarSizePx + workspaceBottomPadding
+ workspacePageIndicatorHeight - mWorkspacePageIndicatorOverlapWorkspace
- mInsets.bottom;
int paddingTop = workspaceTopPadding + (isScalableGrid ? 0 : edgeMarginPx);
int paddingSide = desiredWorkspaceHorizontalMarginPx;
padding.set(paddingSide, paddingTop, paddingSide, paddingBottom);
}
//这里修改了cellLayoutPaddingPx的值
insetPadding(workspacePadding, cellLayoutPaddingPx);
}
//用的是paddings和自己的最小值
private void insetPadding(Rect paddings, Rect insets) {
insets.left = Math.min(insets.left, paddings.left);
paddings.left -= insets.left;
insets.top = Math.min(insets.top, paddings.top);
paddings.top -= insets.top;
insets.right = Math.min(insets.right, paddings.right);
paddings.right -= insets.right;
insets.bottom = Math.min(insets.bottom, paddings.bottom);
paddings.bottom -= insets.bottom;
}
4.2.mInsets
>1.构造方法
mInsets.set(windowBounds.insets);
>2.updateInsets
public void updateInsets(Rect insets) {
mInsets.set(insets);
}
4.3.shouldFadeAdjacentWorkspaceScreens
是否应该淡出相邻的工作空间,参考补充代码,横屏手机模式下才为true
public boolean shouldFadeAdjacentWorkspaceScreens() {
return isVerticalBarLayout();
}
>1.isVerticalBarLayout
public boolean isVerticalBarLayout() {
return isLandscape && transposeLayoutWithOrientation;
}
>2.mTransposeLayoutWithOrientation
如果没有设置,那么非平板的时候为true
if (mTransposeLayoutWithOrientation == null) {
mTransposeLayoutWithOrientation = !mInfo.isTablet(mWindowBounds);
}
也可以设置setTransposeLayoutWithOrientation
public Builder setTransposeLayoutWithOrientation(boolean transposeLayoutWithOrientation) {
mTransposeLayoutWithOrientation = transposeLayoutWithOrientation;
return this;
}
5.LauncherRootView.java
这个是launcher的根容器,workspace,hotseat,allapps,recents等都是在这个容器里的
public class LauncherRootView extends InsettableFrameLayout {
5.1.onApplyWindowInsets
系统方法重写,应用insets
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mActivity.handleConfigurationChanged(mActivity.getResources().getConfiguration());
//参考6.1
insets = WindowManagerProxy.INSTANCE.get(getContext())
.normalizeWindowInsets(getContext(), insets, mTempRect);
handleSystemWindowInsets(mTempRect);
return insets;
}
>1.handleSystemWindowInsets
private void handleSystemWindowInsets(Rect insets) {
// 参考4.2.2
mActivity.getDeviceProfile().updateInsets(insets);
boolean resetState = !insets.equals(mInsets);
setInsets(insets);
if (resetState) {
mActivity.getStateManager().reapplyState(true /* cancelCurrentAnimation */);
}
}
>2.setInsets
public void setInsets(Rect insets) {
//发生变化的时候再处理
if (!insets.equals(mInsets)) {
super.setInsets(insets);//参考5.2.1
mSysUiScrim.onInsetsChanged(insets);
}
}
5.2.父类InsettableFrameLayout
public class InsettableFrameLayout extends FrameLayout implements Insettable {
>1.setInsets
循环所有的child,应用insets
public void setInsets(Rect insets) {
final int n = getChildCount();
for (int i = 0; i < n; i++) {
final View child = getChildAt(i);
//补充2
setFrameLayoutChildInsets(child, insets, mInsets);
}
mInsets.set(insets);
}
>2.setFrameLayoutChildInsets
public void setFrameLayoutChildInsets(View child, Rect newInsets, Rect oldInsets) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
//实现了这个接口的,直接调用对应的方法,比如2.4
if (child instanceof Insettable) {
((Insettable) child).setInsets(newInsets);
} else if (!lp.ignoreInsets) {
lp.topMargin += (newInsets.top - oldInsets.top);
lp.leftMargin += (newInsets.left - oldInsets.left);
lp.rightMargin += (newInsets.right - oldInsets.right);
lp.bottomMargin += (newInsets.bottom - oldInsets.bottom);
}
child.setLayoutParams(lp);
}
6.WindowManagerProxy.java
6.1.normalizeWindowInsets
public WindowInsets normalizeWindowInsets(Context context, WindowInsets oldInsets,
Rect outInsets) {
if (!Utilities.ATLEAST_R || !mTaskbarDrawnInProcess) {
//R也就是android11以下的
outInsets.set(oldInsets.getSystemWindowInsetLeft(), oldInsets.getSystemWindowInsetTop(),
oldInsets.getSystemWindowInsetRight(), oldInsets.getSystemWindowInsetBottom());
return oldInsets;
}
WindowInsets.Builder insetsBuilder = new WindowInsets.Builder(oldInsets);
//获取导航栏的inset
Insets navInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
Resources systemRes = context.getResources();
Configuration config = systemRes.getConfiguration();
boolean isTablet = config.smallestScreenWidthDp > MIN_TABLET_WIDTH;
boolean isGesture = isGestureNav(context);
boolean isPortrait = config.screenHeightDp > config.screenWidthDp;
int bottomNav = isTablet//平板模式的话为0
? 0
: (isPortrait
? getDimenByName(systemRes, NAVBAR_HEIGHT)//竖屏高度
: (isGesture
? getDimenByName(systemRes, NAVBAR_HEIGHT_LANDSCAPE)//手势模式
: 0));
//根据平板模式与否,手势导航与否,修改bottomNav的值
Insets newNavInsets = Insets.of(navInsets.left, navInsets.top, navInsets.right, bottomNav);
insetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
insetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(), newNavInsets);
//获取状态栏的inset
Insets statusBarInsets = oldInsets.getInsets(WindowInsets.Type.statusBars());
Insets newStatusBarInsets = Insets.of(
statusBarInsets.left,
getStatusBarHeight(context, isPortrait, statusBarInsets.top),//修正高度值,补充1
statusBarInsets.right,
statusBarInsets.bottom);
insetsBuilder.setInsets(WindowInsets.Type.statusBars(), newStatusBarInsets);
insetsBuilder.setInsetsIgnoringVisibility(
WindowInsets.Type.statusBars(), newStatusBarInsets);
//手势导航,inset的bottom为0
if (isGesture) {
Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
oldTappableInsets.right, 0);
insetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
}
WindowInsets result = insetsBuilder.build();
//systemBars包含状态栏,导航栏,以及tappableElement
Insets systemWindowInsets = result.getInsetsIgnoringVisibility(
WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
//设置最终的结果
outInsets.set(systemWindowInsets.left, systemWindowInsets.top, systemWindowInsets.right,
systemWindowInsets.bottom);
return result;
}
>1.getStatusBarHeight
protected int getStatusBarHeight(Context context, boolean isPortrait, int statusBarInset) {
Resources systemRes = context.getResources();
int statusBarHeight = getDimenByName(systemRes,
//横竖屏读取不同的配置
isPortrait ? STATUS_BAR_HEIGHT_PORTRAIT : STATUS_BAR_HEIGHT_LANDSCAPE,
STATUS_BAR_HEIGHT);
return Math.max(statusBarInset, statusBarHeight);
}
7.PagedOrientationHandler.java
有3种,竖屏是一种,横屏有两种
public interface PagedOrientationHandler {
//默认用的这个,recents列表页面才可能用其他的
PagedOrientationHandler PORTRAIT = new PortraitPagedViewHandler();
PagedOrientationHandler LANDSCAPE = new LandscapePagedViewHandler();
PagedOrientationHandler SEASCAPE = new SeascapePagedViewHandler();
7.1.PortraitPagedViewHandler
>1.getCenterForPage
public int getCenterForPage(View view, Rect insets) {
return (view.getPaddingTop() + view.getMeasuredHeight() + insets.top
- insets.bottom - view.getPaddingBottom()) / 2;
}
>2.getScrollOffsetStart
public int getScrollOffsetStart(View view, Rect insets) {
return insets.left + view.getPaddingLeft();
}
>3.getScrollOffsetEnd
public int getScrollOffsetEnd(View view, Rect insets) {
return view.getWidth() - view.getPaddingRight() - insets.right;
}
>4.getChildBounds
默认的位置:垂直居中,横向就是起始位置,竖向居中。
public ChildBounds getChildBounds(View child, int childStart, int pageCenter,
boolean layoutChild) {
final int childWidth = child.getMeasuredWidth();
final int childRight = childStart + childWidth;
final int childHeight = child.getMeasuredHeight();
final int childTop = pageCenter - childHeight / 2;
if (layoutChild) {
child.layout(childStart, childTop, childRight, childTop + childHeight);
}
return new ChildBounds(childWidth, childHeight, childRight, childTop);
}
>5.setPrimary
public <T> void setPrimary(T target, Int2DAction<T> action, int param) {
action.call(target, param, 0);
}
8.PagedView
8.1.onMeasure
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (getChildCount() == 0) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
if (widthSize <= 0 || heightSize <= 0) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
int myWidthSpec = MeasureSpec.makeMeasureSpec(
getPageWidthSize(widthSize), MeasureSpec.EXACTLY);//宽度参考补充1,
int myHeightSpec = MeasureSpec.makeMeasureSpec(
heightSize - mInsets.top - mInsets.bottom, MeasureSpec.EXACTLY);
//child的测量使用的宽高是减去了insets的值的。
measureChildren(myWidthSpec, myHeightSpec);
setMeasuredDimension(widthSize, heightSize);
}
>1.getPageWidthSize
- getPanelCount正常就是1,折叠屏可能是2.
private int getPageWidthSize(int widthSize) {
return (widthSize - mInsets.left - mInsets.right - getPaddingLeft() - getPaddingRight())
/ getPanelCount() + getPaddingLeft() + getPaddingRight();
}
8.2.onLayout
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
mIsLayoutValid = true;
final int childCount = getChildCount();
int[] pageScrolls = mPageScrolls;
boolean pageScrollChanged = false;
//补充1,
if (!pageScrollsInitialized()) {
//child个数变化了,重新设置数组
pageScrolls = new int[childCount];
pageScrollChanged = true;
}
//参考补充2
pageScrollChanged |= getPageScrolls(pageScrolls, true, SIMPLE_SCROLL_LOGIC);
mPageScrolls = pageScrolls;
if (childCount == 0) {
onPageScrollsInitialized();
return;
}
final LayoutTransition transition = getLayoutTransition();
//布局正在转换中,等待结束后再更新滚动值
if (transition != null && transition.isRunning()) {
transition.addTransitionListener(new LayoutTransition.TransitionListener() {
@Override
public void startTransition(LayoutTransition transition, ViewGroup container,
View view, int transitionType) { }
@Override
public void endTransition(LayoutTransition transition, ViewGroup container,
View view, int transitionType) {
if (!transition.isRunning()) {
transition.removeTransitionListener(this);
updateMinAndMaxScrollX();
}
}
});
} else {
updateMinAndMaxScrollX();//参考8.3
}
if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < childCount) {
//补充3
updateCurrentPageScroll();
mFirstLayout = false;
}
if (mScroller.isFinished() && pageScrollChanged) {
//参考8.4
setCurrentPage(getNextPage());//补充5
}
onPageScrollsInitialized();
}
>1.pageScrollsInitialized
看是否需要重新初始化,为空,或者数组长度和child个数不一样。
protected boolean pageScrollsInitialized() {
return mPageScrolls != null && mPageScrolls.length == getChildCount();
}
>2.getPageScrolls
获取所有page的滚动距离,类似线性布局,一个挨着一个往后排
protected boolean getPageScrolls(int[] outPageScrolls, boolean layoutChildren,
ComputePageScrollsLogic scrollLogic) {
final int childCount = getChildCount();
final int startIndex = mIsRtl ? childCount - 1 : 0;
final int endIndex = mIsRtl ? -1 : childCount;
final int delta = mIsRtl ? -1 : 1;
//参考7.1.1
final int pageCenter = mOrientationHandler.getCenterForPage(this, mInsets);
//容器左边的位置,除去insets和padding
final int scrollOffsetStart = mOrientationHandler.getScrollOffsetStart(this, mInsets);
//容器右边的位置,除去insets和padding
final int scrollOffsetEnd = mOrientationHandler.getScrollOffsetEnd(this, mInsets);
boolean pageScrollChanged = false;
int panelCount = getPanelCount();
for (int i = startIndex, childStart = scrollOffsetStart; i != endIndex; i += delta) {
final View child = getPageAt(i);
//可见性不是GONE的话就是true
if (scrollLogic.shouldIncludeView(child)) {
//参考7.1.4,获取尺寸并根据是否需要layout进行布局
ChildBounds bounds = mOrientationHandler.getChildBounds(child, childStart,
pageCenter, layoutChildren);
//宽
final int primaryDimension = bounds.primaryDimension;
//右侧位置
final int childPrimaryEnd = bounds.childPrimaryEnd;
//计算child的滚动距离,
final int pageScroll =
mIsRtl ? childPrimaryEnd - scrollOffsetEnd : childStart - scrollOffsetStart;
//记录滚动距离
if (outPageScrolls[i] != pageScroll) {
pageScrollChanged = true;
outPageScrolls[i] = pageScroll;
}
//计算下一个起始位置,初始位置 + child的宽
//getChildGap:默认是0,两个child之间额外需要添加的间距
childStart += primaryDimension + getChildGap(i, i + delta);
//非最后一页
int lastPanel = mIsRtl ? 0 : panelCount - 1;
if (i % panelCount == lastPanel) {
//两个child之间的间隔距离,数据来源参考2.4,子类设置的
childStart += mPageSpacing;
}
}
}
//不看,我们的panel是1
if (panelCount > 1) {
for (int i = 0; i < childCount; i++) {
// In case we have multiple panels, always use left most panel's page scroll for all
// panels on the screen.
int adjustedScroll = outPageScrolls[getLeftmostVisiblePageForIndex(i)];
if (outPageScrolls[i] != adjustedScroll) {
outPageScrolls[i] = adjustedScroll;
pageScrollChanged = true;
}
}
}
return pageScrollChanged;
}
>3.updateCurrentPageScroll
protected void updateCurrentPageScroll() {
//默认是0
int newPosition = 0;
if (0 <= mCurrentPage && mCurrentPage < getPageCount()) {
newPosition = getScrollForPage(mCurrentPage) + mCurrentPageScrollDiff;
}
//就是x移动newPosition的位置,就相当于this.scrollTo(newPosition,0)
mOrientationHandler.setPrimary(this, VIEW_SCROLL_TO, newPosition);
//同样设置Scroller的数据,
mScroller.startScroll(mScroller.getCurrX(), 0, newPosition - mScroller.getCurrX(), 0);
//补充5,立马结束滚动
forceFinishScroller();
}
变量
Int2DAction<View> VIEW_SCROLL_BY = View::scrollBy;
Int2DAction<View> VIEW_SCROLL_TO = View::scrollTo;
>4.forceFinishScroller
结束滚动
public void forceFinishScroller() {
mScroller.forceFinished(true);
mNextPage = INVALID_PAGE;
pageEndTransition();
}
>5.getNextPage
public int getNextPage() {
return (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
}
8.3.updateMinAndMaxScrollX
获取最小和最大的滚动距离
protected void updateMinAndMaxScrollX() {
mMinScroll = computeMinScroll();
mMaxScroll = computeMaxScroll();
}
>1.computeMinScroll
protected int computeMinScroll() {
return 0;
}
>2.computeMaxScroll
其实就是获取最后一个child的滚动数据
protected int computeMaxScroll() {
int childCount = getChildCount();
if (childCount > 0) {
final int index = mIsRtl ? 0 : childCount - 1;
return getScrollForPage(index);//补充3
} else {
return 0;
}
}
>3.getScrollForPage
8.2里给数组设置的数据,这里根据child的索引读取对应的值
public int getScrollForPage(int index) {
if (mPageScrolls == null || index >= mPageScrolls.length || index < 0) {
return 0;
} else {
return mPageScrolls[index];
}
}
8.4.setCurrentPage
public void setCurrentPage(int currentPage) {
setCurrentPage(currentPage, INVALID_PAGE);
}
public void setCurrentPage(int currentPage, int overridePrevPage) {
if (!mScroller.isFinished()) {
abortScrollerAnimation(true);
}
if (getChildCount() == 0) {
return;
}
int prevPage = overridePrevPage != INVALID_PAGE ? overridePrevPage : mCurrentPage;
mCurrentPage = validateNewPage(currentPage);
mCurrentScrollOverPage = mCurrentPage;
updateCurrentPageScroll();//8.2.3
notifyPageSwitchListener(prevPage);
invalidate();
}
8.5.setPageSpacing
父类方法
public void setPageSpacing(int pageSpacing) {
mPageSpacing = pageSpacing;
requestLayout();
}
mPageSpacing使用的地方参考补充1和2
>1.getPageScrolls
- mOrientationHandler用的是PortraitPagedViewHandler
protected boolean getPageScrolls(int[] outPageScrolls, boolean layoutChildren,
ComputePageScrollsLogic scrollLogic) {
final int childCount = getChildCount();
final int startIndex = mIsRtl ? childCount - 1 : 0;
final int endIndex = mIsRtl ? -1 : childCount;
final int delta = mIsRtl ? -1 : 1;
//参考小节7.1
final int pageCenter = mOrientationHandler.getCenterForPage(this, mInsets);
final int scrollOffsetStart = mOrientationHandler.getScrollOffsetStart(this, mInsets);
final int scrollOffsetEnd = mOrientationHandler.getScrollOffsetEnd(this, mInsets);
boolean pageScrollChanged = false;
int panelCount = getPanelCount();
for (int i = startIndex, childStart = scrollOffsetStart; i != endIndex; i += delta) {
final View child = getPageAt(i);
//child的可见性不是GONE的话就为true
if (scrollLogic.shouldIncludeView(child)) {
ChildBounds bounds = mOrientationHandler.getChildBounds(child, childStart,
pageCenter, layoutChildren);
final int primaryDimension = bounds.primaryDimension;
final int childPrimaryEnd = bounds.childPrimaryEnd;
// In case the pages are of different width, align the page to left edge for non-RTL
// or right edge for RTL.
final int pageScroll =
mIsRtl ? childPrimaryEnd - scrollOffsetEnd : childStart - scrollOffsetStart;
if (outPageScrolls[i] != pageScroll) {
pageScrollChanged = true;
outPageScrolls[i] = pageScroll;
}
childStart += primaryDimension + getChildGap(i, i + delta);
// 非最后一个,那么添加页面间距
int lastPanel = mIsRtl ? 0 : panelCount - 1;
if (i % panelCount == lastPanel) {
childStart += mPageSpacing;
}
}
}
if (panelCount > 1) {
for (int i = 0; i < childCount; i++) {
// In case we have multiple panels, always use left most panel's page scroll for all
// panels on the screen.
int adjustedScroll = outPageScrolls[getLeftmostVisiblePageForIndex(i)];
if (outPageScrolls[i] != adjustedScroll) {
outPageScrolls[i] = adjustedScroll;
pageScrollChanged = true;
}
}
}
return pageScrollChanged;
}
>2.getScrollProgress
protected float getScrollProgress(int screenCenter, View v, int page) {
final int halfScreenSize = getMeasuredWidth() / 2;
int delta = screenCenter - (getScrollForPage(page) + halfScreenSize);
int panelCount = getPanelCount();
int pageCount = getChildCount();
int adjacentPage = page + panelCount;
if ((delta < 0 && !mIsRtl) || (delta > 0 && mIsRtl)) {
adjacentPage = page - panelCount;
}
final int totalDistance;
if (adjacentPage < 0 || adjacentPage > pageCount - 1) {
totalDistance = (v.getMeasuredWidth() + mPageSpacing) * panelCount;
} else {
totalDistance = Math.abs(getScrollForPage(adjacentPage) - getScrollForPage(page));
}
float scrollProgress = delta / (totalDistance * 1.0f);
scrollProgress = Math.min(scrollProgress, MAX_SCROLL_PROGRESS);
scrollProgress = Math.max(scrollProgress, -MAX_SCROLL_PROGRESS);
return scrollProgress;
}