由于最近在负责MTK5.1的Android系统开发,所以本文就以5.1的代码为参考。其它版本虽然会略有不同,但是修改思路是大致相同的。

在市面上很多手机都会对原生桌面进行一些修改,比如把时钟、日历修改成动态显示,或者对整个界面风格进行修改。那么我们就来模仿一下,简单地修改原生应用的图标显示,从而达到修改主题样式的目的。

5.1上的Android桌面,其实也就是Launcher3,位置是
alps\packages\apps\Launcher3。


思路

我先说下核心思路:在Launcher3获取应用图标时都会经过一个类:IconCache.java,具体位置是
alps\packages\apps\Launcher3\src\com\android\launcher3\IconCache.java.

在这个类中有一个关键的方法:cacheLocked

    private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info,HashMap<Object, CharSequence> labelCache, UserHandleCompat user, boolean usePackageIcon) {......}

Launcher3获取应用图标的方法有好几个,但是这些方法最终都会调用cacheLocked获取应用图标,因此,我们主要的切换主题的逻辑就放在这里做好了。


步骤

一:

在IconCache类中定义全局变量:

    private int mThemeCode;private List<String> mThemePackageNames;private static final String[] PACKAGE_NAME = {"com.android.fmradio","com.android.email","com.android.music","com.android.gallery3d","com.android.providers.downloads.ui","com.android.browser","com.mediatek.filemanager","com.android.calculator2","com.android.calendar","com.mediatek.camera","com.android.contacts","com.android.deskclock","com.android.dialer","com.android.mms","com.android.settings","com.android.soundrecorder","com.android.stk","com.mediatek.notebook"};

其中PACKAGE_NAME中的包名就是要切换图标的应用包名,mThemeCode是每个主题所对应的值,mThemePackageNames是为了方便处理PACKAGE_NAME中的包名的判断的。


二:

在IconCache的构造函数中初始化成员变量:

    public IconCache(Context context) {......//mikechenmj addSharedPreferences sharedPreferences = mContext.getSharedPreferences("theme_code",Context.MODE_PRIVATE);mThemeCode = sharedPreferences.getInt("theme",0);mThemePackageNames = Arrays.asList(PACKAGE_NAME);//end}

这里通过SharedPreferences来获取mThemeCode的值,不然当手机重启后主题就会被恢复原样了。然后将PACKAGE_NAME转化成一个List赋值给mThemePackageNames。


三:

在cacheLocked方法中修改指定应用的图标。

    private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info,HashMap<Object, CharSequence> labelCache, UserHandleCompat user, boolean usePackageIcon) {......CacheKey cacheKey = new CacheKey(componentName, user);CacheEntry entry = mCache.get(cacheKey);......//mikechenmj addif(entry.icon != null) {Bitmap themeBitmap = getThemeBitmap(componentName ,entry.icon.getWidth(),entry.icon.getHeight());if(themeBitmap != null) {entry.icon = themeBitmap;}}//endreturn entry;......private static class CacheEntry {public Bitmap icon;public CharSequence title;public CharSequence contentDescription;}

在cacheLocked中,通过方法getThemeBitmap获取新的Bitmap,并赋值给entry。getThemeBitmap方法是一个自己编写的获取指定Bitmap的方法。


在IconCache.java中定义需要的方法

    public void setThemeCode(int code) {mThemeCode = code;flush();SharedPreferences sharedPreferences = mContext.getSharedPreferences("theme_code",Context.MODE_PRIVATE);SharedPreferences.Editor editor = sharedPreferences.edit();editor.putInt("theme",mThemeCode);editor.commit();}private Bitmap getThemeBitmap(ComponentName componentName, int reqWidth, int reqHeight) {+ mThemeCode);if (mThemeCode > 0) {String packageName = componentName.getPackageName();if (mThemePackageNames.contains(packageName)) {StringBuilder identifierName = new StringBuilder();int resId;if (packageName.equals("com.android.gallery3d")&& componentName.getClassName().equals("com.android.camera.CameraLauncher")) {identifierName.append("com_mediatek_camera_theme_").append(mThemeCode);resId = mContext.getResources().getIdentifier(identifierName.toString(),"drawable", mContext.getPackageName());} else {identifierName.append(packageName.replace(".", "_")).append("_theme_").append(mThemeCode);resId = mContext.getResources().getIdentifier(identifierName.toString(),"drawable", mContext.getPackageName());}if (resId == 0) {return null;}return decodeResource(mContext, resId, reqWidth, reqHeight);}}return null;}public static Bitmap decodeResource(Context context, int resId, int reqWidth, int reqHeight) {Resources resources = context.getResources();BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeResource(resources, resId, options);options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);options.inJustDecodeBounds = false;Bitmap bitmap = BitmapFactory.decodeResource(resources, resId);return bitmap;}public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth, int reqHeight) {final int height = options.outHeight;final int width = options.outWidth;int inSampleSize = 1;if (height > reqHeight || width > reqWidth) {final int heightRatio = Math.round((float) height / (float) reqHeight);final int widthRatio = Math.round((float) width / (float) reqWidth);inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;}return inSampleSize;}

其中setThemeCode方法是提供给Launcher3的主活动:Launcher.java调用的。
在这个方法中更新了mThemeCode并保存到了SharedPreferences当中,并且清除了缓存,以免被以前的主题影响。

getThemeBitmap方法主要是根据componentName中携带的包名判断是否需要更图标以及合成需要的图标的名称,并通过decodeResource将其解析出来。当mThemeCode不大于0时,代表使用的是默认的风格,所以不往下执行代码。
而图片的命名都是与包名对应的,比如com_android_browser_theme_1.png对应的就是主题值为1,包名为com.android.browser的应用。
除此之外,由于5.1中的相机和图库的包名是同一个,所以判断相机时加上了类名作为判断条件:
componentName.getClassName().equals(“com.android.camera.CameraLauncher”)

decodeResource方法主要是通过calculateInSampleSize方法获取到合适的options.inSampleSize值,然后根据这个值将图标的大小调整合适,再解析出来。


IconCache.java修改完了,接下来就是要想办法去选择并传递ThemeCode。我们可以在主活动Launcher中的OverviewMode模式(既长按桌面进入的调整桌面的模式)中添加一个Button来启动一个新的活动,在新活动中选择主题,并把主题的值返回给Launcher,Launcher再调用IconCache的setThemeCode方法把ThemeCode传递给IconCache,然后通知桌面刷新。代码如下:

在res/layout/overview_panel.xml中新加一个Button

......<TextViewandroid:visibility="gone"android:id="@+id/themes_button"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:drawablePadding="4dp"android:drawableTop="@drawable/theme_button"android:fontFamily="sans-serif-condensed"android:gravity="center_horizontal"android:text="@string/theme_button_text"android:textAllCaps="true"android:textSize="12sp" />

其中android:drawableTop属性中的@drawable/theme_button是自己定义的一个xml文件,里面定义了按键被按时和平时显示的图片。


在Launcher初始化时初始化新加的Button,并设置按键事件。

            View themeButton = findViewById(R.id.themes_button);themeButton.setVisibility(View.VISIBLE);themeButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View arg0) {Intent intent = new Intent("mikechenmj.android.activity.LAUNCHER_THEME_PICKER");startActivityForResult(intent,THEME_PICKER_REQUEST_CODE);}});themeButton.setOnTouchListener(getHapticFeedbackTouchListener());

当点击这个按钮的时候,会启动一个接收
mikechenmj.android.activity.LAUNCHER_THEME_PICKER这个Action的活动,在这个活动中选择主题风格,并且通过类似下面的方法来返回选择的themeCode。

    private void returnThemeCode(int theme) {Intent intent = new Intent();intent.putExtra("theme_code", theme);setResult(RESULT_OK, intent);finish();}

选择主题的活动的代码不重要就不贴了,反正大致就这个思路。


然后修改Launcher.java的onActivityResult,调用方法handleThemePicker。

    @Overrideprotected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {//mikechenmj addhandleThemePicker(requestCode,resultCode,data);//end......}
    private void handleThemePicker(int requestCode, int resultCode, Intent data) {if (resultCode == RESULT_OK) {if(requestCode == THEME_PICKER_REQUEST_CODE && resultCode == RESULT_OK) {int themeCode = data.getIntExtra("theme_code", 0);Resources resources = getResources();WallpaperManager manager = WallpaperManager.getInstance(this);Bitmap wallpager;if(themeCode == 0) {Resources sysRes = Resources.getSystem();int resId = sysRes.getIdentifier("default_wallpaper", "drawable", "android");wallpager = IconCache.decodeResource(this, resId,resources.getDisplayMetrics().widthPixels,resources.getDisplayMetrics().heightPixels);}else {StringBuilder stringBuilder = new StringBuilder("launcher_theme_wallpaper_");stringBuilder.append(themeCode);wallpager = IconCache.decodeResource(this,resources.getIdentifier(stringBuilder.toString(), "drawable", getPackageName()),resources.getDisplayMetrics().widthPixels,resources.getDisplayMetrics().heightPixels);}try {manager.setBitmap(wallpager);} catch (IOException e) {e.printStackTrace();}if (mState != State.WORKSPACE || mWorkspace.isInOverviewMode()) {showWorkspace(true);}mIconCache.setThemeCode(themeCode);mModel.forceReload();}}}

在handleThemePicker方法中对themeCode进行了判断,如果themeCode等于0,则代表用的是系统默认的风格,所以壁纸设置成系统默认壁纸。
如果themeCode不等于0,则根据themeCode合成对应壁纸的名称并加载出来,然后将其设置成壁纸。这里的壁纸名称也是有要求的,都是launcher_theme_wallpaper_ 然后加上themeCode。

然后通过mIconCache.setThemeCode(themeCode)方法将themeCode传递给IconCache,再调用
mModel.forceReload()方法通知桌面刷新,调用showWorkspace(true)退出OverviewMode模式。就这样,整个客制化流程就完成了。

一步步客制化Android桌面(Launcher3)图标相关推荐

  1. Android 12.0 Launcher客制化指导手册

    主要包含 Launcher3升级变化:小部件变化 . Go版本差异.支持自动生成Work文件夹 . UI客制化 :桌面布局 .布局配置 .预置图标 .图标客制化 .待机界面客制化.应用抽屉客制化 功能 ...

  2. MTK Android 13平台开关机动画铃声客制化

    MTK Android 13平台开关机动画铃声客制化 Android T和S的差异很大 主要是MtkShutdownThread.java和ShutdownThread.java差异 未完,待更新,填 ...

  3. android 7.0平台客制化虚拟导航按键(隐藏NavigationBar,上滑显示NavigationBar)

    如图,需求是增加一个按钮可以隐藏NavigationBar,上滑显示NavigationBar. 参考文章: Android 8.1平台客制化虚拟导航按键 Android 7.0 虚拟按键(Navig ...

  4. Android 11.0 充电指示灯红绿显示简单客制化

    客制化充电指示灯的文章在网上也有不少了,项目的需求是在百分百情况下量绿灯,其它情况下都是红灯,这里简单记录下在Android11,kernel-4.19版本下的修改内容,也给有相关需求的各位提供下思路 ...

  5. Android 系统序列号从哪里来,以及客制化序列号

    Android 系统序列号从哪里来,以及客制化序列号 系统获取序列号过程 客制化序列号 系统获取序列号过程 Android系统的SN号,实际是从"cmdline"里面的" ...

  6. android+动画打包命令,Android 开机动画客制化

    Android开机动画总共有三个过程.第一个开机动画是在Kenel启动时显示的,第二个开机动画是在init进程启动时显示的,这两个都是静态图片.第三个动画是在系统服务启动过程中显示的,他是一个动态图片 ...

  7. Android 手机按键客制化详解

    在Android 中会有以下5个按键(Back.Home.Menu.Power.Volume)与用户进行交互,Framework 层中实现按键功能,因此,从手机系统定制的角度,可以满足客户的客制化要求 ...

  8. Android项目客制化

    关键编译目录 Makefile build/core/Makefile build/core/main.mk build/core/config.mk build/core/envsetup.mk b ...

  9. Android 信号格客制化问题处理

    [Android Q] 信号强度客制化代码位置: NR(5G) CellSignalStrengthNr.java updateLevel() LTE MtkSignalStrength.java u ...

最新文章

  1. 【C++】多线程与原子操作和无锁编程【五】
  2. javafx窗体程序_JavaFX真实世界应用程序:EIZO CuratOR Caliop
  3. c++ 将文件内容输出到word上_原来PDF转Word可以这么简单,只需要一个键!办公起来真方便...
  4. c语言 动态链表,C语言的链表(篇章之二:动态链表)
  5. 特征匹配中OpenCV Dmatch类的用法解析以及非常详细的ORB特征提取与匹配解析
  6. Linux SD卡建立两个分区
  7. [转贴]一个农村高考落榜生的心路历程
  8. 分析137份大数据简历-统计技术名词词频
  9. Python爬虫书籍推荐
  10. 两个人相处久了会越来越像吗? | 无意识模仿的秘密
  11. USB计算机连接只能充电,手机连接不上电脑只显示充电怎么办
  12. c++17好用的新特性总结
  13. B站哔哩哔哩视频一键下载,这个视频下载工具太给力了
  14. 传奇单机版就是自己在家里架设一个
  15. 1147 简单评委打分
  16. 悼念一位腾讯游戏大佬。。
  17. iOS开源项目MobileProject功能点介绍
  18. 利用python调用本地摄像头拍照,对图片命名并保存到指定位置,带界面
  19. 未来的五年,你认为最值得创业的行业是什么?
  20. 分治法实验-寻找第k小元素

热门文章

  1. 1905协议详解(一)概述
  2. java和angular_Angular
  3. 点亮一个esp32 的led
  4. BurpSuite 破解版(含注册机,无后门)下载注册详细教程
  5. Python科学计算:用NumPy快速处理数据
  6. 关于“哥和寂寞”的语录。。。。
  7. spider_day08
  8. 封装一个完整版的uniapp图片和视频上传组件,拿来即用,可进行图片视频切换,可自定义上传按钮样式,删除按钮样式,可单独上传图片或者视频,可限制上传数量
  9. Excel清除固定单元格
  10. 阿里三面 Android 研发岗,竟然挂在了性能优化上…