android icon动态旋转
jueme 人气:0碰到客户的这样一个需求,点击icon后,前景的icon开始旋转,背景的icon不动,就是这样一个效果
通过第三方的方法是不可能实现的,我这里是通过修改系统launcher的代码来实现。实现思路是在launcher中找到显示icon图标代码,并把这个图标覆盖掉。很多第手机的时钟icon是可以动态变化的,好在公司已经有人实现这个功能,可以借鉴
我这里先把时钟动态icon的实现说明下,需要的朋友可以参考。
写一个IconScript的基类继承Drawable
package com.android.launcher3; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.view.View; import android.util.Log; public class IconScript extends Drawable{ public boolean isRuning = false; public FastBitmapDrawable mFastBitmapDrawable = null; protected Paint mPaint = new Paint(); public IconScript(){ mPaint.setAntiAlias(true); mPaint.setFilterBitmap(true); } public void draw(Canvas canvas){ if(mFastBitmapDrawable != null){ Log.e("fly","IconScript="); canvas.drawBitmap(mFastBitmapDrawable.getBitmap(), null, getBounds(),mPaint);//画底图 } } /** * 运行脚本 * @param view */ public void run(View view){ isRuning = true; } /** * 停止脚本 * (未调用,暂留入口) */ public void onStop(){ isRuning = false; } /** * 暂停脚本 * (未调用,暂留入口) */ public void onPause(){ isRuning = false; } /** * 恢复脚本 * (未调用,暂留入口) */ public void onResume(){ isRuning = true; } @Override public int getOpacity() { // TODO Auto-generated method stub return 0; } @Override public void setAlpha(int arg0) { // TODO Auto-generated method stub } @Override public void setColorFilter(ColorFilter arg0) { // TODO Auto-generated method stub } @Override public int getIntrinsicWidth() { int width = getBounds().width(); if (width == 0) { width = mFastBitmapDrawable.getBitmap().getWidth(); } return width; } @Override public int getIntrinsicHeight() { int height = getBounds().height(); if (height == 0) { height = mFastBitmapDrawable.getBitmap().getHeight(); } return height; } @Override public int getMinimumWidth() { return getBounds().width(); } @Override public int getMinimumHeight() { return getBounds().height(); } @Override public void setFilterBitmap(boolean filterBitmap) { mPaint.setFilterBitmap(filterBitmap); mPaint.setAntiAlias(filterBitmap); } public void setFastBitmapDrawable(FastBitmapDrawable drawable){ mFastBitmapDrawable = drawable; } public FastBitmapDrawable setFastBitmapDrawable(){ return mFastBitmapDrawable; } }
核心类ClockScript继承IconScript
package com.android.launcher3; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.text.format.Time; import android.view.View; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; public class ClockScript extends IconScript { Rect mRect = null; /** * 效果展示目标View */ private View mView; /** * 通知系统更新视图现成 */ private ClockThread mClockThread = null; /** * 当前是否显示在屏幕上 */ private boolean mIsShowInScreen = false; Context mContext; public ClockScript(Context context){ super(); mContext = context; } public void run(View view) { mView = view; mRect = getBounds(); if(mClockThread == null){ mClockThread = new ClockThread(); mClockThread.start(); } } @Override public void onPause() { mClockThread.pauseRun(); super.onPause(); } @Override public void onResume() { mClockThread.resumeRun(); super.onResume(); } @Override public void onStop() { mClockThread.stopRun(); super.onStop(); } @Override public void draw(Canvas canvas) { super.draw(canvas); mIsShowInScreen = true; drawIndicator(canvas,mRect.centerX(),mRect.centerY(),mPaint); if(mClockThread.wait){ mClockThread.resumeRun(); } } /** * 画指针 * @param canvas * @param centerX * @param centerY * @param p */ private void drawIndicator(Canvas canvas,int centerX,int centerY,Paint p){ Bitmap clockIcon = Utilities.createIconBitmap( BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher_clock),mContext); int X = clockIcon.getWidth()/2; int Y = clockIcon.getHeight()/2; canvas.drawBitmap(clockIcon, null, getBounds(),p);//画底图 Time t=new Time(); t.setToNow(); p.setAntiAlias(true); p.setStrokeWidth(3); p.setColor(Color.WHITE); p.setStyle(Paint.Style.FILL); //hour canvas.drawLine(X, Y, int)(X + (clockIcon.getWidth()/2-35) * Math.cos((t.hour+(float)t.minute/60) * (Math.PI / 6) - Math.PI / 2)), (int)(Y + (clockIcon.getWidth()/2-35) * Math.sin((t.hour+(float)t.minute/60) * (Math.PI / 6) - Math.PI / 2)), p); //minute canvas.drawLine(X, Y,(int)(X + (clockIcon.getWidth()/2-27) * Math.cos(t.minute * (Math.PI / 30) - Math.PI / 2)),(int)(Y + (clockIcon.getWidth()/2-27) * Math.sin(t.minute * (Math.PI / 30) - Math.PI / 2)),p); //second p.setColor(Color.RED); p.setStrokeWidth(1); p.setStyle(Paint.Style.FILL); canvas.drawLine(X, Y,(int)(X + (clockIcon.getWidth()/2-20) * Math.cos(t.second * (Math.PI / 30) - Math.PI / 2)),(int)(Y + (clockIcon.getWidth()/2-20) * Math.sin(t.second * (Math.PI / 30) - Math.PI / 2)),p); p.setColor(Color.WHITE); canvas.drawCircle(X, Y, 4, p); p.setColor(Color.GRAY); canvas.drawCircle(X, Y, 2, p); } class ClockThread extends Thread { int times = 0; boolean running = true; public boolean wait = false; public void stopRun() { running = false; synchronized (this) { this.notify(); } }; public void pauseRun() { this.wait = true; synchronized (this) { this.notify(); } } public void resumeRun() { this.wait = false; synchronized (this) { this.notify(); } } public void run() { while (running) { synchronized (mView) { mView.postInvalidate(); } if(!mIsShowInScreen){ pauseRun(); } mIsShowInScreen = false; try { Thread.sleep(500); } catch (Exception e) { System.out.println(e); } synchronized (this) { if (wait) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } } } }
接下来就是如何初始化这个ClockScript,在Icon类里面添加代码
--- a/packages/apps/Launcher3/src/com/android/launcher3/IconCache.java +++ b/packages/apps/Launcher3/src/com/android/launcher3/IconCache.java @@ -84,6 +84,7 @@ public class IconCache { public CharSequence title = ""; public CharSequence contentDescription = ""; public boolean isLowResIcon; + public IconScript script; } private final HashMap<UserHandleCompat, Bitmap> mDefaultIcons = new HashMap<>(); @@ -591,7 +592,17 @@ public class IconCache { if (info != null) { entry.title = info.getLabel(); } - + + Log.e("IconCache ","componentName.getPackageName()="+componentName.getPackageName()); //加了一个系统的属性来控制 + if(null != entry && componentName.getPackageName().equals("com.android.deskclock") && android.os.SystemProperties.getBoolean("launcher.calender.updateicon", false)) + { + Log.e("IconCache","clock init"); + entry.script = new ClockScript(mContext); + } return entry; } @@ -891,4 +902,20 @@ public class IconCache { return null; } } + public IconScript getScript(Intent intent, UserHandleCompat user){ + synchronized (mCache) { + ComponentName component = intent.getComponent(); + + if (component == null) { + Log.e("IconCache ","component==null"); + return null; + } + LauncherActivityInfoCompat launcherActInfo = mLauncherApps.resolveActivity(intent, user); + CacheEntry entry = cacheLocked(component, launcherActInfo,user, false, false); + return entry.script; + } + }
在BubbleTextView类中添加代码
--- a/packages/apps/Launcher3/src/com/android/launcher3/BubbleTextView.java +++ b/packages/apps/Launcher3/src/com/android/launcher3/BubbleTextView.java @@ -30,6 +30,7 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.util.AttributeSet; +import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; import android.view.KeyEvent; @@ -38,6 +39,7 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.ViewParent; import android.widget.TextView; +import android.graphics.Rect; import com.android.launcher3.IconCache.IconLoadRequest; import com.android.launcher3.model.PackageItemInfo; @@ -148,12 +150,44 @@ public class BubbleTextView extends TextView public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache) { applyFromShortcutInfo(info, iconCache, false); - } + + mScript = info.getScript(iconCache); //zengxiao add + if(mScript!=null){ + if(mScript!=null){ + }else{ + Log.e("rtyre","info.iconResource.packageName ------null"); + } + } + private IconScript mScript; + @Override + public void setCompoundDrawables(Drawable left, Drawable top, + Drawable right, Drawable bottom) { + + if(top != null){ + + if(mScript != null){ + + top = mScript; + Rect rect=new Rect(0,0,LauncherAppState.getInstance().getInvariantDeviceProfile().iconBitmapSize,LauncherAppState.getInstanc + mScript.setBounds(rect); + + if(!mScript.isRuning) + { + + mScript.run(this); + } + } + } + + super.setCompoundDrawables(left, top, right, bottom); + } public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache, boolean promiseStateChanged) { Bitmap b = info.getIcon(iconCache); - + mScript = info.getScript(iconCache); FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(b);
--- a/packages/apps/Launcher3/src/com/android/launcher3/ShortcutInfo.java +++ b/packages/apps/Launcher3/src/com/android/launcher3/ShortcutInfo.java @@ -304,5 +304,13 @@ public class ShortcutInfo extends ItemInfo { public boolean isDisabled() { return isDisabled != 0; } + + + public IconScript getScript(IconCache iconCache){ + return iconCache.getScript(promisedIntent != null ? promisedIntent : intent, user); + } + }
把这些代码添加上功能基本ok.
有了这个基础,我想要实现的效果就变得很简单,同样的定义一个WallpaperScript继承IconScript
package com.android.launcher3; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.text.format.Time; import android.view.View; import android.util.Log; import android.graphics.Rect; import android.graphics.Typeface; import android.graphics.Paint; import java.util.Calendar; import android.graphics.BitmapFactory; import android.graphics.Matrix; public class WallpaperScript extends IconScript { private float mDensity = 1.5f; Time mTime = new Time(); int myCount =0; Context mContext; Boolean isDraw =false; /** * 效果展示目标View */ private View mView; /** * 当前是否显示在屏幕上 */ private boolean mIsShowInScreen = false; /** * 通知系统更新视图现成 */ private WallpaperThread mWallpaperThread = null; int[] arr=new int[]{R.drawable.ic_launcher_wallpaper_1,R.drawable.ic_launcher_wallpaper_2,R.drawable.ic_launcher_wallpaper_3, R.drawable.ic_launcher_wallpaper_4,R.drawable.ic_launcher_wallpaper_5,R.drawable.ic_launcher_wallpaper_6 }; int index = 0; public WallpaperScript(Context context) { super(); mContext = context; } public void run(View view) { mView = view; if(mWallpaperThread == null){ //Log.d("WallpaperScript","mWallpaperThread "); mWallpaperThread = new WallpaperThread(); mWallpaperThread.start(); } IntentFilter filter = new IntentFilter(); filter.addAction("android.intent.action.WallpaperChange"); view.getContext().registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context arg0, Intent arg1) { Log.d("WallpaperScript","onReceive "); isDraw = true; mWallpaperThread.startRun(); mWallpaperThread.start(); // myCount =0; } }, filter); } @Override public void onPause() { mWallpaperThread.pauseRun(); super.onPause(); } @Override public void onResume() { mWallpaperThread.resumeRun(); super.onResume(); } @Override public void onStop() { mWallpaperThread.stopRun(); super.onStop(); } @Override public void draw(Canvas canvas) { super.draw(canvas); Bitmap wallpaperIconfirst = Utilities.createIconBitmap( BitmapFactory.decodeResource(mContext.getResources(), arr[0]),mContext); canvas.drawBitmap(wallpaperIconfirst, null, getBounds(),mPaint);//默认显示的图片 if(isDraw){ index = index%6; Bitmap wallpaperIcon = Utilities.createIconBitmap( BitmapFactory.decodeResource(mContext.getResources(), arr[index]),mContext); index = index+1; myCount =myCount+1; canvas.drawBitmap(wallpaperIcon, null, getBounds(),mPaint);//画底图 //Log.d("WallpaperScript","WallpaperScript index "+index+" myCount "+myCount); if(myCount==49){ myCount=0; mWallpaperThread.stopRun(); //Log.d("WallpaperScript","WallpaperScript myCount " +myCount); isDraw = false; } } } class WallpaperThread extends Thread { int times = 0; boolean running = true; public boolean wait = false; public void startRun() { running = true; synchronized (this) { this.notify(); } }; public void stopRun() { running = false; synchronized (this) { this.notify(); } }; public void pauseRun() { /*this.wait = true; synchronized (this) { this.notify(); } */ } public void resumeRun() { /*this.wait = false; synchronized (this) { this.notify(); }*/ } public void run() { Log.d("WallpaperScript","WallpaperThread running "+ running); while (running) { synchronized (mView) { Log.d("WallpaperScript","WallpaperThread run()"); mView.postInvalidate(); } try { Thread.sleep(50); } catch (Exception e) { System.out.println(e); } } } } }
把所有的图片放到一个数组里面,然后轮流去绘制里面的图片,点击图标的时候会发送一个广播,通过广播去控制线程的开启,这样功能基本上实现。
另外,怎样去实现没有界面的app,这个只需要AndroidManifest设置。
android:theme="@android:style/Theme.NoDisplay"
切换锁屏壁纸和主屏幕壁纸的代码
WallpaperManager manager = WallpaperManager.getInstance(this); try { manager.setBitmap(bitmap,null, true, WallpaperManager.FLAG_LOCK | WallpaperManager.FLAG_SYSTEM); } catch (Exception e) { e.printStackTrace(); }
加载全部内容