Android13 Launcher3 客制化Workspace页面指示器

需求:原生态的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

@Override

public void setActiveMarker(int activePage) {

//added by Kevin.Ye

    if (mActivePage != activePage) {

    mActivePage = activePage;

    }

}

step3:重写onDraw,用新的draw替代原生的

@Override
    protected void onDraw(Canvas canvas) {
        //Kevin.Ye added start
        if(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 testing
           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);
           }

           return;
        }
        //Kevin.Ye end
        if (mTotalScroll == 0 || mNumPagesFloat == 0) {
            return;
        }

step4:需要注意pageindicator的marginBottom是通过计算得来的


@Override

public 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 start

lp.leftMargin = lp.rightMargin = 0;

lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;

if(FeatureFlags.REMOVE_HOTSEAT)

lp.bottomMargin = insets.bottom;//there is no hotseatBarSize

else

lp.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 added
    private int mActivePage = 0;//Kevin.Ye added
    private static final Property<WorkspacePageIndicator, Integer> PAINT_ALPHA
            = new Property<WorkspacePageIndicator, Integer>(Integer.class, "paint_alpha") {
        @Override
        public Integer get(WorkspacePageIndicator obj) {
            return obj.mLinePaint.getAlpha();
        }

        @Override
        public 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") {
        @Override
        public Float get(WorkspacePageIndicator obj) {
            return obj.mNumPagesFloat;
        }

        @Override
        public 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") {
        @Override
        public Integer get(WorkspacePageIndicator obj) {
            return obj.mTotalScroll;
        }

        @Override
        public 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.Ye
        boolean darkText = Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText);
        mActiveAlpha = darkText ? BLACK_ALPHA : WHITE_ALPHA;
        mLinePaint.setColor(darkText ? Color.BLACK : Color.WHITE);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //Kevin.Ye added start
	if(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 end
        if (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);
    }

    @Override
    public 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);
    }

    @Override
    public void setActiveMarker(int activePage) {
       //added by Kevin.Ye
       if (mActivePage != activePage) {
            mActivePage = activePage;
       }
    }

    @Override
    public 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;
            }
        }
    }

    @Override
    public 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() {
            @Override
            public void onAnimationEnd(Animator animation) {
                mAnimators[animatorIndex] = null;
            }
        });
        mAnimators[animatorIndex].setDuration(LINE_ANIMATE_DURATION);
        mAnimators[animatorIndex].start();
    }

    /**
     * Pauses all currently running animations.
     */
    @Override
    public 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.
     */
    @Override
    public void skipAnimationsToEnd() {
        for (int i = 0; i < ANIMATOR_COUNT; i++) {
            if (mAnimators[i] != null) {
                mAnimators[i].end();
            }
        }
    }

    @Override
    public 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 start
            lp.leftMargin = lp.rightMargin = 0;
            lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
            /*
	    if(FeatureFlags.REMOVE_HOTSEAT)
            	lp.bottomMargin = insets.bottom;//there is no hotseatBarSize
            else
	        lp.bottomMargin = grid.hotseatBarSizePx + insets.bottom;
            */
            lp.bottomMargin = insets.bottom + mMarginBottom;
	    //Kevin.Ye modified end
        }
        setLayoutParams(lp);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kwanvin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值