ãã€ãŠãç§ã®åã«ãã¿ã¹ã¯ã¯Androidã®ã¢ããªã±ãŒã·ã§ã³ã®ããŒããäœæããããšã§ããã ç§ãæã«å
¥ãããã£ããã®ïŒ
- å€èŠ³ãåãæ¿ããæ©èœ-ããã€ãã®è²ãšã°ã©ãã£ãã¯ãå€æŽãã
- å€æŽã¯ãªã³ã¶ãã©ã€ã§è¡ãå¿
èŠããããŸãããŠãŒã¶ãŒã¯ãã¶ã€ã³ã®ã¿ãå€æŽããä»ã®ãã¹ãŠïŒå
¥åãã£ãŒã«ãã®å
容ããªã¹ãå
ã®èŠçŽ ã®äœçœ®ãªã©ïŒã¯å€æŽããªãã§ãã ããã
- å°æ¥çã«ã¯ãããšãã°æé垯ãªã©ããŠãŒã¶ãŒã®åå ãªãã«ãããã¯ãå€æŽã§ããããã«ããã
- æ¢åã®ã³ãŒããããŒã¯ã¢ããã倧å¹
ã«å€æŽããããããŸããã çæ³çã«ã¯ãäœããã®æ¹æ³ã§ããŒã¯ã¢ããã®èŠçŽ ãããŒã¯ãããã ãã§ã
- ã¢ããªã±ãŒã·ã§ã³ãæŽæ°ããã«æ°ãããããã¯ãã¢ããããŒãã§ãããšäŸ¿å©ã§ãã
äœãéæãããã©ã®ããã«å®è£
ããããã«ã€ããŠ-ã«ããã®äžã§ã

Stack OverflowãšAndroidã®ããã¥ã¡ã³ããæäŸããŠããæãæçœãªæ¹æ³ã¯ãContext.setThemeã§ãã ã¯ã³ãã£ãã-ãã¹ãŠã®ãã¥ãŒãäœæããåã«ããŒããã€ã³ã¹ããŒã«ããå¿
èŠããããŸãã ãããã¯ããã®å Žã§åãæ¿ããããšãã§ããªãããšã¯ããã«ããããŸãããŠãŒã¶ãŒã¯ã¢ã¯ãã£ããã£å
šäœã®å®å
šãªåäœæã«ééããªãæ°ä»ãã§ãããã ã¯ããåã¢ã¯ãã£ããã£ã®ã³ãŒããäœããã®æ¹æ³ã§ä¿®æ£ããå¿
èŠããããŸãã ã€ã³ã¿ãŒãããäžã§ä»ã®æšå¥šäºé
ã¯èŠã€ãããŸããã§ããïŒæ
å ±ãããã°ããªã³ã¯ã«æè¬ããŸãïŒã
ããŠãå®è£
ãæžããŸãããã ãã©ãã¯ãžã£ãã¯ãšWeakReferenceã䜿çšã
ãã€ã³ã4ããé²ããŸããç§ã¯èªåãã³ãŒããæžããããªãéçºè
ã§ãããšèããŠããŸãã ç§ã¯ã³ãŒããæžãã®ã奜ãã§ã¯ãªãã®ã§ãå°æ¥ãããæžããªãããã«ãããããã®ã³ãŒããæžãæºåãã§ããŠããŸãã ç§ã¯èªåèªèº«ãå©ããããšã¯ã§ããŸããã次ã®ãŠã£ã³ããŠãåçãªãã¶ã€ã³å€æŽã®åœ±é¿ãåããããããã«è¡šç€ºããããšããçžäºäœçšããžãã¯ãèããããããŸããã èŠçŽ ã®æšªã®ããŒã¯ã¢ããã§ãããšãã°ããã®è²ãèæ¯è²ãšçãããªãããšã瀺ãããã ãã§ãã
tagããããã£ã¯ããã«åœ¹ç«ã¡ãŸãã ããšãã°ãListViewã§ã¢ããã¿ãŒãæé©åããããã®Holder'ovãšããŠãã¢ããªã±ãŒã·ã§ã³ã¿ã°ã®ã©ããã§äœ¿çšãããŠããå Žåã¯åé¡ãããŸããã ã³ãŒãã§ã¯ãsetTag / getTagãidãã©ã¡ãŒã¿ãŒãšå
±ã«äœ¿çšã§ããŸãã ãã®ãããªå Žæãããããããå Žåã¯ãæ€çŽ¢ãšçœ®æã圹ç«ã¡ãŸãã
次ã«ãç°¡åãªã¿ã°ã®åœ¢åŒãèããŸãã ãŸããç©ç©ããã¿æ®»ããåé¢ãããã®ã¿ã°ãå®éã«ããŒãã®äœ¿çšã瀺ããŠããããšãç°¡åã«ç¢ºèªããŸããã¿ã°ã¯åžžã«ãïŒãèšå·ã§å§ãŸããŸãã ããã«ã¯ããªãœãŒã¹ã®ååãç¶ããŸãïŒãbackgroundããªã©ïŒã 次ã«ãã»ãã¬ãŒã¿ãã|ããªã©ã®ãªãœãŒã¹ãããã³ããã¹ãã®è²ãèæ¯ç»åãèæ¯è²ãªã©ã®ãªãœãŒã¹ã®ã¿ã€ãã ããšãã°ããïŒChat | tiled_bgããšããã¿ã€ãªã³ã°ã䜿çšãããã£ãããŠã£ã³ããŠã®èæ¯ã ãã¶ããèŠãç®ã®çŸããã§ã¯ãªããããããŸããããããã«è§£æããŸãã ãã®ãããªã¿ã°ãèšè¿°ãããšãã«ãã¹ãæå°éã«æããã«ã¯ãããããæååãªãœãŒã¹ã«å
¥ããŠåå©çšããããšããå§ãããŸã-ã¢ããªã±ãŒã·ã§ã³ã§ã¯ãªãœãŒã¹ã䜿çšããŸãïŒPrimary | text_fgã¯77å䜿çšãããŸãã
æãé£ããéšåã¯èåŸã«ãããäœããã®æ¹æ³ã§ãããã®ã¿ã°ãåŠçããã ãã§ãããã®ãããªã¿ã°ãæã€èŠçŽ ã¯ããèšåŒµããã¥ãŒäžã«ããã«åŠçãããã®åŸããŒããå€æŽãããã³ã«åŠçããå¿
èŠããããŸãã ãèšåŒµãã¯ãã¢ã¯ãã£ããã£ã®setContentViewãšLayoutInflaterã®äœ¿çšã®2ã€ã®æ¹æ³ã§çºçããŸãã setContentViewããå§ããŸãããã
ãã®ã¢ããªã±ãŒã·ã§ã³ã§ã¯ããã¹ãŠã®ã¢ã¯ãã£ããã£ã¯å°æ°ã®åºæ¬ã¢ã¯ãã£ããã£ããç¶æ¿ãããŸãã setContentViewã¡ãœããããªãŒããŒã©ã€ãããã ãã§ãïŒ
public void setContentView(int id) { super.setContentView(id); HotTheme.manage(mActivity.getWindow().getDecorView()); }
getDecorViewã¡ãœããã¯ãViewéå±€ã®ãã«ãŒãããè¿ããŸãã
LayoutInflaterã䜿çšããŠãã¥ãŒãäœæãããšãã«åãããšãè¡ãã«ã¯ãã©ããããŸãã
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)); } }
ä»-ãã¥ãŒéå±€ã®è§£æïŒ
HotTheme.javaïŒ
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); } } } }
ã芧ã®ãšãããã¿ã°ãå«ããã¥ãŒã¯éå±€ããåŒãåºãããŠããŸãã 念ã®ãããè€æ°ã®ã»ãã¬ãŒã¿ã|ããååšããå¯èœæ§ããããšèããŠããŸãããã®å ŽåããªãœãŒã¹ã¯åã¿ã€ãã«é©çšãããŸãïŒããã¯æçšã§ããããšãå€æããå¯èœæ§ããããŸãïŒã
ããã«ããããã®èŠçŽ ã¯ç¹å®ã®ThemedViewã«å€ããããã¹ãŠã®éæ³ãæ
åœããŸãã notifyChangeã¡ãœããã¯ãçŸåšã®ããŒãããã®ãã¥ãŒã«é©çšããŸãã ãããã¯ã®å€æŽãéç¥ããããã«ãå°æ¥ã®ããã«ThemedViewãä¿åããŠãã ãããè€éãªããšã¯äœããããŸããã
ThemedViewã¯ã©ã¹èªäœã¯ãViewã®åçŽãªã©ãããŒã§ãããã³ã³ããã¹ããªãŒã¯ãé²ããŸãã
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(); } }
ããããªäœè«Javaã倧奜ãã§ãã ã¯ããJavaã¯Cããå°ãé
ãã§ããã¯ããPythonãããæè»æ§ãäœãã§ãã ãããã圌女ïŒã¯ããç§ã«ãšã£ãŠJavaã¯ã圌女ãïŒã¯é©ãã¹ãããšãããããšãã§ããŸãã
- ç§ã®ããã«ã çå£ã«ãç§ã¯ã¡ããã©ç§ã欲ãããã®ã圌女ã«äŒããŠããã ããŒã«ã欲ãããªãã圌女ã¯ããã«ãåãã«è¡ãããããéããŠãç§ãçŽ æŽããã飲ã¿ç©ã楜ãããŸã§èŠªåã«åŸ
ã¡ããã®åŸåœŒå¥³ã¯ããã«ãæšãŠãŸãã ããããšãgc
- ç§ã®ããã«èããŸãã åã®åŒ±ãèšèªã®ããã«ãããŒã¿åãèŠããŠããå¿
èŠã¯ãããŸããã ã¡ã¢ãªãã¹ã¿ãã¯ã«å²ãåœãŠãããã®ããããŒãã«å²ãåœãŠãããã®ããèããå¿
èŠã¯ãããŸããã Javaã䜿çšããŠããå Žåãã»ãšãã©èããå¿
èŠã¯ã»ãšãã©ãããŸãããå€ãã®å Žåãããªããæãããšã圌女ã«èª¬æããã ãã§ååã§ãã
- ã³ãŒããæžããŠãã ããã JavaassistãCGLibãjava.lang.reflect.ProxyãJSR-269ã泚éããªãã¬ã¯ã·ã§ã³... Javaã«ããã¡ã¿ããã°ã©ãã³ã°ã¯çŽ æŽãããã§ãïŒ
- ãã£ã¹ãããŠãã ããã ãããŠããã¯å®å
šã§ãïŒ ãŸããã»ãšãã©ã å°ãªããšã@SuppressWarningïŒãæªãã§ãã¯ãrawtypesãïŒã§åœŒå¥³ã«æ鳎ããŸã§ã¯ã ãžã§ããªãã¯ã«æè¬
- Javaã¯èªãã«æã£ãŠããŸããã 圌女ã¯ãããã圌女ã®æ§è³ªã«åããŠãããšããäºå®ã«ãããããããå®å
šã§ãªãæ¹æ³ãç¥ã£ãŠããŸãã
ã¯ãã圌女ã«ã¯æ¬ é¥ããããŸãã 圌女ã¯ãã£ããã倧奜ãã§ã-Javaãããåé·ãªèšèªãèŠãããšã¯ãããŸããïŒãã¡ãããèšèªã®ãã¹ã«ã«ã¯èæ
®ããŠããŸããïŒã ããããéåžžIDEã§ã¯ãããŸããŸãªèªå眮æãšãã³ãã¬ãŒãã䜿çšããŠãããå
æã§ããŸãã
Androidã¯Javaã䜿çšããŸãã ã¯ããããã¯åœŒå¥³ã®çŸåŸ³ã®1ã€ã圌ã«æ®ã£ãŠããªãã£ãã ãã§ãã 圌ã¯ãçŸããåŸé ãªå¥³æ§ãšããããããé
ã£ãŠããªãã²ããåã£ãŠããªãç·æ§ã®ããã«èŠããŸãã ç§ã¯åœŒã«èšããŸã-ç§ã¯ããŒã«ã欲ããããããŠåœŒã¯ç§ã«èšã£ã-å®æ°ãååŸããã€ã³ãã³ããäœæããããŒã¿ãã·ãªã¢ã«åããã¢ã¯ãã£ããã£ãéããçµæãååŸããŸã...ãã¹ãŠãããŸãããã°ããããéã·ãªã¢ã«åãããããŒã«ãã¿ã€ãã«å€æããŸãã ãããŠãã¯ããããŒã«ãåŸãããã®ããªãã®æäœããã€ã§ãäžæããããããããªãããšãå¿ã«çããŠãããŠãã ããã ãã§ã«æ¯æã£ããšããŠãã 1ã€ã®ç©çããã»ã¹ã®ã³ã³ããã¹ãã«ããå Žåã¯ç¹ã«åã°ããŸãã

ã©ã®ã¿ã€ãã®Message.objã«å°ããã¯ãMessage.whatã«äŸåããããšãåžžã«å¿µé ã«çœ®ããŠããå¿
èŠããããŸãã ãããŠã巚倧ãªåãæ¿ããè¡ããŸãã ãšãŠãå¿«é©ã§ãã
Androidã®ã³ãŒãçæã¯å¥ã®ããšã§ãã Javaassit / CGLibã«ã€ããŠã¯ã»ãšãã©å¿ããããšãã§ããŸãïŒäŒŒããããªå®è£
ãããã€ããããŸããããã®é床ã«ã¯å€ãã®èŠæããããŸãïŒã æ®ãïŒãããã·ãJSR-269ãã¢ãããŒã·ã§ã³ããªãã¬ã¯ã·ã§ã³ïŒã«ã€ããŠã¯å®æçã«çœªãç¯ããŸãããå€ããå°ãªãã蚱容ã§ããé床ã§åäœãããããã«å€ãã®ãžã§ã¹ãã£ãŒãããªããã°ãªããŸããã
Androidã¯èªãã«æã£ãŠããŸãã 圌ã¯å®å
šã§ãªãããšãç¥ã£ãŠããŸãã ãããŠãããã¯ãã®æ§è³ªã«åãããã®ã§ã¯ãããŸããïŒNDKãRenderScriptãªã©ãèæ
®ã«å
¥ããïŒã ã¯ãããªãã¬ã¯ã·ã§ã³ãä»ããŠã®ã¿äœ¿çšã§ããŸããããã«ãããUnsafeã®å©ç¹ã®ã»ãšãã©ã倱ãããŸãã
ã ãããç§ã¯äœãããŠããŸãã Javaã®æåŸã®ãããã§ãWeakReferenceãªã©ã®ããŒã«ã¯éåžžã«ãŸãã«ãã䜿çšããããæã倧èãªãšããã£ãã¯ãªç©ºæ³ã§ã®ã¿äœ¿çšãããŸãïŒããšãã°ãããŸããŸãªORMã§ã®ããŒã¿æŽåæ§ã®ãµããŒãïŒã Androidã§ã¯ãããã³ã¹ã®ä»£ããã«ãWeakReferenceã䜿çšããŠBDSMã¹ã¿ã€ã«ãæ¯é
ããå¿
èŠããããŸãã ãªããžã§ã¯ãã¯ãæªç¥ã®ã©ã€ããµã€ã¯ã«ã®åœ±é¿ãåããŠããªããžã§ã¯ãèªèº«ã®ç掻ãéããšããäºå®ã«ææ
¢ããå¿
èŠããããŸãã ã³ã³ããã¹ããªãŒã¯ïŒã³ã³ããã¹ãïŒãçºçããªãããã«ãWeakReferenceã䜿çšããŠãããã«ãåºå·ãããå¿
èŠããããŸãã ãããããAndroidã®äžã§ããã³ãããã䟡å€ããããåã¢ã¯ãã£ããã£ã§ãçµäºããããšããViewéå±€ã®ç»é²ã解é€ããŸãããåé¡ã¯ãããå€æŽããããã¥ãŒããªããªãããšã§ãïŒç¹ã«ListViewã®å
žåçãªèŠçŽ ã§ããããã®èŠçŽ ã¯åžžã«è¡šç€º/é衚瀺ã«ãªããŸãïŒç»é¢ããïŒã ããã¯äž»ã«ãã¢ããªã±ãŒã·ã§ã³ã¢ãžã¥ãŒã«ã®äžéšãèŠèŠéšåã«åœ±é¿ãäžããå Žåã«ã»ãšãã©åžžã«WeakReferenceã䜿çšããçç±ã§ãããã¹ãŠã®ãã¥ãŒã¯WeakReferenceã®ã¿ã«ä¿åããããããäœæ¥ã®ããžãã¯ãéåžžã«è€éã«ãªããŸãã
ThemedViewã«æ»ããŸãããããã®åå«ã§ãonChangeã¡ãœããã§ãViewã§äœãèµ·ããããæ£ç¢ºã«æ±ºå®ããŸãã
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) {
types.getïŒèŠçŽ ïŒã³ãŒãã¯ãåã«å°æåã®æååã§enumãè¿ããŸãã
ãããããã¡ãœããdecodeBitmapãdecodeDrawableãããã³getColorã¯æ®ããŸããã
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()); }
ThemeProviderã¯ã©ã¹ã®ãªããžã§ã¯ãã¯sCurrentProviderãšããŠæ©èœããŸããsCurrentProviderã®å¯äžã®ã¿ã¹ã¯ã¯ãååãšã¿ã€ãã«ãã£ãŠãªãœãŒã¹æ
å ±ãååŸããããšã§ãã
æãåçŽãªå®è£
ã§ã¯ãããã€ãã®ãããã¯IDããªãœãŒã¹åã®ãã¬ãã£ãã¯ã¹ãšããŠè¿œå ããŸãã
@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); }
getResIdã¡ãœããã¯ãResources.getIdentifierã¡ãœããã®å°ããªã©ãããŒã§ãã
mNextãã£ãŒã«ããThemeProviderãªããžã§ã¯ãã§ãã ãªãœãŒã¹ãèŠã€ãããªãã£ãå Žåã«ãã§ãŒã³ãæ€çŽ¢ããããã«å¿
èŠã§ãïŒæçµçã«ã¯ããã©ã«ãã䜿çšãããŸãïŒã
ãã®çµæã次ã®ãããã¯ãäœæããã«ã¯ãå¿
èŠãªãªãœãŒã¹ã®ã»ãããè¿œå ããäœããã®çš®é¡ã®ãã¬ãã£ãã¯ã¹ãè¿œå ããã ãã§ãã ããšãã°ããã£ãããŠã£ã³ããŠã®èæ¯ã®ãªãœãŒã¹åïŒ
def_chat_bg
night_chat_bg
pink_chat_bg
wood_chat_bg
åèšåé ã§è¿°ã¹ãããã«ãã¢ããªã±ãŒã·ã§ã³èªäœããã ãã§ãªããå€éšããããªãœãŒã¹ãããŒãã§ããããšã¯çŽ æŽãããããšã§ãã ãœãŒã¹ã¯ãããšãã°å¥ã®ã¢ããªã±ãŒã·ã§ã³ã§ãã ãã®å Žåãããã±ãŒãžåãšResourcesãªããžã§ã¯ããé€ãããã¹ãŠãåãããã«æ©èœããŸãã 次ã«ãPackageManager.getResourcesForApplicationããååŸã§ããŸãã
éæãããããšãããäžåºŠæãåºããŠãã ããã
- ãã¶ã€ã³ãåãæ¿ããæ©èœ-å®äº
- å€æŽã¯ããªã³ã¶ãã©ã€ãã§å®è¡ããå¿
èŠããããŸã-ããã§å®äºã§ã
- å°æ¥çã«ã¯ããŠãŒã¶ãŒã®åå ãªãã«ãããã¯ãå€æŽããããšèããŠããŸã-èŠç¹ãèŠããé害ã¯ãããŸãã
- æ¢åã®ã³ãŒããããŒã¯ã¢ããã倧å¹
ã«å€æŽããããããŸãããå®éãã³ãŒãã¯å€§å¹
ã«å€æŽãããŠããŸãããäž»ã«æ€çŽ¢ãšçœ®æãè¡ããããããã«ãã©ã¹èšå·ãè¿œå ã§ããŸãã
- ã¢ããªã±ãŒã·ã§ã³ãæŽæ°ããã«æ°ãããããã¯ãã¢ããããŒã-æºåå®äº
ãã¹ãŠãããŸããã£ãããã§ãã æåŸãŸã§èšäºããã¹ã¿ãŒããçããã«æè¬ããŸãã 誰ãã説æããããã¯ããã¯ã䜿çšããŠãã¢ããªã±ãŒã·ã§ã³ããŠãŒã¶ãŒã®æ°åã«é©å¿ããèœåãäžããããšãé¡ã£ãŠããŸã-ä¿¡ããŠãã ããã圌ãã¯ããªãã«æè¬ããã§ãããïŒ
PSïŒããŠããããŠãäºæ³éãã1åéã®åºå-
ããã«ããŒããæã€ãšãŒãžã§ã³ãã®ã¢ããªã±ãŒã·ã§ã³ã