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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/412535.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

顺序循环队列

顺序循环队列 队头插入元素&#xff0c;队尾删除元素 本来应该判空和判断是否存满的条件都是&#xff1a;队头 队尾&#xff0c;但这样就没办法区分了&#xff0c;所以&#xff0c;就牺牲一个空间&#xff08;比如长度为10&#xff0c;但只能存9个&#xff09;&#xff0c;这…

auto的使用场景

auto的两面性 合理使用auto 不仅可以减少代码量, 也会大大提高代码的可读性. 但是事情总有它的两面性 如果滥用auto, 则会让代码失去可读性 推荐写法 这里推荐两种情况下使用auto 一眼就能看出声明变量的初始化类型的时候 比如迭代器的循环, 用例如下 #include <iostre…

利用autoDecoder工具在数据包加密+签名验证站点流畅测试

站点是个靶场 https://github.com/0ctDay/encrypt-decrypt-vuls 演示地址http://39.98.108.20:8085/ 不是仅登录位置暴力破解的那种场景&#xff0c;使用autoDecoder&#xff08;https://github.com/f0ng/autoDecoder&#xff09;的好处就是每个请求自动加解密&#xff0c;测…

关于ThinkPHP 5 框架开启自动搜索控制器 无法访问的问题坑

假如当前有一个登陆接口功能 因为后续会有不同版本的 登陆接口 这时候 我们可以在控制器中 新建文件夹 做区分 方便管理即 新建了一个 api 模块 文件路径是 api/controller/V1/Login 正常情况下 controller 目录下 是 控制器文件 login.php 文件&#xff0c;由于我们有多个…

Qt text-align和padding属性

1. text-align属性是用来设置文本的水平对齐方式。 text-align: center 文本将居中显示text-align: left 文本将左对齐显示text-align: right 文本将右对齐显示 2. 内边距padding: 内边距是元素内容与其边框之间的空间 padding-left: 10px; 距离内左边距10个像素点padding-r…

时序预测 | 基于WTC+transformer时间序列组合预测模型(pytorch)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 WTCtransformer时间序列组合预测模型 WTC,transformer 创新点&#xff0c;超级新。先发先得&#xff0c;高精度代码。 预测主模型transformer也可以改其他WTC-former系列&#xff0c;比如WTC-informer/autoformer等等…

LLaMA Factory微调Llama3模型

LLaMA Factory是一款开源低代码大模型微调框架&#xff0c;集成了业界最广泛使用的微调技术&#xff0c;支持通过Web UI界面零代码微调大模型&#xff0c;目前已经成为开源社区内最受欢迎的微调框架。 &#x1f4a5;GPU推荐使用24GB显存的A10&#xff08;ecs.gn7i-c8g1.2xlarg…

论文泛读: DETRs Beat YOLOs on Real-time Object Detection

[toc[ DETRs Beat YOLOs on Real-time Object Detection 论文地址: https://openaccess.thecvf.com/content/CVPR2024/html/Zhao_DETRs_Beat_YOLOs_on_Real-time_Object_Detection_CVPR_2024_paper.html 代码地址: https://zhao-yian.github.io/RTDETR 动机 现状 YOLO系列因…

ubuntu设置为自己需要的屏幕分辨率

先说一下我处理该问题的大体背景&#xff1a;我是学习Linux的新手&#xff0c;刚学完嵌入式Linux驱动开发相关课程。现在想接着学习一下QT开发。我是在电脑上装了虚拟机之后安装的ubuntu系统。因为换了电脑&#xff0c;所以重新装了ubuntu系统。但是&#xff0c;装完ubuntu系统…

SELF-INSTRUCT: Aligning Language Modelswith Self-Generated Instructions 学习

指令微调就是要训练模型执行用户的要求的能力。 文章首先说“指令微调”数据集经常是人工生成&#xff0c;有数量少等缺点。文章提供了一个让语言模型自己生成指令微调数据&#xff0c;自己学习的方法。首先会让一个语言模型自己生成要求&#xff0c;输入和输出&#xff0c;然…

【JS】使用MessageChannel实现深度克隆

前言 通常使用简便快捷的JSON 序列化与反序列化实现深克隆&#xff0c;也可以递归实现或者直接使用lodash。 但 JSON 序列化与反序列化 无法处理如下的循环引用&#xff1a; 实现 MessageChannel 内部使用了浏览器内置的结构化克隆算法&#xff0c;该算法可以在不同的浏览器上…

redis集群部署

因为Redis是c开发的,因此安装redis需要c语言的编译环境,即先需要安装gcc. 1.解压包 [rootredis01 Redis]# tar -zvxf redis-3.2.9.tar.gz 查看是否存在Makefile文件,存在则直接make编译redis源码 2.编译文件 [rootredis01 redis-3.2.9]# make 安装编译好的文件 [rootredi…

上传拍摄素材和后期剪辑素材太慢?镭速助力企业加速大文件传输

随着时光的流逝&#xff0c;当代人对视觉体验的要求越来越高&#xff0c;每一帧画面都追求极致的清晰度与细腻感。这无疑为影视制作带来了机遇&#xff0c;同时也带来了挑战。高清4K、8K视频等大文件的传输需求日益增长&#xff0c;传统的FTP、HTTP等数据传输方式已难以满足行业…

华硕天选Air:开学季的性价比之巅

正值开学季&#xff0c;华硕天选Air全能本以8999元的首发价回归&#xff0c;为学生和需求高性能笔记本的用户带来了超值的选择。 这款笔记本以其轻薄设计和强悍性能&#xff0c;成为市场上的热点。 轻薄设计&#xff0c;潮流先锋 华硕天选Air 2024采用了全新模具设计&#xf…

零基础学习Python(七)

1. 字符串常用方法 lower()、upper()&#xff1a;转换为小写字符串、大写字符串 split(str)&#xff1a;按照指定字符串str进行分割&#xff0c;结果为列表&#xff1a; email "123qq.com" print(email.split("")) [123, qq.com] count(str)&#xf…

python12 中,No module named‘distutils‘错误

python12跑redis的时候&#xff0c;突然发现报错“ No module nameddistutils ” distutils在python标准库从2012年就开始断更了&#xff0c;python12中已经移除该库&#xff0c;可以安装以下库进行解决。 pip install setuptools --upgrade “setuptools”是一个处理Python软…

OceanBase 功能解析之 Binlog Service

前言 MySQL&#xff0c;是在全球广泛应用的开源关系型数据库&#xff0c;除了其稳定性、可靠性和易用性&#xff0c;他早期推出的二进制日志功能&#xff0c;即binlog&#xff0c;也是MySQL广受欢迎的原因。 MySQL binlog&#xff0c;即二进制日志&#xff0c;是 MySQL 中用于…

爆品是测出来的,不是选出来的

我在亚马逊摸爬滚打了五年&#xff0c;深深感受到了"七分选品&#xff0c;三分运营"的重要性。不管你的产品图片、描述多么精美&#xff0c;如果不去精选和测试&#xff0c;很难保证能出单。我见过很多跨境新手在选品上卡了几个月&#xff0c;纠结于卖什么。但实际上…

光敏电阻传感器详解(STM32)

目录 一、介绍 二、传感器原理 1.光敏电阻传感器介绍 2.原理图 三、程序设计 main.c文件 ldr.h文件 ldr.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 光敏电阻器是利用半导体的光电导效应制成的一种电阻值随入射光的强弱而改变的电阻器&#xff0c;又称为光…

上新!Matlab实现基于QRGRU-Attention分位数回归门控循环单元注意力机制的时间序列区间预测模型

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现基于QRGRU-Attention分位数回归门控循环单元注意力机制的时间序列区间预测模型&#xff1b; 2.多图输出、多指标输出(MAE、RMSE、MSE、R2)&#xff0c;多输入单输出&#xff0c;含不同置信区间图、概率…