我的 CustomTextView 出了问题。我正在尝试从layout-xml
文件中获取自定义值,并在我的setText()
方法中使用此值。不幸的是 setText()方法在构造函数之前调用,因此我不能在此方法中使用自定义值。
这是我的代码(细分到相关部分):
public class CustomTextView extends TextView {
private float mHeight;
private final String TAG = "CustomTextView";
private static final Spannable.Factory spannableFactory = Spannable.Factory.getInstance();
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
Log.d(TAG, "in CustomTextView constructor");
TypedArray values = context.obtainStyledAttributes(attrs, R.styleable.CustomTextView);
this.mHeight = values.getDimension(R.styleable.CustomTextView_cHeight, 20);
}
@Override
public void setText(CharSequence text, BufferType type) {
Log.d(TAG, "in setText function");
Spannable s = getCustomSpannableString(getContext(), text);
super.setText(s, BufferType.SPANNABLE);
}
private static Spannable getCustomSpannableString(Context context, CharSequence text) {
Spannable spannable = spannableFactory.newSpannable(text);
doSomeFancyStuff(context, spannable);
return spannable;
}
private static void doSomeFancyStuff(Context context, Spannable spannable) {
/*Here I'm trying to access the mHeight attribute.
Unfortunately it's 0 though I set it to 24 in my layout
and it's correctly set in the constructor*/
}
}
<declare-styleable name="CustomTextView">
<attr name="cHeight" format="dimension"/>
</declare-styleable>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ctvi="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.mypackage.views.CustomTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/my_fancy_string"
android:textSize="16sp"
ctvi:cHeight="24dp" />
</LinearLayout>
就像一个证明 - 这是LogCat输出:
30912-30912/com.mypackage.views D/CustomTextView﹕ in setText function
30912-30912/com.mypackage.views D/CustomTextView﹕ in CustomTextView constructor
因此,您可以看到在构造函数之前调用setText()
方法。这有点奇怪,我不知道我需要改变什么才能在setText方法中使用我的自定义属性(cHeight
)。
提前感谢您的帮助!
答案 0 :(得分:6)
根据属性值调用TextView
的{{1}} super()
构造函数。
如果您在设置文本值时确实需要访问自定义属性,请同时使用文本的自定义属性。
答案 1 :(得分:2)
我不认为这些解决方案有任何好处,恕我直言。如果您只使用setCustomText()
等自定义方法而不是覆盖自定义TextView.setText()
,该怎么办?我认为在可扩展性方面可能会好得多,而且TextView
的黑客/覆盖实施可能会引导您解决未来的问题。
干杯!
答案 2 :(得分:1)
首先,请记住在使用后始终回收TypedArray
。
TextView
在构建过程中调用#setText(CharSequence text, BufferType type)
因此定义了对setText
的延迟调用,如下所示:
private Runnable mDelayedSetter;
private boolean mConstructorCallDone;
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
Log.d(TAG, "in CustomTextView constructor");
TypedArray values = context.obtainStyledAttributes(attrs, R.styleable.CustomTextView);
this.mHeight = values.getDimension(R.styleable.CustomTextView_cHeight, 20);
mConstructorCallDone = true;
}
然后在setText
内 - 覆盖:
public void setText(final CharSequence text, final TextView.BufferType type) {
if (!mConstructorCallDone) {
// The original call needs to be made at this point otherwise an exception will be thrown in BoringLayout if text contains \n or some other characters.
super.setText(text, type);
// Postponing setting text via XML until the constructor has finished calling
mDelayedSetter = new Runnable() {
@Override
public void run() {
CustomTextView.this.setText(text, type);
}
};
post(mDelayedSetter);
} else {
removeCallbacks(mDelayedSetter);
Spannable s = getCustomSpannableString(getContext(), text);
super.setText(s, BufferType.SPANNABLE);
}
}
答案 3 :(得分:0)
很遗憾,这是对Java的限制,它要求先在构造函数中调用super(..)
。因此,唯一的解决方法是在初始化自定义属性后再次调用setText(..)
。
请记住,由于setText
在初始化自定义属性之前也被调用,它们可能具有空值,您可以获得NullPointerException
检查我的customTextView
示例,该示例将首字母大写并在和处添加双点(我在所有活动中都使用了它)
package com.example.myapp_android_box_detector;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.v7.widget.AppCompatTextView;
import android.util.AttributeSet;
public class CapsTextView extends AppCompatTextView {
public Boolean doubleDot;
private Boolean inCustomText = false;
public CapsTextView(Context context){
super(context);
doubleDot = false;
setText(getText());
}
public CapsTextView(Context context, AttributeSet attrs){
super(context, attrs);
initAttrs(context, attrs);
setText(getText());
}
public CapsTextView(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
initAttrs(context, attrs);
setText(getText());
}
public void initAttrs(Context context, AttributeSet attrs){
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CapsTextView, 0, 0);
doubleDot = a.getBoolean(R.styleable.CapsTextView_doubleDot, false);
a.recycle();
}
@Override
public void setText(CharSequence text, BufferType type) {
if (text.length() > 0){
text = String.valueOf(text.charAt(0)).toUpperCase() + text.subSequence(1, text.length());
// Adds double dot (:) to the end of the string
if (doubleDot != null && doubleDot){
text = text + ":";
}
}
super.setText(text, type);
}
}