Search code examples
androidtextviewandroid-custom-viewandroid-stylesandroid-custom-attributes

Why text is not bold when passing the style through custom view attributes?


Suppose I have the .xml style:

<style name="MaterialPreviewDetailsTextSmall">
    <item name="android:layout_marginLeft">@dimen/material_margin</item>
    <item name="android:textColor">@color/material_preview_backgroud</item>
    <item name="android:textSize">@dimen/material_normal_smal_text</item>
    <item name="android:textStyle">bold</item>
</style>

I would like to pass it to a custom view through attributes:

<pl.valueadd.ledoc.view.ViewPreviewRow                            
     ...
     app:view_title="@string/equipment_overview_id_label"         
     app:view_title_style="@style/MaterialPreviewDetailsTextSmall"/>

Inside the custom view I obtain styled attributes and apply it to the TextView:

public class ViewPreviewRow extends RelativeLayout {
    TextView tvTitle;

    public ViewPreviewRow(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        initView(context);
        obtainStyledAttributes(context, attrs);
    }

    private void obtainStyledAttributes(Context context, AttributeSet attrs)  {
        TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.ViewPreviewRow);
        CharSequence text = attributes.getString(R.styleable.ViewPreviewRow_view_title);
        if (text != null) {
            tvTitle.setText(text);
        } else {
            throw new NullPointerException("Attribute view_title cannot be null");
        }
        int appearance = attributes.getResourceId(R.styleable.ViewPreviewRow_view_title_style, 0);
        if (appearance != 0) {
            setTextViewAppearance(tvTitle, appearance);
            tvTitle.requestLayout();
        }
        ...
        attributes.recycle();
    }

The question is:

Why the text has appropriate color, size, marginLeft however is not bold?


Solution

  • I have found a answer.

    The the expected style attributes can be applied one by one:

    int appearance = attributes.getResourceId(R.styleable.ViewPreviewRow_view_title_style, 0);
    applyBold(context, attrs, appearance, tvTitle);
    applyTextSize(context, attrs, appearance, tvTitle);
    applyTextColor(context, attrs, appearance, tvTitle);
    
    
    private void applyBold(Context context, AttributeSet attrs, int style, TextView textView) {
        int[] textStyleAttr = new int[]{android.R.attr.textStyle};
        int indexOfAttrTextStyle = 0;
        TypedArray a = context.obtainStyledAttributes(style, textStyleAttr);
        int styleIndex = a.getInt(indexOfAttrTextStyle, -1);
        a.recycle();
        setTypefaceFromAttrs(textView, styleIndex);
    }
    
    private void applyTextSize(Context context, AttributeSet attrs, int style, TextView textView) {
        int[] textStyleAttr = new int[]{android.R.attr.textSize};
        int indexOfAttrTextStyle = 0;
        TypedArray a = context.obtainStyledAttributes(style, textStyleAttr);
        int size = a.getDimensionPixelSize(indexOfAttrTextStyle, -1);
        a.recycle();
        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
    }
    
    private void applyTextColor(Context context, AttributeSet attrs, int style, TextView textView) {
        int[] textStyleAttr = new int[]{android.R.attr.textColor};
        int indexOfAttrTextStyle = 0;
        TypedArray a = context.obtainStyledAttributes(style, textStyleAttr);
        ColorStateList color = a.getColorStateList(indexOfAttrTextStyle);
        if (color == null) {
            color = ColorStateList.valueOf(a.getColor(indexOfAttrTextStyle, -1));
        }
        a.recycle();
        textView.setTextColor(color);
    }
    
    private void setTypefaceFromAttrs(TextView textView, int styleIndex) {
        textView.setTypeface(Typeface.DEFAULT);
        if (styleIndex == 1) {
            textView.setTypeface(null, Typeface.BOLD);
        } else {
            textView.setTypeface(null, Typeface.NORMAL);
        }
    }
    

    However this solution is very inefficient. It requires initialisation of TypedArray for every expected attribute (initialisation of it is a very memory & time comsuming task). However... it works.