[Android]解决Fragment无法使用android:onClick属性
16lz
2021-01-23
有过MFC或者WinForm或者WPF经验的程序员一定对于Button的Click事件绑定情有独钟,然而当转移到 Android平台开发的时候会发现,之前的种种便利全都不在,只有看着闹心的setOnClickListener()。 实际上Android里面也有同样的功能,在布局文件中同样可以给Button绑定Click时间的响应函数。如: 然后在Activity里面实现OnClick()函数: public void OnClick(View v) { System.out.println("Click"); } 真的很方便,不是吗?但是你千万不要高兴的太早,因为正有一个很大的坑在等着你!不信你瞧着。 Android中有很多很多的布局器,它们带来了极大的灵活性。与此同时,也出现了一种叫做Fragment 的东西——它不是布局器,却可以管理布局器。Fragment为开发灵活的界面组件库提供了可能,同样的 我们也想到在Fragment管理的布局文件中使用按钮的onClick属性。多么自然! 问题就出在Fragment身上,前面说了它不是布局器,所以它不具备渲染视图的能力,它管理的布局器 最终要加载到一个ViewGroup对象内,由ViewGroup对象来渲染。而视图树并不知道每一个子控件来源 于哪里,这就导致了一个结果:不管是在什么地方定义的onClick属性,都必须在包含该Button的 Activity中去寻找OnClick()方法。 可能有人说,API中对onClick早有说明:设置点击时从上下文中调用指定的方法,只是你不知道而已。 事实确实是这样,但是我是一个追根溯源的人,如果我没有遇到这么多的问题,我可能只知道应该怎 么做,却不知道为何应该这样做。 抛开onClick属性,我们仍然可以使用代码的方式实现对Click的事件响应,但是毕竟——不优雅——了。 曾经我想到了一种办法——使用约定:让使用该Fragment的Activity实现一个包含所有OnClick()函数的 接口。但是那样做仍然不够优雅! 对于Java程序员来说,反射有时候是一种水到渠成的方案。下面的ParseButtonIdClick()函数就是从 配置文件中读取指定id的Button的OnClick实现函数,然后使用setOnClickListener()将实现函数和 Button的Click事件进行绑定。 /** * 解析按钮的Click事件响应 */ protected void ParseButtonIdClick() { XmlResourceParser xmlParser = rootView.getResources().getXml( R.xml.button_id_click);
int event = XmlPullParser.START_DOCUMENT;
try { event = xmlParser.getEventType(); } catch (XmlPullParserException e) { e.printStackTrace(); }
String pkgName = (new Throwable()).getStackTrace()[0].getClassName(); System.out.println(pkgName);
do { switch (event) { case XmlPullParser.START_TAG: { if (xmlParser.getName().equals("Button")) { String id_str = xmlParser.getAttributeValue(0); String click = xmlParser.getAttributeValue(1);
try { Class<?> classRId = Class .forName("com.abc.keyboard.R$id"); Field filedId = classRId.getField(id_str); int id_int = filedId.getInt(null); Button idButton = (Button) rootView .findViewById(id_int);
final Method method = getClass().getDeclaredMethod( click, View.class); idButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { try { method.invoke(KeyboardFragmentBase.this, v); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } });
} catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } break; }
try { event = xmlParser.next(); } catch (XmlPullParserException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
} while (event != XmlPullParser.END_DOCUMENT);
xmlParser.close(); }
int event = XmlPullParser.START_DOCUMENT;
try { event = xmlParser.getEventType(); } catch (XmlPullParserException e) { e.printStackTrace(); }
String pkgName = (new Throwable()).getStackTrace()[0].getClassName(); System.out.println(pkgName);
do { switch (event) { case XmlPullParser.START_TAG: { if (xmlParser.getName().equals("Button")) { String id_str = xmlParser.getAttributeValue(0); String click = xmlParser.getAttributeValue(1);
try { Class<?> classRId = Class .forName("com.abc.keyboard.R$id"); Field filedId = classRId.getField(id_str); int id_int = filedId.getInt(null); Button idButton = (Button) rootView .findViewById(id_int);
final Method method = getClass().getDeclaredMethod( click, View.class); idButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { try { method.invoke(KeyboardFragmentBase.this, v); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } });
} catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } break; }
try { event = xmlParser.next(); } catch (XmlPullParserException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
} while (event != XmlPullParser.END_DOCUMENT);
xmlParser.close(); }
我不是一个Java高手,所以如果有人能优化上面的代码,我会十分高兴。
更多相关文章
- Android 中LayoutInflater(布局加载器)之实战篇
- 无废话Android之常见adb指令、电话拨号器、点击事件的4种写法、
- Android布局之相对布局——RelativeLayout
- 【Android 初学】3、控件布局初步
- Android ListView中点击单行实现RadioButton的单选功能,自定义Ite
- android五种布局-霓虹灯效果实现
- Android事件分发机制浅析
- Android布局管理器-详细解析布局实现
- android主流UI布局