anroid动态更新UI界面

来源:互联网 发布:php的memcache 编辑:程序博客网 时间:2024/06/15 12:11

背景

在android中,一成不变的UI布局可能会使用户厌烦(现在基本上都是ViewPager+ListView的方式),那么有没有什么方式实现动态更新UI布局提高用户的体验呢?答案是肯定的,本文就是介绍一种方式实现动态更新UI布局的方式。

技术途径

在动态实现类补丁这篇文章中,我实现了动态加载类,它可以实现dalvik动态更新类(art原生支持文章提到方式),结合这篇文章我们可以很清楚明白,在实现动态更新类的时候,同时替换布局xml文件也是可以得。这个时候我们需要将dex文件,layout等资源文件一起打包生成APK。具体实现是在Activity setContentView():
@Overrideprotected 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,网上有很多教程,这里不再详细叙述。



0 0
原创粉丝点击