您的位置:首页 > 移动开发 > Android开发

[Android]解决Fragment无法使用android:onClick属性

2015-07-09 16:22 525 查看

有过MFC或者WinForm或者WPF经验的程序员一定对于Button的Click事件绑定情有独钟,然而当转移到Android平台开发的时候会发现,之前的种种便利全都不在,只有看着闹心的setOnClickListener()。

实际上Android里面也有同样的功能,在布局文件中同样可以给Button绑定Click时间的响应函数。如:

<Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="OnClick"
        android:text="Button" />

然后在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) {

4000
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高手,所以如果有人能优化上面的代码,我会十分高兴。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: