How to Use Standard Attribute Android:Text in My Custom View

How to use standard attribute android:text in my custom view?

use this:

public YourView(Context context, AttributeSet attrs) {
super(context, attrs);
int[] set = {
android.R.attr.background, // idx 0
android.R.attr.text // idx 1
};
TypedArray a = context.obtainStyledAttributes(attrs, set);
Drawable d = a.getDrawable(0);
CharSequence t = a.getText(1);
Log.d(TAG, "attrs " + d + " " + t);
a.recycle();
}

i hope you got an idea

How to support tools:text in a new custom view ?

If you want to use existing tools: attributes in you custom views, all you have to do is to add those attributes to your <declare-stleable>:

<declare-styleable name="CustomView">
<!-- This will allow you to use tools:text attribute as well -->
<attr name="android:text"/>
</declare-styleable>

In your custom view you can get the attribute like this:

a.getText(R.styleable.CustomView_android_text)

Now you can do this:

<com.myapplication.CustomView
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Lorem ipsum"/>

This will allow you to use standard tools: attributes in your custom views. If you'd like to define your own design-time attributes, I've written a small library that allows you to do that.

How to get android default attributes in a custom view

You can add android:text to your declared syleable. But be sure to not redeclare it.

<declare-styleable name="CustomEditText">
<attr name="android:text" />
</declare-styleable>

And then get this value from the style like you would with any other of your attributes with the index of R.styleable.CustomEditText_android_text.

CharSequence text = typedArray.getText(R.styleable.CustomEditText_android_text);

Custom view custom attribute not being set without @{}

Mike's comments answer the confusion I had about the different syntaxes.

What I ended up doing ultimately is to inherit the existing android TextView. TextView has a bunch of stuff I needed like font, font size etc, so it was just simpler to inherit rather than re-implement it myself.

Android Custom View with custom Attributes

Change your define <attr name="firstName" format="reference"/>
And in your code use int firstNameViewID = a.getResourceId(attr, -1);

Hope this help!

Default attribute values for my custom view (inherited from LinearLayout)

Similar problem has been solved at https://stackoverflow.com/a/25982512/3554436

Add android:layout_margin to attrs.xml

<declare-styleable name="PersonView">
...
<attr name="android:layout_margin" />
...
</declare-styleable>

Access the attribute like other custom attributes:

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PersonView);
float margin = a.getDimension(R.styleable.PersonView_android_layout_margin, 0);
...
boolean hasMargin = a.hasValue(R.styleable.PersonView_android_layout_margin);

Is it possible in Android to add a custom view composed of standard views and use it in code but have the layout know only about the standard views?

I would use Custom Attributes.

Imagine you have a "Custom View" that takes a title and an image. You could add to your attrs.xml the following:

<?xml version="1.0" encoding="utf-8"?>
<resources>

<declare-styleable name="MyCustomView">
<attr name="title" format="string|reference" />
<attr name="android:drawable" />
</declare-styleable>

</resources>

You'd then implement this custom view in Code:

(something like this... keep in mind this is "pseudo code", I took it from a custom view I have, but it's greatly simplified for the purposes of this...)
In this custom view, I can supply a title and an image, and will also use a custom background (not included in the demo) :)

class MyCustomView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
@AttrRes defStyle: Int = 0
) : ConstraintLayout(context, attrs, defStyle) {

private val title: MaterialTextView
private val image: ImageView

init {
View.inflate(context, R.layout.HERE_GOES_THE_LAYOUT, this).also {
title = it.findViewById(R.id.title)
image = it.findViewById(R.id.image
// set some properties (just an example)
it.setBackgroundResource(R.drawable.EXAMPLE_OF_A_BACKGROUND)
it.isClickable = true
it.isFocusable = true

// I want this view to be clickable and provide material feedback...
val foregroundResId = context.theme.getThemeAttributeValue(R.attr.selectableItemBackground, false)
it.foreground = context.theme.getDrawable(foregroundResId)
}

context.theme.obtainStyledAttributes(attrs, R.styleable.MyCustomView, defStyle, 0).run {
initProperties(this)
recycle()
}
}

private fun initProperties(typedArray: TypedArray) = with(typedArray) {
// The names of these attrs are a mix from attr.xml + the attr name.
getString(R.styleable.MyCustomView_title).also { setTitle(it) }
getDrawable(R.styleable.MyCustomView_android_drawable).also { setImage(it) }
}

private fun setTitle(title: CharSequence?) {
title.text = title
}

private fun setImage(source: Drawable?) {
source?.let {
image.setImageDrawable(it)
}
}
}

This is all supported by my layout: R.layout.HERE_GOES_THE_LAYOUT

Here's a simplified version:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:layout_height="wrap_content"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">

<ImageView
android:id="@+id/image"
android:layout_width="40dp"
android:layout_height="40dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/title"
app:layout_constraintBottom_toBottomOf="parent" />

<com.google.android.material.textview.MaterialTextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:gravity="center_vertical" />

</merge>

And how do I use this?

In any layout! E.g.:

<Some ViewGroup like LinearLayout It Doesn't Matter .... >

<com.the.package.of.your.MyCustomView
android:id="@+id/yourCustomViewOne"
android:layout_width="match_parent"
android:layout_height="wrap_content"
<!-- HERE ARE THE TWO 'custom' ATTRIBUTES -->
android:drawable="@drawable/some_drawable_of_your_choice"
app:title="@string/some_string" />

<com.the.package.of.your.MyCustomView
android:id="@+id/yourCustomViewTwo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawable="@drawable/a_different_drawable_perhaps"
app:title="@string/and_also_a_different_string />

</Some ViewGroup like LinearLayout It Doesn't Matter>

Hopefully you get the idea. You can add more custom attributes, or even (like I did) use an Android one, and internally give it your own meaning (overriding an android attribute and misusing it would likely confuse your users, so be "smart"). E.g.: if the above custom view had MORE than one image, using android:Drawable may not be a great choice, because users wouldn't be sure what drawable is that thing setting... in this example it works "fine" because you only have one image.

Last but not least, don't neglect Accessibility! Provide descriptions where you see fit, test with TalkBack, etc.

How does this answer the question?

Well, you don't need to "expand" the custom view, you just need to encapsulate the dynamic properties so whoever is using the Widget/View, can provide the values via XML (or programmatically) and your custom view does the job behind the scenes.



Related Topics



Leave a reply



Submit