Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom TextView - setText() called before constructor

I've got a problem with my CustomTextView. I'm trying to get a custom value from my layout-xml file and use this in my setText() method. Unfortunately the setText() method gets called before the constructor and because of this I can't use the custom value in this method.

Here's my code (broken down to the relevant parts):

CustomTextView.class

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*/
    }
}

styles.xml

<declare-styleable name="CustomTextView">
    <attr name="cHeight" format="dimension"/>
</declare-styleable>

layout.xml

<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>

And just as a proof - here's the LogCat output:

30912-30912/com.mypackage.views D/CustomTextView﹕ in setText function
30912-30912/com.mypackage.views D/CustomTextView﹕ in CustomTextView constructor

So as you can see the setText() method is called before the constructor. That's kinda weird and I don't know what I need to change in order to use my custom attribute (cHeight) in the setText-method.

Thanks in advance for any help!

like image 777
user3989678 Avatar asked Aug 29 '14 09:08

user3989678


People also ask

What is the difference between setText () and append () methods of textarea?

The basic difference is that setText() replaces all the text from the existing one and append() adds your new value to existing one.

How do I assign text to TextView?

Set The Text of The TextView You can set the text to be displayed in the TextView either when declaring it in your layout file, or by using its setText() method. The text is set via the android:text attribute. You can either set the text as attribute value directly, or reference a text defined in the strings.

Can we change the text in TextView?

TextView tv1 = (TextView)findViewById(R. id. textView1); tv1. setText("Hello"); setContentView(tv1);


2 Answers

It's the TextView super() constructor that calls your setText() based on the attribute values.

If you really need to access your custom attribute when setting a text value, use a custom attribute for the text as well.

like image 169
laalto Avatar answered Oct 01 '22 03:10

laalto


Unfortunately it is a limitation on Java, that requires to call super(..) in constructor before anything else. So, your only workaround is to call setText(..) again after you initialize the custom attributes.

Just remember, as setText called also before you initialize your custom attributes, they may have null value and you can get NullPointerException

Check my example of customTextView which capitalize first letter and adds double dots at the and (I use it in all my activities)

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);
    }
}
like image 34
Kostanos Avatar answered Oct 01 '22 01:10

Kostanos