Android系統中Launcher桌面圖標簡答來講就是通過PackageManager獲取對應APP的Icon即可,所獲取的ICON就是簡單地標准圖標,對於Android來說,其標准的ICON為72*72簡單桌面實現可以直接根據自身ICON標准,對該icon進行放大縮小後繪制即可。不過就目前來說,由於Android並未規定圖標的標准樣式,所以各家所制作的ICON各式各樣,有的就是簡單圖標,有的是在方形背板基礎上繪制ICON。所以各家桌面在自己繪制ICON時,都會對其增加背板,以統一各廠家APP圖標,以標准的樣式進行展示。如果背板是標准的,那問題也很好辦,例如背板80*80的方形,圖標縮放為72*72,直接繪制在(4,4)的位置即可。而隨著各家桌面的不斷進化,對圖標繪制的要求也越來越高,各家允許使用自定義的背板,簡單來說就是背板可以使方的、圓的、甚至是不規則的圖形。這就使得ICON繪制時不能采用預定義配置的方式,需要根據背板的實際樣式對ICON進行裁剪後繪制。簡單來說就是按照背板的樣式裁剪ICON,使ICON在背板中留出一定寬度的邊沿後,在背板正中心位置進行繪制。
初拿到這個需求後,稍稍一考慮,很簡單!從實現角度分析需求就是按照比背板小的邊緣對ICON進行裁剪。分解來說就是:1.如何識別背板的邊緣。2.如何進行裁剪。背板的邊緣很好識別,通過getAlpha獲取背板的Alpha即可。如何裁剪那,有點圖像處理知識的就知道,只需要通過簡單地4點采樣,判斷當前點是否需要切掉即可。說干就干,第一版的代碼如下:
/**/
/*背板留出5像素邊*/
private static final int EDGE_WIDTH = 5;
/*alpha值的最低值,低於該值則認為是透明*/
private static final int ALPHA_BLUR = 200;
/**
* 返回圖標按背板裁剪後得Bitmap
* @param background 背板的Bitmap
* @param icon 圖標的Bitmap
* */
public static Bitmap getBitmapWithNoScale(Drawable background, Bitmap icon){
/*首先調整icon大小與背板一致,通過縮放或居中顯示*/
if(icon.getWidth() > background.getIntrinsicWidth()){
icon = Bitmap.createScaledBitmap(icon, background.getIntrinsicWidth(), background.getIntrinsicHeight(), true);
}
if(icon.getWidth() < background.getIntrinsicWidth() ){
Bitmap tmp = null;
try {
tmp = Bitmap.createBitmap(background.getIntrinsicWidth(), background.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
} catch (OutOfMemoryError e) {
// 如果發生了OOM問題, 重新申請一次
tmp = Bitmap.createBitmap(background.getIntrinsicWidth(), background.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
e.printStackTrace();
}
Canvas mCanvas = new Canvas(tmp);
mCanvas.drawBitmap(icon,(background.getIntrinsicWidth()-icon.getWidth())/2,(background.getIntrinsicHeight()-icon.getHeight())/2, null);
icon = tmp;
}
return getBitmapAlpha(background, icon);
}
private static ImageView mGarbage ;
/*obj 背板,res 圖標*/
private static Bitmap getBitmapAlpha(Drawable backgroundDrawable, Bitmap icon){
// Bitmap myAlpha = obj.extractAlpha();
Bitmap background = ((BitmapDrawable)backgroundDrawable).getBitmap();
Bitmap alpha2 = background.extractAlpha();
int back_width = background.getWidth();
int back_height = background.getHeight();
int icon_width = icon.getWidth();
int icon_height = icon.getHeight();
int alpha_arr[] = new int[back_width*back_height];
alpha2 = alpha2.copy(Config.ARGB_8888, true);
alpha2.getPixels(alpha_arr, 0, back_width, 0, 0, back_width, back_height);
int icon_arr[] = new int[icon_width*icon_height];
icon.getPixels(icon_arr, 0, icon_width, 0, 0, icon_width, icon_height);
int back_startx = 0, back_starty = 0, back_endx = 0, back_endy = 0;
/*在圖標比背板小的情況下,圖標在背板中心位置,相對於背板的位置*/
int icon_startx,icon_starty,icon_endx,icon_endy;
int icon_offset = 0;
if(icon_width < back_width){
/*圖標比較小哈,將其居中顯示裁剪*/
Log.e("pluszhang","get icon smaller");
back_startx = EDGE_WIDTH;
back_endx = back_width-EDGE_WIDTH-1;
back_starty = EDGE_WIDTH;
back_endy = back_height-EDGE_WIDTH-1;
icon_offset = back_width-icon_width > EDGE_WIDTH*2?0:EDGE_WIDTH-(back_width-icon_width)/2;
}
else if(icon_width == back_width){
/*直接裁剪即可*/
back_startx = EDGE_WIDTH;
back_endx = back_width-EDGE_WIDTH-1;
back_starty = EDGE_WIDTH;
back_endy = back_height-EDGE_WIDTH-1;
}
for(int i=0; i<icon_height;i++){
for(int j=0; j<icon_width;j++){
if(i<back_starty||i>back_endy||j<back_startx||j>back_endx){
icon_arr[i*icon_width+j] = 0;
}
else{
if((alpha_arr[(i-EDGE_WIDTH)*back_width+j] >> 24 &0xff) < ALPHA_BLUR ||
(alpha_arr[(i+EDGE_WIDTH)*back_width+j] >> 24&0xff)< ALPHA_BLUR ||
(alpha_arr[(j-EDGE_WIDTH)+i*back_width] >> 24&0xff)< ALPHA_BLUR ||
(alpha_arr[(j+EDGE_WIDTH)+i*back_width] >> 24&0xff)< ALPHA_BLUR){
icon_arr[i*icon_width+j] = 0;
}
}
}
}
mGarbage.setBackgroundDrawable(backgroundDrawable);
mGarbage.setImageBitmap(Bitmap.createBitmap(icon_arr, icon_width, icon_height, Config.ARGB_8888));
return mGarbage.getDrawingCache();
}
以上這段代碼是采用切的方法實現對圖標處理方法,主要來講就是首先進行標准化,將ICON與背板處理到相同大小,只縮不放大,防止ICON變形,通過背板的Alpha視圖,采樣繪制ICON,最終實現對ICON的繪制。通過對上述代碼分析,由於對圖標采用的是掃描方式進行處理,也就是說80X80的圖標,要計算6400次,一個圖標還好,要是有2,3百個圖標,效率確實有些低。關鍵是效果不太好,由於該方法采用的是采樣的方式,對於背板特別不規則的會導致圖標邊緣切割有許多毛刺,這個問題是該算法自身的問題,采用切的算法,不會有更好的效果,這是該算法本身決定的,如果要達到最優效果,只有更換算法換一種思路。