主页固定显示
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package com.uiui.iconloader;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
|
||||
assertEquals("com.ttstd.iconloader.test", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
1
iconloader/src/main/AndroidManifest.xml
Normal file
1
iconloader/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
||||
<manifest package="com.uiui.iconloader" />
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.uiui.iconloader;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
|
||||
public class CacheKeyGenerator {
|
||||
// 生成ComponentName的标准字符串格式(包名/类名)
|
||||
public static String generateKey(ComponentName componentName) {
|
||||
return componentName.flattenToShortString(); // 如:com.example.app/.MainActivity
|
||||
}
|
||||
|
||||
// 从Intent中提取ComponentName作为键
|
||||
public static String generateKey(Intent intent) {
|
||||
return generateKey(intent.getComponent());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.uiui.iconloader;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.collection.LruCache;
|
||||
|
||||
import com.jakewharton.disklrucache.DiskLruCache;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class IconCacheManager {
|
||||
private static final String TAG = "IconCacheManager";
|
||||
private LruCache<String, Drawable> mMemoryCache;
|
||||
private DiskLruCache mDiskCache;
|
||||
|
||||
public IconCacheManager(Context context) {
|
||||
// 内存缓存(最大8MB)
|
||||
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
|
||||
int cacheSize = maxMemory / 8;
|
||||
mMemoryCache = new LruCache<String, Drawable>(cacheSize) {
|
||||
@Override
|
||||
protected int sizeOf(String key, Drawable icon) {
|
||||
if (icon instanceof BitmapDrawable) {
|
||||
return ((BitmapDrawable) icon).getBitmap().getByteCount() / 1024;
|
||||
}
|
||||
return 1; // 非Bitmap对象按1KB计算
|
||||
}
|
||||
};
|
||||
|
||||
// 磁盘缓存(50MB)
|
||||
File cacheDir = new File(context.getCacheDir(), "icon_cache");
|
||||
try {
|
||||
mDiskCache = DiskLruCache.open(cacheDir, 1, 1, 50 * 1024 * 1024);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "IconCacheManager: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public Drawable getFromMemory(String key) {
|
||||
return mMemoryCache.get(key);
|
||||
}
|
||||
|
||||
public Drawable getFromDisk(String key) {
|
||||
try {
|
||||
DiskLruCache.Snapshot snapshot = mDiskCache.get(key);
|
||||
if (snapshot != null) {
|
||||
InputStream in = snapshot.getInputStream(0);
|
||||
return Drawable.createFromStream(in, null);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e("IconCache", "Disk read error", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void putToMemory(String key, Drawable icon) {
|
||||
mMemoryCache.put(key, icon);
|
||||
}
|
||||
|
||||
|
||||
public void putToDisk(String key, Drawable icon) {
|
||||
try {
|
||||
DiskLruCache.Editor editor = mDiskCache.edit(key);
|
||||
if (icon instanceof BitmapDrawable) {
|
||||
Bitmap bitmap = ((BitmapDrawable) icon).getBitmap();
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, editor.newOutputStream(0));
|
||||
editor.commit();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e("IconCache", "Disk write error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
97
iconloader/src/main/java/com/uiui/iconloader/IconLoader.java
Normal file
97
iconloader/src/main/java/com/uiui/iconloader/IconLoader.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package com.uiui.iconloader;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.AdaptiveIconDrawable;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.loader.content.AsyncTaskLoader;
|
||||
|
||||
public class IconLoader extends AsyncTaskLoader<Drawable> {
|
||||
private final ComponentName mComponentName; // 关键:使用ComponentName标识组件
|
||||
private final IconCacheManager mCache;
|
||||
|
||||
public IconLoader(Context context, ComponentName componentName, IconCacheManager cache) {
|
||||
super(context);
|
||||
mComponentName = componentName;
|
||||
mCache = cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStartLoading() {
|
||||
String key = CacheKeyGenerator.generateKey(mComponentName);
|
||||
// 1. 检查内存缓存
|
||||
Drawable cachedIcon = mCache.getFromMemory(key);
|
||||
if (cachedIcon != null) {
|
||||
deliverResult(cachedIcon); // 直接返回缓存
|
||||
return;
|
||||
}
|
||||
forceLoad(); // 触发后台加载
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable loadInBackground() {
|
||||
String key = CacheKeyGenerator.generateKey(mComponentName);
|
||||
|
||||
// 1. 检查内存缓存
|
||||
Drawable cachedIcon = mCache.getFromMemory(key);
|
||||
if (cachedIcon != null) return cachedIcon;
|
||||
|
||||
// 2. 检查磁盘缓存
|
||||
Drawable diskCached = mCache.getFromDisk(key);
|
||||
if (diskCached != null) {
|
||||
mCache.putToMemory(key, diskCached);
|
||||
return diskCached;
|
||||
}
|
||||
|
||||
// 3. 从PackageManager加载组件专属图标
|
||||
try {
|
||||
PackageManager pm = getContext().getPackageManager();
|
||||
ActivityInfo info = pm.getActivityInfo(mComponentName, 0); // 获取ActivityInfo
|
||||
Drawable rawIcon = info.loadIcon(pm); // 加载该组件的图标
|
||||
|
||||
// 4. 图标处理(压缩/主题适配)
|
||||
Drawable processedIcon = processIcon(rawIcon);
|
||||
|
||||
// 5. 更新缓存
|
||||
mCache.putToMemory(key, processedIcon);
|
||||
mCache.putToDisk(key, processedIcon);
|
||||
return processedIcon;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Drawable processIcon(Drawable icon) {
|
||||
// 自适应图标处理(Android 8.0+)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||
&& icon instanceof AdaptiveIconDrawable) {
|
||||
AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) icon;
|
||||
int targetColor = isDarkMode() ? Color.WHITE : Color.BLACK;
|
||||
adaptiveIcon.setColorFilter(targetColor, PorterDuff.Mode.SRC_ATOP); // 根据主题切换单色模式
|
||||
}
|
||||
|
||||
// 图标尺寸压缩(避免内存溢出)
|
||||
if (icon instanceof BitmapDrawable) {
|
||||
Bitmap bitmap = ((BitmapDrawable) icon).getBitmap();
|
||||
int scaledWidth = bitmap.getWidth() / 2;
|
||||
int scaledHeight = bitmap.getHeight() / 2;
|
||||
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, true);
|
||||
return new BitmapDrawable(getContext().getResources(), scaledBitmap);
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
private boolean isDarkMode() {
|
||||
return (getContext().getResources().getConfiguration().uiMode
|
||||
& Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.uiui.iconloader;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user