活动地址:CSDN21天学习挑战赛
1、概述
在定制化10.0的项目开发中,在Launcher3的app列表页,有时钟app的图标,由于图标是静态的开发的需要要求调成动态图标,时刻显示时间,所以要把时钟图标替换成动态图标。
2、9.0 10.0时钟动态图标的定制化主要核心代码
packages/apps/Launcher3/src/com/android/launcher3/BubbleTextView.javapackages/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 / 12drgeeHour += 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() {@Overridepublic 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") {@Overridepublic Float get(BubbleTextView bubbleTextView) {return bubbleTextView.mDotParams.scale;}@Overridepublic 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") {@Overridepublic Float get(BubbleTextView bubbleTextView) {return bubbleTextView.mTextAlpha;}@Overridepublic 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(PLEX_UNIT_PX, grid.iconTextSizePx);setCompoundDrawablePadding(grid.iconDrawablePaddingPx);defaultIconSize = grid.iconSizePx;mIconTextSize = grid.iconTextSizePx;} else if (display == DISPLAY_ALL_APPS) {DeviceProfile grid = mActivity.getDeviceProfile();setTextSize(PLEX_UNIT_PX, grid.allAppsIconTextSizePx);setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);defaultIconSize = grid.allAppsIconSizePx;mIconTextSize = grid.allAppsIconTextSizePx;} else if (display == DISPLAY_FOLDER) {DeviceProfile grid = mActivity.getDeviceProfile();setTextSize(PLEX_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.*/@Overridepublic 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 WorkspaceItemInfosuper.setTag(info);// Verify high res immediatelyverifyHighRes();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 WorkspaceItemInfosuper.setTag(info);// Verify high res immediatelyverifyHighRes();}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() {@Overridepublic 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();}}@Overrideprotected void onPause() {/* begin /DeskClockUtil.getInstance().onPause();/ end */....}
如果觉得《【Android】时钟动态图标的定制化》对你有帮助,请点赞、收藏,并留下你的观点哦!