Launcher3源码分析(Workspace)

    xiaoxiao2021-03-25  229

    Workspace主要功能:完成多个屏幕的以及壁纸的显示,多个屏幕之间的切换和壁纸的添加。

    /** * Used to inflate the Workspace from XML. */ public Workspace(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); //获取绘制轮廓的辅助类对象 mOutlineHelper = HolographicOutlineHelper.obtain(context); mLauncher = (Launcher) context; mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this); final Resources res = getResources(); DeviceProfile grid = mLauncher.getDeviceProfile(); mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens(); mFadeInAdjacentScreens = false; //获取Wallpaper管理器 mWallpaperManager = WallpaperManager.getInstance(context); //获取自定义属性 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Workspace, defStyle, 0); //在allapp 应用程序菜单里拖动app时workspace的缩放比例 mSpringLoadedShrinkFactor = res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f; //长按桌面进入预览模式时的缩放比例 mOverviewModeShrinkFactor = grid.getOverviewModeScale(mIsRtl); //开机时的屏幕 mOriginalDefaultPage = mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1); a.recycle(); //监听view层次的变化 setOnHierarchyChangeListener(this); //打开触摸反馈 setHapticFeedbackEnabled(false); //初始化workspace initWorkspace(); // Disable multitouch across the workspace/all apps/customize tray setMotionEventSplittingEnabled(true); }

    初始化workspace

    /** * Initializes various states for this workspace. */ protected void initWorkspace() { //默认页 mCurrentPage = mDefaultPage; LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = mLauncher.getDeviceProfile(); //保存应用图片的缓存 mIconCache = app.getIconCache(); setWillNotDraw(false); setClipChildren(false); setClipToPadding(false); //设置子view绘图缓存开始 setChildrenDrawnWithCacheEnabled(true); setMinScale(mOverviewModeShrinkFactor); setupLayoutTransition(); //wallpaper偏移 mWallpaperOffset = new WallpaperOffsetInterpolator(); //获取屏幕大小 Display display = mLauncher.getWindowManager().getDefaultDisplay(); display.getSize(mDisplaySize); mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx); // Set the wallpaper dimensions when Launcher starts up setWallpaperDimension(); setEdgeGlowColor(getResources().getColor(R.color.workspace_edge_effect_color)); }

    workspace实现了DragSource和Dragtarget,说明它即是一个拖动的容器也是一个拖动的源,分析开始拖动方法:

    public void startDrag(CellLayout.CellInfo cellInfo){ startDrag(cellInfo,false); } public void startDrag(CellLayout.CellInfo cellInfo,boolean accessible){ View child = cellInfo.cell; // Make sure the drag was started by a long press as opposed to a long click. if (!child.isInTouchMode()) { return; } mDragInfo = cellInfo; //原位置的item设置为不可见 child.setVisibility(INVISIBLE); CellLayout layout = (CellLayout) child.getParent().getParent(); layout.prepareChildForDrag(child); beginDragShared(child, this, accessible); } public void beginDragShared(View child, DragSource source,boolean accessible){ beginDragShared(child, new Point(), source, accessible); } public void beginDragShared(View child, Point relativeTouchPos,DragSource source, boolean accessible){ child.clearFocus(); child.setPressed(false); //当item拖动时跟随着的背景图 mDragOutline = createDragOutline(child,DRAG_BITMAP_PADDING); //开始拖动 mLauncher.onDragStarted(child); // The drag bitmap follows the touch point around on the screen AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING); final Bitmap b = createDragBitmap(child,padding); float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY); int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2); int dragLayerY = Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2 - padding.get() / 2); DeviceProfile grid = mLauncher.getDeviceProfile(); Point dragVisualizeOffset = null; Rect dragRect = null; if (child instanceof BubbleTextView) { BubbleTextView icon = (BubbleTextView) child; int iconSize = grid.iconSizePx; int top = child.getPaddingTop(); int left = (bmpWidth - iconSize) / 2; int right = left + iconSize; int bottom = top + iconSize; if (icon.isLayoutHorizontal()) { // If the layout is horizontal, then if we are just picking up the icon, then just // use the child position since the icon is top-left aligned. Otherwise, offset // the drag layer position horizontally so that the icon is under the current // touch position. if (icon.getIcon().getBounds().contains(relativeTouchPos.x, relativeTouchPos.y)) { dragLayerX = Math.round(mTempXY[0]); } else { dragLayerX = Math.round(mTempXY[0] + relativeTouchPos.x - (bmpWidth / 2)); } } dragLayerY += top; // Note: The drag region is used to calculate drag layer offsets, but the // dragVisualizeOffset in addition to the dragRect (the size) to position the outline. dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2); dragRect = new Rect(left, top, right, bottom); } else if (child instanceof FolderIcon) { int previewSize = grid.folderIconSizePx; dragRect = new Rect(0, child.getPaddingTop(), child.getWidth(), previewSize); } // Clear the pressed state if necessary if (child instanceof BubbleTextView) { BubbleTextView icon = (BubbleTextView) child; icon.clearPressedBackground(); } if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) { String msg = "Drag started with a view that has no tag set. This " + "will cause a crash (issue 11627249) down the line. " + "View: " + child + " tag: " + child.getTag(); throw new IllegalStateException(msg); } //调用DragController的startDrag方法 DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(), DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale, accessible); dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor()); if (child.getParent() instanceof ShortcutAndWidgetContainer) { mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent(); } b.recycle(); }

    workspace主要负责左右滑动,滑动功能主要分两步:1、在onInterceptTouchEvent中进行拦截。2、在onTouchEvent中进行滑动

    public boolean onTouch(View v, MotionEvent event){ return (worspaceInModalSate() || !isFinishedSwitchingState()) || (!workspaceInModalState() && indexofChild(V) != mCurrentPage); } public boolean onInterceptTounchEvent(MotionEvent ev){ switch(ev.getAction() & MptionEvent.ACTION_MASK){ case MotionEvcent.ACTION_DOWN: mXDown = ev.getX(); mYDown = ev.getY(); //记录按下的时间 mTouchDownTime = system.currentTimeMillis(); break; case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_up: if(mTouvhState == TOUCH_STATE_RESET){ final CellLayout currentPage == (CellLayout)getChildAt(mCurrentPage); if(currentPage != null){ onWallpaperTap(ev); } } //调用父类的onInterceptTouchEvent(ev);主要功能是在onTouchEvent()方法之前处理touch事件。包括:down、up、move事件。 return super.onInterceptTouchEvent(ev); } }

    重写了父类的computeScroll();主要功能是计算拖动的位移量、更新背景、设置要显示的屏幕。

    public void computeScroll(){ super.computeScroll(); mWallpaperOffset.syncWithScroll(); } public void syncWithScroll(){ float offset = wallpaperOffsetForCurrentScroll(); mWallpaperOffset.setFinalX(offset); updateOffset(true); } //处理壁纸移动的类 class WallpaperOffsetInterpolator implements Choreographer.FrameCallback { float mFinalOffset = 0.0f; float mCurrentOffset = 0.5f; // to force an initial update boolean mWaitingForUpdate; Choreographer mChoreographer; Interpolator mInterpolator; boolean mAnimating; long mAnimationStartTime; float mAnimationStartOffset; private final int ANIMATION_DURATION = 250; // Don't use all the wallpaper for parallax until you have at least this many pages private final int MIN_PARALLAX_PAGE_SPAN = 3; int mNumScreens; public WallpaperOffsetInterpolator() { mChoreographer = Choreographer.getInstance(); mInterpolator = new DecelerateInterpolator(1.5f); } @Override public void doFrame(long frameTimeNanos) { //调用了updateOffset方法,这个方法即是处理壁纸的位移的。 updateOffset(false); } private void updateOffset(boolean force) { if (mWaitingForUpdate || force) { mWaitingForUpdate = false; if (computeScrollOffset() && mWindowToken != null) { try { mWallpaperManager.setWallpaperOffsets(mWindowToken, mWallpaperOffset.getCurrX(), 0.5f); setWallpaperOffsetSteps(); } catch (IllegalArgumentException e) { Log.e(TAG, "Error updating wallpaper offset: " + e); } } } } public boolean computeScrollOffset() { final float oldOffset = mCurrentOffset; if (mAnimating) { long durationSinceAnimation = System.currentTimeMillis() - mAnimationStartTime; float t0 = durationSinceAnimation / (float) ANIMATION_DURATION; float t1 = mInterpolator.getInterpolation(t0); mCurrentOffset = mAnimationStartOffset + (mFinalOffset - mAnimationStartOffset) * t1; mAnimating = durationSinceAnimation < ANIMATION_DURATION; } else { mCurrentOffset = mFinalOffset; } if (Math.abs(mCurrentOffset - mFinalOffset) > 0.0000001f) { scheduleUpdate(); } if (Math.abs(oldOffset - mCurrentOffset) > 0.0000001f) { return true; } return false; } private float wallpaperOffsetForCurrentScroll() { // TODO: do different behavior if it's a live wallpaper? // Don't use up all the wallpaper parallax until you have at least // MIN_PARALLAX_PAGE_SPAN pages int numScrollingPages = getNumScreensExcludingEmptyAndCustom(); int parallaxPageSpan; if (mWallpaperIsLiveWallpaper) { parallaxPageSpan = numScrollingPages - 1; } else { parallaxPageSpan = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages - 1); } mNumPagesForWallpaperParallax = parallaxPageSpan; if (getChildCount() <= 1) { if (mIsRtl) { return 1 - 1.0f/mNumPagesForWallpaperParallax; } return 0; } // Exclude the leftmost page int emptyExtraPages = numEmptyScreensToIgnore(); int firstIndex = numCustomPages(); // Exclude the last extra empty screen (if we have > MIN_PARALLAX_PAGE_SPAN pages) int lastIndex = getChildCount() - 1 - emptyExtraPages; if (mIsRtl) { int temp = firstIndex; firstIndex = lastIndex; lastIndex = temp; } int firstPageScrollX = getScrollForPage(firstIndex); int scrollRange = getScrollForPage(lastIndex) - firstPageScrollX; if (scrollRange == 0) { return 0; } else { // Sometimes the left parameter of the pages is animated during a layout transition; // this parameter offsets it to keep the wallpaper from animating as well int adjustedScroll = getScrollX() - firstPageScrollX - getLayoutTransitionOffsetForPage(0); float offset = Math.min(1, adjustedScroll / (float) scrollRange); offset = Math.max(0, offset); // On RTL devices, push the wallpaper offset to the right if we don't have enough // pages (ie if numScrollingPages < MIN_PARALLAX_PAGE_SPAN) if (!mWallpaperIsLiveWallpaper && numScrollingPages < MIN_PARALLAX_PAGE_SPAN && mIsRtl) { return offset * (parallaxPageSpan - numScrollingPages + 1) / parallaxPageSpan; } return offset * (numScrollingPages - 1) / parallaxPageSpan; } } private int numEmptyScreensToIgnore() { int numScrollingPages = getChildCount() - numCustomPages(); if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && hasExtraEmptyScreen()) { return 1; } else { return 0; } } private int getNumScreensExcludingEmptyAndCustom() { int numScrollingPages = getChildCount() - numEmptyScreensToIgnore() - numCustomPages(); return numScrollingPages; } public void syncWithScroll() { //获取壁纸偏移量 float offset = wallpaperOffsetForCurrentScroll(); //设置壁纸偏移量 mWallpaperOffset.setFinalX(offset); //更新壁纸偏移量 updateOffset(true); } public float getCurrX() { return mCurrentOffset; } public float getFinalX() { return mFinalOffset; } private void animateToFinal() { mAnimating = true; mAnimationStartOffset = mCurrentOffset; mAnimationStartTime = System.currentTimeMillis(); } private void setWallpaperOffsetSteps() { // Set wallpaper offset steps (1 / (number of screens - 1)) float xOffset = 1.0f / mNumPagesForWallpaperParallax; if (xOffset != mLastSetWallpaperOffsetSteps) { mWallpaperManager.setWallpaperOffsetSteps(xOffset, 1.0f); mLastSetWallpaperOffsetSteps = xOffset; } } public void setFinalX(float x) { scheduleUpdate(); mFinalOffset = Math.max(0f, Math.min(x, 1.0f)); if (getNumScreensExcludingEmptyAndCustom() != mNumScreens) { if (mNumScreens > 0) { // Don't animate if we're going from 0 screens animateToFinal(); } mNumScreens = getNumScreensExcludingEmptyAndCustom(); } } private void scheduleUpdate() { if (!mWaitingForUpdate) { mChoreographer.postFrameCallback(this); mWaitingForUpdate = true; } } //把壁纸最终偏移量设置为当前偏移量 public void jumpToFinal() { mCurrentOffset = mFinalOffset; } }

    onLayout()交给父类PageView处理

    protected void onLayout(boolean changed, int left,int top,int right,int bottom){ if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { mWallpaperOffset.syncWithScroll(); mWallpaperOffset.jumpToFinal(); } super.onLayout(changed, left, top, right, bottom); }

    查找新屏幕的索引,如果存在空屏幕,则在那之前插入它

    public void long insertNewWorkspaceScreenBeforeEmptyScreen(long screenId){ } public long insertNewWorkspaceScreen(long screenId, int insertIndex){ if(mWorkspaceScreens.containsKey(screenId)){ throw new RuntimeException("Screen id " + screenId + "already exists!"); } //inflate xml CellLayout newScreen = (CellLayout)mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen,this,false); //设置屏幕的长按、点击事件监听 newScreen.setOnLongClickListener(mLongClickListener); newScreen.setOnClickListener(mLauncher); //屏幕声音效果 newScreen.setSoundEffectEnabled(false); mWorkspaceScreens.put(screenId,newScreen); mScreenOrder.add(insertIndex,screenId); addView(newScreen,insertIndex); //屏幕允许拖曳? LauncherAccessibilityDelegate delegate = LauncherAppSate.getInstance().getAccessibleDrag(); if(delegate != null && dlelegate.isInAccessibleDrag()){ newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILTY_DRAG); return screenId; } }
    转载请注明原文地址: https://ju.6miu.com/read-1139.html

    最新回复(0)