anroid动态更新UI界面
Posted Jarlene
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了anroid动态更新UI界面相关的知识,希望对你有一定的参考价值。
背景
在android中,一成不变的UI布局可能会使用户厌烦(现在基本上都是ViewPager+ListView的方式),那么有没有什么方式实现动态更新UI布局提高用户的体验呢?答案是肯定的,本文就是介绍一种方式实现动态更新UI布局的方式。技术途径
在动态实现类补丁这篇文章中,我实现了动态加载类,它可以实现dalvik动态更新类(art原生支持文章提到方式),结合这篇文章我们可以很清楚明白,在实现动态更新类的时候,同时替换布局xml文件也是可以得。这个时候我们需要将dex文件,layout等资源文件一起打包生成APK。具体实现是在Activity setContentView():@Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); mContext = this; String apkPath = HookManager.getInstance().getPatchDir(mContext).getAbsolutePath() + File.separator + "DexTest.apk"; PatchResource patchResource = ResourceManager.getInstance().getPatchResource(mContext, apkPath); int resId = patchResource.getResApkLayoutId("activity_main"); if (resId <= 0) setContentView(R.layout.activity_main); else setContentView(resId);
....而PatchResource类主要是对patch中的资源文件进行提取:具体实现:
/** * 获取apk里面的资源文件 * Created by Jarlene on 2015/11/23. */ public class PatchResource public static final String TAG = PatchResource.class.getSimpleName(); private Resources res;// 获取的资源apk里面的res private String apkPackageName;// 资源apk里面的包名 private PatchContext mPatchContext; public PatchResource(Context context, String apkPatch) mPatchContext = new PatchContext(context, apkPatch); res = mPatchContext.getResources(); apkPackageName = ApkUtils.getPackageInfo(context, apkPatch).packageName; public PatchResource(Resources res, String apkPackageName) this.res = res; this.apkPackageName = apkPackageName; /** * 获取layout文件中的id号 * * @param layoutName * layout名 */ public int getResApkLayoutId(String layoutName) Log.d(TAG, "getResApkLayoutId"); return res.getIdentifier(layoutName, "layout", apkPackageName); /** * 获取布局layout文件 * * @param context * 上下文 * @params layoutName * @return view */ public View getResApkLayoutView(Context context, String layoutName) Log.d(TAG,"getResApkLayoutView"); LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); return inflater.inflate(res.getLayout(getResApkLayoutId(layoutName)), null); /** * 获取控件view的id号 * * @param widgetName * 控件名 */ public int getResApkWidgetViewID(String widgetName) Log.d(TAG,"getResApkWidgetViewID"); return res.getIdentifier(widgetName, "id", apkPackageName); /** * 获取布局文件中的控件 * * @params layout,资源apk中的布局(view) * @params widgetName 控件名称 * @return widgetView */ public View getResApkWidgetView(View layout, String widgetName) Log.d(TAG,"getResApkWidgetView"); return layout.findViewById(getResApkWidgetViewID(widgetName)); /** * 获取drawable文件的id * * @param imgName * 图片名字 */ public int getDrawableId(String imgName) Log.d(TAG,"getDrawableId"); return res.getIdentifier(imgName, "drawable", apkPackageName); /** * 获取图片资源 * * @param imgName * @return drawable */ public Drawable getResApkDrawable(String imgName) Log.d(TAG,"getResApkDrawable"); return res.getDrawable(getDrawableId(imgName)); /** * 获取string文件中的id号 * * @param stringName * 字符串在String文件中的名字 */ public int getResApkStringId(String stringName) Log.d(TAG,"getResApkStringId"); return res.getIdentifier(stringName, "string", apkPackageName); /** * 获取String字符串 * * @param stringName * @return string */ public String getResApkString(String stringName) Log.d(TAG,"getResApkString"); return res.getString(getResApkStringId(stringName)); /** * 获取anim文件中的id号 * * @param animationName */ public int getResApkAnimId(String animationName) Log.d(TAG,"getResApkAnimId"); return res.getIdentifier(animationName, "anim", apkPackageName); /** * 获取anim文件 XmlPullParser * * @param animationName * @return XmlPullParser */ public XmlPullParser getResApkAnimXml(String animationName) Log.d(TAG,"getResApkAnimXml"); return res.getAnimation(getResApkAnimId(animationName)); /** * 获取动画anim * * @params animationName * @param context */ public Animation getResApkAnim(Context context, String animationName) Log.d(TAG,"getResApkAnim"); Animation animation = null; XmlPullParser parser = getResApkAnimXml(animationName); AttributeSet attrs = Xml.asAttributeSet(parser); try animation = createAnimationFromXml(context, parser, null, attrs); catch (XmlPullParserException e) e.printStackTrace(); catch (IOException e) e.printStackTrace(); return animation; /** * 获取anim动画 */ private Animation createAnimationFromXml(Context c, XmlPullParser parser, AnimationSet parent, AttributeSet attrs) throws XmlPullParserException, IOException Log.d(TAG,"createAnimationFromXml"); Animation anim = null; int type; int depth = parser.getDepth(); while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) if (type != XmlPullParser.START_TAG) continue; String name = parser.getName(); if (name.equals("set")) anim = new AnimationSet(c, attrs); createAnimationFromXml(c, parser, (AnimationSet) anim, attrs); else if (name.equals("alpha")) anim = new AlphaAnimation(c, attrs); else if (name.equals("scale")) anim = new ScaleAnimation(c, attrs); else if (name.equals("rotate")) anim = new RotateAnimation(c, attrs); else if (name.equals("translate")) anim = new TranslateAnimation(c, attrs); else throw new RuntimeException("Unknown animation name: "+ parser.getName()); if (parent != null) parent.addAnimation(anim); return anim; /** * 获取 color文件中的id号 * * @param colorName */ public int getResApkColorId(String colorName) Log.d(TAG,"getResApkColorId"); return res.getIdentifier(colorName, "color", apkPackageName); /** * 获取color 值 * * @param colorName * @return int */ public int getResApkColor(String colorName) Log.d(TAG,"getResApkColor"); return res.getColor(getResApkColorId(colorName)); /** * 获取 dimens文件中的id号 * * @param dimenName */ public int getResApkDimensId(String dimenName) Log.d(TAG,"getResApkDimensId"); return res.getIdentifier(dimenName, "dimen", apkPackageName); /** * 获取dimens文件中值 * * @param dimenName * @return float */ public float getResApkDimens(String dimenName) Log.d(TAG,"getResApkDimens"); return res.getDimension(getResApkDimensId(dimenName));里面的PatchContext主要是代理实现Context,具体如下:
/** * 主要为patch apk实现资源提取(伪Context) * Created by Jarlene on 2015/12/1. */ public class PatchContext extends ContextThemeWrapper private AssetManager mAssetManager; private Resources mResources; private Resources mProxyResource; private Context mContext; private String mPatchPath; public PatchContext(Context base, String apkPath) super(base, 0); this.mContext = base; this.mProxyResource = base.getResources(); this.mPatchPath = apkPath; @Override public Resources getResources() if (mResources == null) mResources = new Resources(getAssets(), mProxyResource.getDisplayMetrics(), mProxyResource.getConfiguration()); return mResources; @Override public AssetManager getAssets() if (mAssetManager == null) mAssetManager = (AssetManager) newInstanceObject(AssetManager.class); invokeMethod(mAssetManager, "addAssetPath", new Class[]String.class, new Object[]mPatchPath); return mAssetManager; private Object invokeMethod(Object obj, String methodName, Class[] valueType, Object[] values) try Class<?> clazz = obj.getClass(); Method method = clazz.getDeclaredMethod(methodName, valueType); method.setAccessible(true); return method.invoke(obj, values); catch (IllegalAccessException e) e.printStackTrace(); catch (InvocationTargetException e) e.printStackTrace(); catch (NoSuchMethodException e) e.printStackTrace(); return null; private Object newInstanceObject(Class<?> clazz) try return clazz.getConstructor().newInstance(); catch (Exception e) e.printStackTrace(); return null;到此为止就将patch中的资源提取出来了,同时伴随着Activity类一起加载。实现UI动态更新。 至于怎么生成APK,网上有很多教程,这里不再详细叙述。
以上是关于anroid动态更新UI界面的主要内容,如果未能解决你的问题,请参考以下文章