android實現icon動態旋轉效果
本文實例為大傢分享瞭android實現icon動態旋轉效果的具體代碼,供大傢參考,具體內容如下
碰到客戶的這樣一個需求,點擊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(); }
以上就是本文的全部內容,希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。
推薦閱讀:
- Android顏色處理SweepGradient掃描及梯度渲染示例
- Android自定義view仿QQ的Tab按鈕動畫效果(示例代碼)
- Android圓形控件實現畫圓效果
- Android Studio實現幀動畫
- Android實現微信朋友圈圖片和視頻播放