【Android】时钟动态图标的定制化

发布于:2023-01-22 ⋅ 阅读:(7) ⋅ 点赞:(0) ⋅ 评论:(0)

活动地址:CSDN21天学习挑战赛

1、概述

在定制化10.0的项目开发中,在Launcher3的app列表页,有时钟app的图标,由于图标是静态的开发的需要要求调成动态图标,时刻显示时间,所以要把时钟图标替换成动态图标。

在这里插入图片描述

2、9.0 10.0时钟动态图标的定制化主要核心代码

packages/apps/Launcher3/src/com/android/launcher3/BubbleTextView.java
 
packages/apps/Launcher3/src/com/android/launcher3/Launcher.java

3、9.0 10.0时钟动态图标的定制化主要核心代码和功能实现

在Launcher3 workspace 第二屏app列表页中 默认的时钟app图标是静态图标 就是时间不会变化的但这也显然不符合要求,时间是时刻都在变化的。

3.1 定义绘制时间图标类IconUtil.java

package com.android.launcher3;
 
import android.content.Context;
 
import android.graphics.*;
 
import com.android.launcher3.R;
 
public class IconUtil {
 
private static final String TAG = "IconUtil";
 
private static Bitmap getBitmap(Context context, int res) {
 
BitmapFactory.Options options = new BitmapFactory.Options();
 
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
 
return BitmapFactory.decodeResource(context.getResources(), res, options);
 
}
 
public static Bitmap getDeskClockIcon(Context context) {
 
Bitmap empty = getBitmap(context, R.drawable.icon_time);
 
int x = empty.getWidth();
 
int y = empty.getHeight();
 
Bitmap deskClock = Bitmap.createBitmap(x, y, Bitmap.Config.ARGB_4444);
 
Canvas canvas = new Canvas(deskClock);
 
Paint paint = new Paint();
 
paint.setAntiAlias(true);
 
canvas.drawBitmap(empty, 0, 0, paint);
 
paint.setStrokeCap(Paint.Cap.ROUND);
 
paint.setStrokeWidth(30);
 
paint.setColor(Color.parseColor("#FFA500"));
 
int radius = 200;
 
int cx = x / 2;
 
int cy = y / 2;
 
int hour = Integer.parseInt(DateUtils.getCurrentHour());
 
int min = Integer.parseInt(DateUtils.getCurrentMin());
 
//时针的角度,这里是整点的角度。因为0°是从3点开始,所以这里减90°,从9点开始计算角度
 
int drgeeHour = hour * 30 - 90;
 
if (drgeeHour < 0) {
 
drgeeHour += 360;
 
}
 
// 加上时针在两个整点之间的角度,一分钟在分针上是6°,在时针上是min * 6 / 12
 
drgeeHour += min * 6 / 12;
 
//时针 针尖的x y坐标,相当于已知圆心坐标和半径,求圆上任意一点的坐标
 
int xHour = (int) (cx + radius * Math.cos(drgeeHour * 3.14 / 180));
 
int yHour = (int) (cy + radius * Math.sin(drgeeHour * 3.14 / 180));
 
canvas.drawLine(xHour, yHour, cx, cy, paint);
 
//分针的长度
 
radius = 260;
 
paint.setStrokeWidth(20);
 
paint.setColor(Color.RED);
 
//分针的角度
 
int drgeeMin = min * 6 - 90;
 
if (drgeeMin < 0) {
 
drgeeMin += 360;
 
}
 
//分针 针尖的x y坐标
 
int x1 = (int) (cx + radius * Math.cos(drgeeMin * Math.PI / 180));
 
int y1 = (int) (cy + radius * Math.sin(drgeeMin * Math.PI / 180));
 
canvas.drawLine(x1, y1, cx, cy, paint);
 
return deskClock;
 
}
 
}

3.2 计算当前时间工具类 DateUtils.java

package com.android.launcher3;
 
import java.text.SimpleDateFormat;
 
public class DateUtils {
 
public static String getCurrentDay() {
 
SimpleDateFormat format = new SimpleDateFormat("dd");
 
Long t = new Long(System.currentTimeMillis());
 
String d = format.format(t);
 
return d;
 
}
 
public static String getCurrentSecond() {
 
SimpleDateFormat format = new SimpleDateFormat("ss");
 
Long t = new Long(System.currentTimeMillis());
 
String d = format.format(t);
 
return d;
 
}
 
public static String getCurrentMin() {
 
SimpleDateFormat format = new SimpleDateFormat("mm");
 
Long t = new Long(System.currentTimeMillis());
 
String d = format.format(t);
 
return d;
 
}
 
public static String getCurrentHour() {
 
SimpleDateFormat format = new SimpleDateFormat("HH");
 
Long t = new Long(System.currentTimeMillis());
 
String d = format.format(t);
 
return d;
 
}
 
}

3.3 时钟工具类DeskClockUtil.java

package com.android.launcher3;
 
import android.content.Context;
 
import android.graphics.Bitmap;
 
import android.os.Handler;
 
import android.os.Message;
 
import com.android.launcher3.LauncherSettings;
 
public class DeskClockUtil {
 
private OnDeskClockIconChangeListener mListener;
 
private boolean mIsResume;
 
private Handler mHandler = new Handler() {
 
@Override
 
public void handleMessage(Message msg) {
 
super.handleMessage(msg);
 
if (msg.what == 100) {
 
Message msg1 = new Message();
 
msg1.what = 100;
 
msg1.obj = msg.obj;
 
mHandler.sendMessageDelayed(msg1, 60000);
 
if (mListener != null) {
 
mListener.onChange(IconUtil.getDeskClockIcon((Context) msg.obj));
 
}
 
}
 
}
 
};
 
private static DeskClockUtil sInstance;
 
private DeskClockUtil() {
 
}
 
public static DeskClockUtil getInstance() {
 
if (sInstance == null) {
 
sInstance = new DeskClockUtil();
 
}
 
return sInstance;
 
}
 
private void refresh(Context context) {
 
if (mListener != null) {
 
mListener.onChange(IconUtil.getDeskClockIcon(context));
 
}
 
if (mHandler.hasMessages(100)) {
 
mHandler.removeMessages(100);
 
}
 
Message msg = new Message();
 
msg.what = 100;
 
msg.obj = context;
 
mHandler.sendMessageDelayed(msg,
 
1000 * (60 - Integer.parseInt(DateUtils.getCurrentSecond())));
 
}
 
public void onResume(Context context) {
 
mIsResume = true;
 
refresh(context);
 
}
 
public void onPause() {
 
mIsResume = false;
 
mHandler.removeMessages(100);
 
}
 
public void setListener(OnDeskClockIconChangeListener listener,Context context) {
 
mListener = listener;
 
if (mIsResume) {
 
refresh(context);
 
}
 
}
 
public interface OnDeskClockIconChangeListener {
 
void onChange(Bitmap icon);
 
}
 
}

3.4.BubbleTextView.java中设置动态图标

public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback {
 
 
private static final int DISPLAY_WORKSPACE = 0;
 
private static final int DISPLAY_ALL_APPS = 1;
 
private static final int DISPLAY_FOLDER = 2;
 
 
private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
 
 
 
private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY
 
= new Property<BubbleTextView, Float>(Float.TYPE, "dotScale") {
 
@Override
 
public Float get(BubbleTextView bubbleTextView) {
 
return bubbleTextView.mDotParams.scale;
 
}
 
 
@Override
 
public void set(BubbleTextView bubbleTextView, Float value) {
 
bubbleTextView.mDotParams.scale = value;
 
bubbleTextView.invalidate();
 
}
 
};
 
 
public static final Property<BubbleTextView, Float> TEXT_ALPHA_PROPERTY
 
= new Property<BubbleTextView, Float>(Float.class, "textAlpha") {
 
@Override
 
public Float get(BubbleTextView bubbleTextView) {
 
return bubbleTextView.mTextAlpha;
 
}
 
 
@Override
 
public void set(BubbleTextView bubbleTextView, Float alpha) {
 
bubbleTextView.setTextAlpha(alpha);
 
}
 
};
 
 
private final ActivityContext mActivity;
 
private Drawable mIcon;
 
private final boolean mCenterVertically;
 
 
private final CheckLongPressHelper mLongPressHelper;
 
private final StylusEventHelper mStylusEventHelper;
 
private final float mSlop;
 
 
private final boolean mLayoutHorizontal;
 
private final int mIconSize;
 
private float mIconTextSize;
 
 
@ViewDebug.ExportedProperty(category = "launcher")
 
private boolean mIsIconVisible = true;
 
@ViewDebug.ExportedProperty(category = "launcher")
 
private int mTextColor;
 
@ViewDebug.ExportedProperty(category = "launcher")
 
private float mTextAlpha = 1;
 
 
@ViewDebug.ExportedProperty(category = "launcher")
 
private DotInfo mDotInfo;
 
private DotRenderer mDotRenderer;
 
@ViewDebug.ExportedProperty(category = "launcher", deepExport = true)
 
private DotRenderer.DrawParams mDotParams;
 
private Animator mDotScaleAnim;
 
private boolean mForceHideDot;
 
 
@ViewDebug.ExportedProperty(category = "launcher")
 
private boolean mStayPressed;
 
@ViewDebug.ExportedProperty(category = "launcher")
 
private boolean mIgnorePressedStateChange;
 
@ViewDebug.ExportedProperty(category = "launcher")
 
private boolean mDisableRelayout = false;
 
 
private IconLoadRequest mIconLoadRequest;
 
 
public BubbleTextView(Context context) {
 
this(context, null, 0);
 
}
 
 
public BubbleTextView(Context context, AttributeSet attrs) {
 
this(context, attrs, 0);
 
}
 
 
public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
 
super(context, attrs, defStyle);
 
mActivity = ActivityContext.lookupContext(context);
 
mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
 
 
TypedArray a = context.obtainStyledAttributes(attrs,
 
R.styleable.BubbleTextView, defStyle, 0);
 
mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
 
 
int display = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
 
final int defaultIconSize;
 
if (display == DISPLAY_WORKSPACE) {
 
DeviceProfile grid = mActivity.getWallpaperDeviceProfile();
 
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
 
setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
 
defaultIconSize = grid.iconSizePx;
 
mIconTextSize = grid.iconTextSizePx;
 
} else if (display == DISPLAY_ALL_APPS) {
 
DeviceProfile grid = mActivity.getDeviceProfile();
 
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
 
setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
 
defaultIconSize = grid.allAppsIconSizePx;
 
mIconTextSize = grid.allAppsIconTextSizePx;
 
} else if (display == DISPLAY_FOLDER) {
 
DeviceProfile grid = mActivity.getDeviceProfile();
 
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.folderChildTextSizePx);
 
setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx);
 
defaultIconSize = grid.folderChildIconSizePx;
 
mIconTextSize = grid.folderChildTextSizePx;
 
} else {
 
defaultIconSize = mActivity.getDeviceProfile().iconSizePx;
 
mIconTextSize = mActivity.getWallpaperDeviceProfile().iconTextSizePx;
 
}
 
mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);
 
 
mIconSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconSizeOverride,
 
defaultIconSize);
 
a.recycle();
 
 
mLongPressHelper = new CheckLongPressHelper(this);
 
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
 
 
mDotParams = new DotRenderer.DrawParams();
 
 
setMaxLines(mActivity.getDeviceProfile().maxIconLabelLines);
 
setEllipsize(TruncateAt.END);
 
setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
 
setTextAlpha(1f);
 
}
 
/**
* Applies the item info if it is same as what the view is pointing to currently.
*/
 
@Override
 
public void reapplyItemInfo(ItemInfoWithIcon info) {
 
if (getTag() == info) {
 
mIconLoadRequest = null;
 
mDisableRelayout = true;
 
 
// Optimization: Starting in N, pre-uploads the bitmap to RenderThread.
 
info.iconBitmap.prepareToDraw();
 
 
if (info instanceof AppInfo) {
 
//加载app图标
 
applyFromApplicationInfo((AppInfo) info);
 
} else if (info instanceof WorkspaceItemInfo) {
 
applyFromWorkspaceItem((WorkspaceItemInfo) info);
 
mActivity.invalidateParent(info);
 
} else if (info instanceof PackageItemInfo) {
 
applyFromPackageItemInfo((PackageItemInfo) info);
 
}
 
 
mDisableRelayout = false;
 
}
 
}
 
reapplyItemInfo()负责加载appInfo workspaceitem PackageItemInfo的相关信息
 
 
public void applyFromApplicationInfo(AppInfo info) {
 
applyIconAndLabel(info);
 
 
// We don't need to check the info since it's not a WorkspaceItemInfo
 
super.setTag(info);
 
 
// Verify high res immediately
 
verifyHighRes();
 
 
if (info instanceof PromiseAppInfo) {
 
PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
 
applyProgressLevel(promiseAppInfo.level);
 
}
 
applyDotState(info, false /* animate */);
 
}
 
 
public void applyFromPackageItemInfo(PackageItemInfo info) {
 
applyIconAndLabel(info);
 
// We don't need to check the info since it's not a WorkspaceItemInfo
 
super.setTag(info);
 
 
// Verify high res immediately
 
verifyHighRes();
 
}
 
 
private void applyIconAndLabel(ItemInfoWithIcon info) {
 
FastBitmapDrawable iconDrawable = DrawableFactory.INSTANCE.get(getContext())
 
.newIcon(getContext(), info);
 
mDotParams.color = IconPalette.getMutedColor(info.iconColor, 0.54f);
 
 
setIcon(iconDrawable);
 
setText(info.title);
 
if (info.contentDescription != null) {
 
setContentDescription(info.isDisabled()
 
? getContext().getString(R.string.disabled_app_label, info.contentDescription)
 
: info.contentDescription);
 
}
 
}

4、9.0 10.0时钟动态图标的定制化功能实现

private void applyIconAndLabel(ItemInfoWithIcon info) {
 
String pkgname = "";
 
if (info.getIntent() != null && info.getIntent().getComponent() != null) {
 
pkgname = info.getIntent().getComponent().getPackageName();
 
}
 
android.util.Log.e("Launcher3","pkgname:"+pkgname);
 
FastBitmapDrawable iconDrawable = null;
 
if(pkgname.equals("com.android.deskclock")){
 
DeskClockUtil.getInstance().setListener(new DeskClockUtil.OnDeskClockIconChangeListener() {
 
@Override
 
public void onChange(Bitmap icon) {
 
FastBitmapDrawable deskiconDrawable = new FastBitmapDrawable(icon);
 
android.util.Log.e("Launcher3","deskiconDrawable:"+deskiconDrawable+"--icon:"+icon);
 
if(deskiconDrawable!=null)setIcon(deskiconDrawable);
 
}
 
}, getContext());
 
}else{
 
iconDrawable = DrawableFactory.INSTANCE.get(getContext())
 
.newIcon(getContext(), info);
 
}
 
//FastBitmapDrawable iconDrawable = DrawableFactory.INSTANCE.get(getContext())
 
//.newIcon(getContext(), info);
 
mDotParams.color = IconPalette.getMutedColor(info.iconColor, 0.54f);
 
if(iconDrawable!=null)setIcon(iconDrawable);
 
setText(info.title);
 
if (info.contentDescription != null) {
 
setContentDescription(info.isDisabled()
 
? getContext().getString(R.string.disabled_app_label, info.contentDescription)
 
: info.contentDescription);
 
}
 
}

5、在Launcher.java的onResume()和onPause()中分别开始和暂停

protected void onResume() {
 
......
 
/* begin /DeskClockUtil.getInstance().onResume(this);/ end */
 
if (mLauncherCallbacks != null) {
 
mLauncherCallbacks.onResume();
 
}
 
}
 
@Override
 
protected void onPause() {
 
/* begin /DeskClockUtil.getInstance().onPause();/ end */
 
....
 
}