Android插件开发之-换肤功能前篇
来源:互联网 发布:app软件测试工程师 编辑:程序博客网 时间:2024/05/16 12:28
1. 前言
这人一闲着没事干就喜欢作,昨天研究了一天的换肤效果,今天总算有点眉目了,我不会录屏所以只能截个图了,效果效果来来来:
那么如何使用呢?
// 1.将资源插件打成apk,成功后将其改名为skin_plugin.skin,后缀名也可以随便取名, // 将其copy到手机根目录下面,实际项目中肯定从网上下载 // 2.建一个Application,在onCrate()中初始化皮肤管理 SkinManager.getInstance().init(this); // 3.新建一个BaseActivity extends Activity , 其他Activity继承它 public abstract class BaseActivity extends Activity implements OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 捕捉闪退信息 CustomCrashHandler.getInstance().setCustomCrashHanler(this); // 统一管理Activity AppManagerUtil.instance().addActivity(this); // 设置布局主布局 int contentViewId = getContentViewId(); if (contentViewId == 0) { throw new IllegalArgumentException("get content view id is zero!"); } setContentView(contentViewId); // 注册皮肤管理 SkinManager.getInstance().register(this); // 初始化头部 initTitle(); // 初始化界面 initView(); // 初始化数据 initData(); } protected abstract int getContentViewId(); public abstract void initTitle(); public abstract void initView(); public abstract void initData(); @Override protected void onDestroy() { super.onDestroy(); SkinManager.getInstance().unregister(this); } } // 4.在布局中,我们给View添加tag : 规则是这样的skin:main_bg:background -> // 开头:资源id:类型 = 以skin开头 + 插件中的资源Id名称 + 操作类型是src 或者 textColor ... // 5.调用换肤的方法 SkinManager.getInstance().changeSkin(mSkinPkgPath, "com.imooc.skin_plugin", new ISkinChangingCallback() { @Override public void onStart() { } @Override public void onError(Exception e) { Toast.makeText(MenuActivity.this, "换肤失败", Toast.LENGTH_SHORT).show(); } @Override public void onComplete() { Toast.makeText(MenuActivity.this, "换肤成功", Toast.LENGTH_SHORT).show(); } }); }
2.分析和实现
2.1分析:
根据效果能想到的就这么几种方式,不知道有没有更好的实现方式,先这么搞吧:
1. 建一个资源插件项目,该项目中只存放资源,没有Activity,将其在后台运行起来,当我们在主程序中需要的换肤的时候去检测运行的插件apk,通过sharedUserId加上反射去获取资源,达到换肤的效果,这种方式需要运行插件apk;
2. 资源内置在主apk中,通过动态的改变其后缀,加载不同的本地资源达到换肤的效果;
3. 自己目前用的这种方式,皮肤可以是zip可以是apk或是其他,我们联网去下载皮肤包,通过资源管理去获取插件资源,达到换肤的效果,这种方式不需要运行插件apk。
2.2实现第一种:
1.先制作资源插件apk
1.新建一个SummerSkinPlugin项目在mipmap-xhdpi中放置一张main_bg的图片; 2.在androidMainfest中配置android:sharedUserId="com.example.hui.summerplug.background" 3.将资源插件打包成apk即可
2.主程序换肤,新建AndroidExchangeSkin项目,在MainActivity先测试一下
// 在androidMainfest中配置android:sharedUserId="com.example.hui.summerplug.background" // 布局中就放置一个mExchangeSkinBt按钮,和一个全屏mMainIv图片 // 插件列表 private List<Map<String,String>> mPulginList; public void onClick(View view){ // 查找插件列表 mPulginList = findPluginList(); if(mPulginList == null || mPulginList.size() == 0){ Toast.makeText(this,"目前没有皮肤,请下载皮肤",Toast.LENGTH_LONG).show(); return; } // 用dialog显示插件的名称列表,当条目点击时获取当前插件条目map: // 1.获取插件上下文 Context pluginContext = findPluginContext(map); // 2.根据插件资源加载器,加载资源 int resourceId = findResourceId(pluginContext,map); Log.e("TAG",resourceId+""); if(resourceId != 0){ // 直接设置是不行的, (必须通过插件来加载) // iv.setImageResource(resouceId); iv.setImageDrawable(pluginContext.getResources().getDrawable(resourceId)); } } /** * 查找插件列表 **/ private List<Map<String, String>> findPluginList() { List<Map<String,String>> pluginList = new ArrayList<>(); // 获取包管理器 PackageManager packageManager = this.getPackageManager(); // 获取手机已安装的包信息 List<PackageInfo> packageInfos = packageManager.getInstalledPackages( PackageManager.GET_ACTIVITIES); try { // 获取当前APP的包信息 PackageInfo currentPackageInfo = this.getPackageManager(). getPackageInfo(getPackageName(),0); /** * 循环包信息,根据sharedUserId去找插件 **/ for (PackageInfo packageInfo:packageInfos){ String packageName = packageInfo.packageName; String sharedUserId = packageInfo.sharedUserId; if(TextUtils.isEmpty(sharedUserId) || ! sharedUserId.equals(currentPackageInfo.sharedUserId) || packageName.equals(currentPackageInfo.packageName)){ // 如果是空,或者与主程序shared user id 不同 ,或者报名相同 continue; } // 加载插件 Map<String,String> pluginMap = new HashMap<>(); // 获取插件程序的名称 String label = packageInfo.applicationInfo.loadLabel(packageManager).toString(); // 获取包名称 pluginMap.put("packageName",packageName); pluginMap.put("label",label); // 添加插件 pluginList.add(pluginMap); } } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return pluginList; } /** * 获取插件上下文 */ private Context findPluginContext(Map<String, String> map) { try{ return this.createPackageContext(map.get("packageName"), Context.CONTEXT_IGNORE_SECURITY); }catch(Exception e){ e.printStackTrace(); } return null; } /** * 查找资源的Id * 利用反射 */ private int findResourceId(Context context,Map<String, String> map) { String packageName = map.get("packageName"); // 通过类加载器加上反射获取图片资源Id ClassLoader classLoader = new PathClassLoader(context.getPackageCodePath(), PathClassLoader.getSystemClassLoader()); try { Class clazz = Class.forName(packageName+".R$mipmap",true,classLoader); Field[] fields = clazz.getFields(); for(Field field : fields){ String name = field.getName(); if(name.endsWith("main_bg")){ return field.getInt(R.drawable.class); } } } catch (Exception e) { e.printStackTrace(); } return 0; }
将插件皮肤apk和主程序都运行起来就会惊奇的发现可以换肤了,我们可以新建两套或者更多的插件皮肤,当然这只是图片,文字肯定也是一样的,这只是单个功能实现。用到项目中我们每次都写这么多代码肯定是不行的,所以肯定要进行封装的。
最后就要感谢Hongyang了,自己是看你的代码和你的慕课视频成长的http://blog.csdn.net/lmj623565791/article,万分感谢啊!
源码下载地址:http://download.csdn.net/detail/z240336124/9430544
0 0
- Android插件开发之-换肤功能前篇
- Android插件开发之-换肤功能后篇
- Android插件换肤功能实战
- Android中插件开发篇之----应用换肤原理解析
- Android中插件开发篇之----应用换肤原理解析
- Android中插件开发篇之----应用换肤原理解析
- Android中插件开发篇之----应用换肤原理解析
- Android中插件开发篇之----应用换肤原理解析
- Android中插件开发篇之—-应用换肤原理解析
- Android中插件开发篇之----应用换肤原理解析
- android插件化开发---换肤
- Android开发"夜间模式"换肤功能
- Android换肤之——插件换肤
- android 换肤功能
- Android插件化开发之动态加载本地皮肤包进行换肤
- Android开发之APP换肤简介
- Android插件化开发实现动态换肤
- android换肤功能实现
- IOS沙盒路径
- hdu 2844 多重背包模板题 01背包、完全背包、多重背包模板
- leetcode笔记--Partition List
- 欢迎使用CSDN-markdown编辑器
- MyBatis 实现消息页面,批量查询用户信息
- Android插件开发之-换肤功能前篇
- iOS解析XML
- Unity Physics.Raycast 之 layerMask
- 关于Listview中长按删除
- [Scala函数特性系列]——使用命名参数
- Activity登堂入室
- php设计模式
- Xcode中常用的快捷键操作
- iOS开发:设计模式那点事