需求:原生态的workspace页面指示器是个长条,不大好看,需要进行客制化
实现效果如图:
实现原理:
代码实现在WorkspacePageIndicator.java 布局在launcher.xml里
实现在WorkspacePageIndicator.java通过重写onDraw函数即可以实现
修改步骤:
step1: workspace page indicator的大小是在values里定义的
<!-- Workspace page indicator -->
<dimen name="workspace_page_indicator_height">32dp</dimen>
<dimen name="workspace_page_indicator_line_height">6dp</dimen>
<dimen name="workspace_page_indicator_overlap_workspace">0dp</dimen>
step2:添加一个成员变量,代表当前所在页
private int mActivePage = 0;//Kevin.Ye added@Overridepublic void setActiveMarker(int activePage) {//added by Kevin.Yeif (mActivePage != activePage) {mActivePage = activePage;}}
step3:重写onDraw,用新的draw替代原生的
@Overrideprotected void onDraw(Canvas canvas) {//Kevin.Ye added startif(FeatureFlags.CUSTOMIZED_PAGE_INDICATOR){if(mNumPagesFloat == 0){return;}int numPage = (int)mNumPagesFloat;int dotTop = getHeight() / 2 - mLineHeight / 2;int dotWidth = 48;int dotHeight = mLineHeight;int gapWidth = 16;int indicatorWidth = numPage*dotWidth + (numPage-1)*gapWidth;int indicatorLeft = getWidth()/2-indicatorWidth/2;//canvas.drawRoundRect(0,0,getWidth(),getHeight(),dotHeight,dotHeight,mLinePaint);//only for testingfor(int i=0;i<numPage;i++){mLinePaint.setColor(i == mActivePage ? 0xff00ffb3 : 0xff7690b1);int dotLeft = indicatorLeft+i*(dotWidth+gapWidth);canvas.drawRoundRect(dotLeft,dotTop,dotLeft+dotWidth,dotTop+dotHeight,dotHeight,dotHeight,mLinePaint);}return;}//Kevin.Ye endif (mTotalScroll == 0 || mNumPagesFloat == 0) {return;}
step4:需要注意pageindicator的marginBottom是通过计算得来的
@Overridepublic void setInsets(Rect insets) {DeviceProfile grid = mLauncher.getDeviceProfile();FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();if (grid.isVerticalBarLayout()) {Rect padding = grid.workspacePadding;lp.leftMargin = padding.left + grid.workspaceCellPaddingXPx;lp.rightMargin = padding.right + grid.workspaceCellPaddingXPx;lp.bottomMargin = padding.bottom;} else {//Kevin.Ye added startlp.leftMargin = lp.rightMargin = 0;lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;if(FeatureFlags.REMOVE_HOTSEAT)lp.bottomMargin = insets.bottom;//there is no hotseatBarSizeelselp.bottomMargin = grid.hotseatBarSizePx + insets.bottom;//Kevin.Ye added end}setLayoutParams(lp);}
完整代码:
package com.android.launcher3.pageindicators;import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Property;
import android.view.Gravity;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.FrameLayout;import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Themes;
import com.android.launcher3.config.FeatureFlags;//Kevin.Ye
/*** A PageIndicator that briefly shows a fraction of a line when moving between pages** The fraction is 1 / number of pages and the position is based on the progress of the page scroll.*/
public class WorkspacePageIndicator extends View implements Insettable, PageIndicator {private static final int LINE_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration();private static final int LINE_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();public static final int WHITE_ALPHA = (int) (0.70f * 255);public static final int BLACK_ALPHA = (int) (0.65f * 255);private static final int LINE_ALPHA_ANIMATOR_INDEX = 0;private static final int NUM_PAGES_ANIMATOR_INDEX = 1;private static final int TOTAL_SCROLL_ANIMATOR_INDEX = 2;private static final int ANIMATOR_COUNT = 3;private ValueAnimator[] mAnimators = new ValueAnimator[ANIMATOR_COUNT];private final Handler mDelayedLineFadeHandler = new Handler(Looper.getMainLooper());private final Launcher mLauncher;private boolean mShouldAutoHide = true;// The alpha of the line when it is showing.private int mActiveAlpha = 0;// The alpha that the line is being animated to or already at (either 0 or mActiveAlpha).private int mToAlpha;// A float value representing the number of pages, to allow for an animation when it changes.private float mNumPagesFloat;private int mCurrentScroll;private int mTotalScroll;private Paint mLinePaint;private final int mLineHeight;private final int mMarginBottom;//Kevin.Ye addedprivate int mActivePage = 0;//Kevin.Ye addedprivate static final Property<WorkspacePageIndicator, Integer> PAINT_ALPHA= new Property<WorkspacePageIndicator, Integer>(Integer.class, "paint_alpha") {@Overridepublic Integer get(WorkspacePageIndicator obj) {return obj.mLinePaint.getAlpha();}@Overridepublic void set(WorkspacePageIndicator obj, Integer alpha) {obj.mLinePaint.setAlpha(alpha);obj.invalidate();}};private static final Property<WorkspacePageIndicator, Float> NUM_PAGES= new Property<WorkspacePageIndicator, Float>(Float.class, "num_pages") {@Overridepublic Float get(WorkspacePageIndicator obj) {return obj.mNumPagesFloat;}@Overridepublic void set(WorkspacePageIndicator obj, Float numPages) {obj.mNumPagesFloat = numPages;obj.invalidate();}};private static final Property<WorkspacePageIndicator, Integer> TOTAL_SCROLL= new Property<WorkspacePageIndicator, Integer>(Integer.class, "total_scroll") {@Overridepublic Integer get(WorkspacePageIndicator obj) {return obj.mTotalScroll;}@Overridepublic void set(WorkspacePageIndicator obj, Integer totalScroll) {obj.mTotalScroll = totalScroll;obj.invalidate();}};private Runnable mHideLineRunnable = () -> animateLineToAlpha(0);public WorkspacePageIndicator(Context context) {this(context, null);}public WorkspacePageIndicator(Context context, AttributeSet attrs) {this(context, attrs, 0);}public WorkspacePageIndicator(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);Resources res = context.getResources();mLinePaint = new Paint();mLinePaint.setAlpha(0);mLauncher = Launcher.getLauncher(context);mLineHeight = res.getDimensionPixelSize(R.dimen.workspace_page_indicator_line_height);mMarginBottom = res.getDimensionPixelSize(R.dimen.workspace_page_indicator_margin_bottom);//added by Kevin.Yeboolean darkText = Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText);mActiveAlpha = darkText ? BLACK_ALPHA : WHITE_ALPHA;mLinePaint.setColor(darkText ? Color.BLACK : Color.WHITE);}@Overrideprotected void onDraw(Canvas canvas) {//Kevin.Ye added startif(FeatureFlags.CUSTOMIZED_PAGE_INDICATOR){if(mNumPagesFloat == 0){return;}int numPage = (int)mNumPagesFloat;if(numPage <= 1) return;if(mLauncher.getWorkspace().getVisibility() != View.VISIBLE)return;/* int dotTop = getHeight() / 2 - mLineHeight / 2;int dotWidth = 48;int dotHeight = mLineHeight;int gapWidth = 16;int indicatorWidth = numPage*dotWidth + (numPage-1)*gapWidth;int indicatorLeft = getWidth()/2-indicatorWidth/2;for(int i=0;i<numPage;i++){mLinePaint.setColor(i == mActivePage ? 0xff00ffb3 : 0xff7690b1);int dotLeft = indicatorLeft+i*(dotWidth+gapWidth);canvas.drawRoundRect(dotLeft,dotTop,dotLeft+dotWidth,dotTop+dotHeight,dotHeight,dotHeight,mLinePaint);}*/int dotRadius = 6;//mLineHeight/2;int dotGap = 30;int indicatorWidth = numPage*dotRadius*2 + (numPage-1)*dotRadius*2;int indicatorLeft = getWidth()/2-indicatorWidth/2;for(int i=0;i<numPage;i++){mLinePaint.setColor(i == mActivePage ? 0xffffffff : 0x80ffffff);float dotCX = indicatorLeft+i*(dotRadius*2+dotGap)-dotRadius;float dotCY = dotRadius;canvas.drawCircle(dotCX,dotCY,dotRadius,mLinePaint);}return;}//Kevin.Ye endif (mTotalScroll == 0 || mNumPagesFloat == 0) {return;}// Compute and draw line rect.float progress = Utilities.boundToRange(((float) mCurrentScroll) / mTotalScroll, 0f, 1f);int availableWidth = getWidth();int lineWidth = (int) (availableWidth / mNumPagesFloat);int lineLeft = (int) (progress * (availableWidth - lineWidth));int lineRight = lineLeft + lineWidth;canvas.drawRoundRect(lineLeft, getHeight() / 2 - mLineHeight / 2, lineRight,getHeight() / 2 + mLineHeight / 2, mLineHeight, mLineHeight, mLinePaint);}@Overridepublic void setScroll(int currentScroll, int totalScroll) {if (getAlpha() == 0) {return;}animateLineToAlpha(mActiveAlpha);mCurrentScroll = currentScroll;if (mTotalScroll == 0) {mTotalScroll = totalScroll;} else if (mTotalScroll != totalScroll) {animateToTotalScroll(totalScroll);} else {invalidate();}if (mShouldAutoHide) {hideAfterDelay();}}private void hideAfterDelay() {mDelayedLineFadeHandler.removeCallbacksAndMessages(null);mDelayedLineFadeHandler.postDelayed(mHideLineRunnable, LINE_FADE_DELAY);}@Overridepublic void setActiveMarker(int activePage) {//added by Kevin.Yeif (mActivePage != activePage) {mActivePage = activePage;}}@Overridepublic void setMarkersCount(int numMarkers) {if (Float.compare(numMarkers, mNumPagesFloat) != 0) {setupAndRunAnimation(ObjectAnimator.ofFloat(this, NUM_PAGES, numMarkers),NUM_PAGES_ANIMATOR_INDEX);} else {if (mAnimators[NUM_PAGES_ANIMATOR_INDEX] != null) {mAnimators[NUM_PAGES_ANIMATOR_INDEX].cancel();mAnimators[NUM_PAGES_ANIMATOR_INDEX] = null;}}}@Overridepublic void setShouldAutoHide(boolean shouldAutoHide) {mShouldAutoHide = shouldAutoHide;if (shouldAutoHide && mLinePaint.getAlpha() > 0) {hideAfterDelay();} else if (!shouldAutoHide) {mDelayedLineFadeHandler.removeCallbacksAndMessages(null);}}private void animateLineToAlpha(int alpha) {if (alpha == mToAlpha) {// Ignore the new animation if it is going to the same alpha as the current animation.return;}mToAlpha = alpha;setupAndRunAnimation(ObjectAnimator.ofInt(this, PAINT_ALPHA, alpha),LINE_ALPHA_ANIMATOR_INDEX);}private void animateToTotalScroll(int totalScroll) {setupAndRunAnimation(ObjectAnimator.ofInt(this, TOTAL_SCROLL, totalScroll),TOTAL_SCROLL_ANIMATOR_INDEX);}/*** Starts the given animator and stores it in the provided index in {@link #mAnimators} until* the animation ends.** If an animator is already at the index (i.e. it is already playing), it is canceled and* replaced with the new animator.*/private void setupAndRunAnimation(ValueAnimator animator, final int animatorIndex) {if (mAnimators[animatorIndex] != null) {mAnimators[animatorIndex].cancel();}mAnimators[animatorIndex] = animator;mAnimators[animatorIndex].addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {mAnimators[animatorIndex] = null;}});mAnimators[animatorIndex].setDuration(LINE_ANIMATE_DURATION);mAnimators[animatorIndex].start();}/*** Pauses all currently running animations.*/@Overridepublic void pauseAnimations() {for (int i = 0; i < ANIMATOR_COUNT; i++) {if (mAnimators[i] != null) {mAnimators[i].pause();}}}/*** Force-ends all currently running or paused animations.*/@Overridepublic void skipAnimationsToEnd() {for (int i = 0; i < ANIMATOR_COUNT; i++) {if (mAnimators[i] != null) {mAnimators[i].end();}}}@Overridepublic void setInsets(Rect insets) {DeviceProfile grid = mLauncher.getDeviceProfile();FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();if (grid.isVerticalBarLayout()) {Rect padding = grid.workspacePadding;lp.leftMargin = padding.left + grid.workspaceCellPaddingXPx;lp.rightMargin = padding.right + grid.workspaceCellPaddingXPx;lp.bottomMargin = padding.bottom;} else {//Kevin.Ye added startlp.leftMargin = lp.rightMargin = 0;lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;/*if(FeatureFlags.REMOVE_HOTSEAT)lp.bottomMargin = insets.bottom;//there is no hotseatBarSizeelselp.bottomMargin = grid.hotseatBarSizePx + insets.bottom;*/lp.bottomMargin = insets.bottom + mMarginBottom;//Kevin.Ye modified end}setLayoutParams(lp);}
}