您的位置:首页 > 产品设计 > UI/UE

anroid动态更新UI界面

2015-12-01 20:53 423 查看

背景

在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,网上有很多教程,这里不再详细叙述。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: