public void setContentView(int id) { super.setContentView(id); HotTheme.manage(mActivity.getWindow().getDecorView()); }
public class HotLayoutInflater { private LayoutInflater inflater; private HotLayoutInflater(LayoutInflater inflater) { this.inflater = inflater; } public View inflate(int resource, ViewGroup root, boolean attachToRoot) { View v = inflater.inflate(resource, root, attachToRoot); HotTheme.manage(v); return v; } public View inflate(int resource, ViewGroup root) { View v = inflater.inflate(resource, root); HotTheme.manage(v); return v; } public static HotLayoutInflater wrap(LayoutInflater layoutInflater) { return new HotLayoutInflater(layoutInflater); } public static HotLayoutInflater from(Context context) { return new HotLayoutInflater(LayoutInflater.from(context)); } }
public static void manage(View... views) { for (View v : views) { simpleManage(v); if (v instanceof ViewGroup) { ViewGroup vg = (ViewGroup) v; for (int i = 0; i < vg.getChildCount(); i++) { manage(vg.getChildAt(i)); } } } } public static void simpleManage(View view) { Object t = view.getTag(); if (t instanceof String) { String tag = (String) t; if (tag.startsWith("!")) { tag = tag.substring(1); String[] elements = tag.split("\\|"); String base = elements[0]; for (int i = elements.length - 1; i >= 1; i--) { ThemedView tv = createThemedView(view, base, elements[i]); tv.notifyChange(); HotTheme.sViews.add(tv); } } } }
private static abstract class ThemedView { private WeakReference<View> view; ThemedView(View v) { view = new WeakReference<View>(v); } boolean notifyChange() { View v = view.get(); if (v == null) { return false; } onChange(v); return true; } abstract void onChange(View v); @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ThemedView view1 = (ThemedView) o; View v1 = view.get(); View v2 = view1.view.get(); return (v1 != null ? v1.equals(v2) : v2 == null); } @Override public int hashCode() { if (view == null) { return 0; } View v = view.get(); return v != null ? v.hashCode() : 0; } }
for (Iterator<ThemedView> it = views.iterator(); it.hasNext(); ) { if (!it.next().notifyChange()) { it.remove(); } }
I love java. Yes, Java is a bit slower C. Yes, it is less flexible than Python. But she (yes, for me Java - “she”) can do amazing things:
- Do for me. Seriously, I'm just telling her what I want. If I want a beer, she goes for a bottle, opens it and kindly waits while I enjoy a wonderful drink, after which she throws the bottle away. Thank you, GC!
- Think for me. I do not need to keep in mind the data types, as in languages ​​with weak typing. I don't need to think about whether memory is allocated on the stack or on the heap. When you have Java, you rarely have to think at all - it is often enough to explain to her what you want.
- Write the code for me. Javaassist, CGLib, java.lang.reflect.Proxy, JSR-269, annotations, reflections ... Metaprogramming with Java is great!
- Cast for me. And safe! Almost. At a minimum, until you yell at it with @SuppressWarning (“unchecked, rawtypes”). Thanks, Generics!
- Java is not proud. She can do Unsafe, despite the fact that it is contrary to her nature.
Yes, she has flaws. She likes to chat - I haven’t met a much more verbose Java (Pascal doesn’t count for the language, of course). But usually IDE allows you to overcome this with the help of various auto-permutations and templates.
Android uses Java. Yes, that's just none of its merits in it left. He looks more like a drunken unshaven peasant than a beautiful and submissive woman. I tell him - I want a beer, and he told me - get a constant, create an Intent, serialize the data, open the Activity, get the result ... If everything is fine, deserialize it and bring it to the type of "Beer". And yes, bear in mind that at any moment your operation to get a beer can be interrupted. Even if you already paid for it. Especially happy when you are in the context of a single physical process.
I constantly have to keep in mind what type of Message.obj will be based on Message.what. And make a huge switch. Very comfortably.
Code generation on Android is something else. You can almost forget about Javaassit / CGLib (there are some realizations of something similar, but the speed of their work leaves much to be desired). With the rest (Proxy, JSR-269, annotations and reflections) I periodically sin, but I have to make a lot of gestures in order to make it work at a more or less acceptable speed.
Android is proud. He can Unsafe. And this is not contrary to its nature (including NDK, RenderScript, etc.). Yes, only it is available exclusively through reflections, which destroys most of the advantages of Unsafe.
So, what is it for me? Due to the obedience of Java, such a tool as WeakReference is used quite rarely, only in the wildest erotic fantasies (for example, data consistency support in various ORMs). With Android instead of romance, WeakReference has to be used for domination in the BDSM style. We have to put up with the fact that objects live their lives, obeying the unknown life-cycle. You have to “cling” to them with the help of WeakReference, so as not to cause context leaks (Context). Perhaps it would be worthwhile to “bend” under Android, and in each activation, upon exit, “unregister” the View hierarchy, but the trouble is, it can change, and some of the View will not be there (especially typical for ListView, whose elements can constantly appear / disappear) from the screen). That's why I use the WeakReference almost always when some of the application modules affect the visual part - all the View are stored only on the WeakReference, which, of course, greatly complicates the logic of work.
private static ThemedView createThemedView(View v, final String base, String element) { ThemeType type = types.get(element); switch (type) { case TILED_BG: return new ThemedView(v) { @Override public void onChange(View v) { Bitmap bmp = decodeBitmap(base + "_bg"); BitmapDrawable bd = new BitmapDrawable(app().getResources(), bmp); bd.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); v.setBackgroundDrawable(bd); } }; case VIEW_COLOR_BG: return new ThemedView(v) { @Override public void onChange(View v) { int color = getColor(base + "_bg"); v.setBackgroundColor(color); if (v instanceof ListView) { // There is an android bug in setCacheColorHint // That caused the IndexOutOfBoundsException // look here: // http://code.google.com/p/android/issues/detail?id=12840 // // Moreover, that bug doesn't allow us to setDrawableCacheColor // for recycled views. That's why we need to perform cleaning up // via reflections // // Fixed in android 4.1.1_r1 try { ((ListView) v).setCacheColorHint(color); } catch (IndexOutOfBoundsException ex) { try { Field mRecycler = AbsListView.class.getDeclaredField("mRecycler"); mRecycler.setAccessible(true); Object recycler = mRecycler.get(v); Method m = recycler.getClass().getDeclaredMethod("clear"); m.setAccessible(true); m.invoke(recycler); } catch (Throwable t) { // No need to report this } } } } }; case VIEW_IMAGE_BG: return new ThemedView(v) { @Override public void onChange(View v) { v.setBackgroundDrawable(decodeDrawable(base + "_bg")); } }; case IMAGE_FG: return new ThemedView(v) { @Override public void onChange(View v) { ((ImageView) v).setImageDrawable(decodeDrawable(base + "_bg")); } }; case TEXT_COLOR: return new ThemedView(v) { @Override public void onChange(View v) { final int color = getColor(base + "_fg"); if (v instanceof TextView) { ((TextView) v).setTextColor(color); } } }; case TEXT_HINT: return new ThemedView(v) { @Override public void onChange(View v) { final int color = getColor(base + "_hint_fg"); if (v instanceof TextView) { ((TextView) v).setHintTextColor(color); } } }; case PAGER: return new ThemedView(v) { @Override public void onChange(View v) { int active = getColor(base + "_active_fg"); int inactive = getColor(base + "_inactive_fg"); int footer = getColor(base + "_footer_bg"); TitlePageIndicator pager = (TitlePageIndicator) v; pager.setSelectedColor(active); pager.setTextColor(inactive); pager.setFooterColor(footer); } }; case DIVIDER: return new ThemedView(v) { @Override public void onChange(View v) { int color = getColor(base + "_divider"); ListView lv = (ListView) v; int h = lv.getDividerHeight(); lv.setDivider(new ColorDrawable(color)); lv.setDividerHeight(h); } }; case TABBUTTON_BG: return new ThemedView(v) { @Override void onChange(View v) { StateListDrawable stateDrawable = new StateListDrawable(); Drawable selectedBd = decodeDrawable(base + "_selected"); stateDrawable.addState(new int[]{android.R.attr.state_selected}, selectedBd); stateDrawable.addState(new int[]{android.R.attr.state_pressed}, selectedBd); stateDrawable.addState(new int[]{}, decodeDrawable(base + "_unselected")); v.setBackgroundDrawable(stateDrawable); } }; case EDITTEXT_COLOR: return new ThemedView(v) { @Override void onChange(View v) { int color = getColor(base + "_fg"); EditText edit = (EditText) v; edit.setTextColor(color); int hintColor = getColor(base + "_disabled_fg"); edit.setHintTextColor(hintColor); } }; case GROUP_TINT: return new ThemedView(v) { @Override void onChange(View v) { int tintColor = getColor(base + "_fg"); ImageView imageView = (ImageView) v; imageView.setColorFilter(tintColor, PorterDuff.Mode.SRC_ATOP); } }; default: throw new IllegalArgumentException("Error in layout: no such type \"" + element + "\" (" + base + ")"); } }
private static ResourceInfo findResource(String base, ResourceType type) { return sCurrentProvider.findResource(base, type); } public static Drawable decodeDrawable(String base) { ResourceInfo info = findResource(base, ResourceType.Drawable); return info.getResources().getDrawable(info.getResId()); } public static Bitmap decodeBitmap(String base) { ResourceInfo info = findResource(base, ResourceType.Drawable); return BitmapFactory.decodeResource(info.getResources(), info.getResId(), Util.newPurgeableBitmapOptions()); } public static int getColor(String base) { ResourceInfo info = findResource(base, ResourceType.Color); return info.getResources().getColor(info.getResId()); }
@Override public ResourceInfo findResource(String name, ResourceType type) { int id = IdUtils.getResId(app().getResources(), mPrefix + "_" + name, type.getType(), PACKAGE_NAME); if (id == 0 && mNext != null) { return mNext.findResource(name, type); } return new ResourceInfo(app().getResources(), id); }
Source: https://habr.com/ru/post/178061/
All Articles