public class PowerInformationPreference extends Preference {
private TextView firstName;
public PowerInformationPreference(Context context, AttributeSet attr, int defStyle) {
super(context, attr, defStyle);
setLayoutResource(R.layout.layout_power_information);
}
@Override
protected void onBindView(View view) {(1)
// TODO Auto-generated method stub
super.onBindView(view);
firstName = (TextView)view.findViewById(R.id.firstname);(2)
}
}
上面是我们很常见到的自定义preference的基本形式,我们在onBindView()函数中操作自定义视图,比如上面的代码中我们需要动态的改变firstName这一TextView的text值。
如下,我们提供一个setText()接口,通过这个接口我们可以动态改变TextView的text值。
public void setText(String text){(3) firstName.setText(text);//NullPointerException(4) }
如果我们真的是这样写这个接口的话,会发现一定会出现NullPointerException,调试发现这里的firstName为空。而回到onBindView()中调试分析又会发现,firstName = (TextView)view.find...实际上是会运行的,也就是有这个firstName对象存在。那么唯一的解释就是setText()的调用在onBindView()运行之前,因此我们的疑惑来了,如何确保onBindView()在setText()调用之前运行呢?
事实上,到这里我们如果继续这样分析的话,就会钻入误区,跳出来,从逻辑层面上想想,我们会发现,(1),(2),(4)是对UI的操作,(3)是对数据的操作。这里,(1)(2)和(4)分开了,也就是说,(1)(2)运行的时候不能保证(4)运行;(4)运行的时候不能保证(1)(2)运行,这对UI操作是非常危险的事,很容易就产生了控制正异常。
不仅仅是这里,我们查看Adapter.java的源码,查看有getView(), onCreateView(), onBindView()等类的相关源码,都会发现,凡是涉及到UI操作的一定是放在一起的。
现给出上例的修正方案:
public class PowerInformationPreference extends Preference { private TextView firstName; private String text; public PowerInformationPreference(Context context, AttributeSet attr, int defStyle) { super(context, attr, defStyle); setLayoutResource(R.layout.layout_power_information); } @Override protected void onBindView(View view) {(1) // TODO Auto-generated method stub super.onBindView(view); firstName = (TextView)view.findViewById(R.id.firstname);(2) firstName.setText(text); }
public void setText(String text){(3) this.text = text;
notifyChanged();
}
}
这里还需要强调的一点是一定不要忘记notifyChanged()这个函数通知UI更新数据,否则我们会发现通过这个接口设置的数据并没有改变原来的值。这个函数如同View中的invalidate(),虽然数据更新了,但是显示在视图中的数字图像依然没变,因此需要通知UI更新显示。