Custom xml attributes without attrs.xml file - android

Recently I've faced with adding custom xml parameters to my views in xml layout. I know that I should use attrs.xml file for this purpose, but... I have found, that I can use custom parameters without any attrs.xml file at all. Can somebody explain this ? Is this a bug or is this a valid case ?
here is my custom view:
public class TestView extends View {
public TestView(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public TestView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
final String scheme = "http://red.com/ui/scheme";
if (attrs != null) {
Log.d("TestView", "custom param value: " + attrs.getAttributeValue(scheme, "cutom"));
}
}
}
and here is the main layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:red="http://red.com/ui/scheme"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello" />
<com.red.ui.TestView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#ffAABBCC"
red:cutom="customvalue"
/>
</LinearLayout>
It is a simple scratch project, created by Android wizard.

The custom attribute that you added is not available in R.java
I think the main motto of making custom attributes is to use it at multiple places.
But through this code we cann't accomplish the same scenario.
Here is the sample code - attrs.xml file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyLayout">
<attr name="text" format="string" />
</declare-styleable>
</resources>
I am changing main.xml to add the the text attribute
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:red="http://red.com/ui/scheme"
xmlns:myapp="http://schemas.android.com/apk/res/com.psl"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello"
myapp:text="Text String" />
<com.psl.TestView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#ffAABBCC"
myapp:text="My Special Text String"
red:cutom="customvalue" />
</LinearLayout>
TestView.java -
public class TestView extends View {
public TestView(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public TestView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
final String scheme = "http://red.com/ui/scheme";
if (attrs != null) {
Log.d("TestView", "custom param value: " + attrs.getAttributeValue(scheme, "cutom"));
}
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.MyLayout);
CharSequence s = a.getString(R.styleable.MyLayout_text);
Log.d("MyTestView", "attrs param value: " + s.toString());
}
}
If you noticed after making the attr in attrs.xml. It is available everywhere.
But the attr defined in xml itself through custom namespace is available only through the namespace that you have to define everywhere.
May be its a bug because the attribute is getting added to some custom namespace and not in the application itself.

This is not a "bug" of course. This is how you use a custom view in your xml.
refer to this : http://developer.android.com/guide/topics/ui/custom-components.html

Related

getting string from StyledAttributes custom class Android

I have a custom class, extending android.support.v7.widget.AppCompatImageView where I would like to catch a custom string value from XML during its construction.
I followed this top answer, everything compile like a charms however when I try to acces my custom value, I get Null.
Here is my custom class
public class CustomImageView extends android.support.v7.widget.AppCompatImageView {
private Context context = null;
private AttributeSet attrs = null;
public CustomImageView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
this.context = context;
this.attrs = attrs;
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CustomImageView);
String e = ta.getString(R.styleable.CustomImageView_customUrl);
System.out.print(e) // e is null
ta.recycle();
}
}
here is my activity.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.custom.customView.customImageView
android:id="#+id/image"
android:src="#mipmap/ic_launcher_round"
android:alpha="1"
app:customUrl="http://google.fr"/>
...
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CustomImageView">
<attr name="customUrl" format="string" />
</declare-styleable>
</resources>
I understand there might be plenty of answer about similar or pseudo similar cases however, I couldn't manage to find any answer regarding getting null from getString
Edit : fix wrong link

NumberFormatException when using styleable attrs

I created a custom view for Android which renders two inner views to store a key and a value in two columns. The class looks like this:
public class KeyValueRow extends RelativeLayout {
protected TextView mLabelTextView;
protected TextView mValueTextView;
public KeyValueRow(final Context context) {
super(context);
init(context, null, 0);
}
public KeyValueRow(final Context context, final AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public KeyValueRow(final Context context,
final AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
protected void init(final Context context,
final AttributeSet attrs, int defStyle) {
View layout = ((LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE))
.inflate(R.layout.key_value_row, this, true); // line 46
mLabelTextView = (TextView) layout.findViewById(
R.id.key_value_row_label);
mValueTextView = (TextView) layout.findViewById(
R.id.key_value_row_value);
}
public void setLabelText(final String text) {
mLabelTextView.setText(text);
}
public void setValueText(final String text) {
mValueTextView.setText(text);
}
}
The associated layout file layout/key_value_row.xml looks like this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="1.0">
<TextView
android:id="#+id/key_value_row_label"
style="#style/KeyValueLabel"
android:layout_weight=".55"
tools:text="Label"/>
<TextView
android:id="#+id/key_value_row_value"
style="#style/KeyValueValue"
android:layout_weight=".45"
tools:text="Value"/>
</LinearLayout>
This can be used as follows in a layout:
<com.example.myapp.customview.KeyValueRow
android:id="#+id/foobar"
style="#style/KeyValueRow" />
Until here everything works fine!
The challenge
Now, I want to allow custom settings for the layout_weight attributes of both inner TextViews. Therefore, I prepared the attributes in values/attrs.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="KeyValueRow">
<attr name="label_layout_weight" format="float" />
<attr name="value_layout_weight" format="float" />
</declare-styleable>
</resources>
First question would be: is float the correct format for layout_weight?
Next, I would apply them in the layout file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="1.0">
<TextView
android:id="#+id/key_value_row_label"
style="#style/KeyValueLabel"
android:layout_weight="?label_layout_weight"
tools:text="Label"/>
<TextView
android:id="#+id/key_value_row_value"
style="#style/KeyValueValue"
android:layout_weight="?value_layout_weight"
tools:text="Value"/>
</LinearLayout>
Then they can be used in the example:
<com.example.myapp.customview.KeyValueRow
android:id="#+id/foobar"
style="#style/KeyValueRow"
custom:label_layout_weight=".25"
custom:value_layout_weight=".75" />
When I run this implementation the following exception is thrown:
Caused by: java.lang.NumberFormatException: Invalid float: "?2130772062"
at java.lang.StringToReal.invalidReal(StringToReal.java:63)
at java.lang.StringToReal.parseFloat(StringToReal.java:310)
at java.lang.Float.parseFloat(Float.java:300)
at android.content.res.TypedArray.getFloat(TypedArray.java:288)
at android.widget.LinearLayout$LayoutParams.<init>(LinearLayout.java:1835)
at android.widget.LinearLayout.generateLayoutParams(LinearLayout.java:1743)
at android.widget.LinearLayout.generateLayoutParams(LinearLayout.java:58)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:757)
at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
at com.example.myapp.customview.KeyValueRow.init(KeyValueRow.java:46)
at com.example.myapp.customview.KeyValueRow.<init>(KeyValueRow.java:28)
... 33 more
You can use
private void applyAttributes(Context context, AttributeSet attrs) {
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs, R.styleable.KeyValueRow, 0, 0);
try {
labelWeight = a.getFloat(
R.styleable.KeyValueRow_label_layout_weight, 0.55f);
valueWeight = a.getFloat(
R.styleable.KeyValueRow_value_layout_weight, 0.45f);
} finally {
a.recycle();
}
}
and after this you can apply this via:
mLabelTextView = (TextView) layout.findViewById(R.id.key_value_row_label);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, labelWeight);
mLabelTextView.setLayoutParams(layoutParams);
To get it running replace android:layout_weight="?label_layout_weight" with some default value such as android:layout_weight="0.5" in layout/key_value_row.xml. It will be overwritten.

Reference a string value in a styleable attribute

I am trying to extend an android.widget.Button and added a styleable attribute to my custom widget, which should hold a reference to a value in res/values/strings.xml.
<resources>
<attr name="infoText" format="reference" />
<declare-styleable name="FooButton">
<attr name="infoText" />
</declare-styleable>
</resources
In my layout I have something like this:
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="horizontal">
<com.example.FooButton
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/fooButton"
infoText="#string/fooButtonInfoText" />
</LinearText>
My res/values/strings.xml looks like this:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="fooButtonInfoText">BAR</string>
</resources>
The extraction of the attribute value in my custom FooButton looks like this:
TypedArray typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.FooButton);
Integer infoTextId = typedArray.getResourceId(R.styleable.FooButton_infoText, 0);
if (infoTextId > 0) {
infoText = context.getResources().getString(infoTextId);
}
typedArray.recycle();
I've got these three constructors implemented:
public FooButton(Context context) {
super(context);
}
public FooButton(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
setInfoText(context, attributeSet);
}
public FooButton(Context context, AttributeSet attributeSet, int defStyle) {
super(context, attributeSet, defStyle);
setInfoText(context, attributeSet);
}
The method FooButton.setInfoText(context, attributeSet) is called every time there is a FooButton declared.
I am fighting this problem for too long and read dozens of Stackoverflow questions... why does this not work?
You must declare the namespace for custom attributes. It should look like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/auto"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="horizontal">
<com.example.FooButton
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/fooButton"
app:infoText="#string/fooButtonInfoText" />
</LinearText>

Inflating Custom Views - Layout File Inconsistencies

No, no one answered that question, and the problem still remains... This question here is about another symptom to the same problem (please see comments below):
In Monodroid atleast, when inflating a custom view from a layout, sometimes it needs to be wrapped in a ViewGroup (ie, LinearLayout) in order to not get an exception, and other times does not.
I was wondering if anyone is familiar with this situation, and if it happens in "raw" Android as well (ie, no Monodroid) ?
I always first try without, as in
TextView1.axml
<?xml version="1.0" encoding="utf-8"?>
<Monodroid.Activity1.TextView1
android:id="#+id/text_view1"
android:layout_width="300dp"
android:layout_height="50dp"/>
but if I get an inflation exception, then I'll have to wrap it up
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ll_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Monodroid.Activity1.TextView1
android:id="#+id/text_view1"
android:layout_width="300dp"
android:layout_height="50dp"/>
</LinearLayout>
where
public class TextView1 : TextView
{
public TextView1 (Context context) : base(context) { }
public TextView1 (Context context, IAttributeSet attributes) : base(context, attributes) { }
public TextView1 (Context context, IAttributeSet attributes, int defStyle) : base(context, attributes, defStyle) { }
}
Thank you.
This layout file inflates with no containing viewgroup:
<?xml version="1.0" encoding="utf-8"?>
<fieldinspection.droid.views.custom.FieldInput
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/RecordDataFieldInput"
style="#style/FieldInput"
android:layout_marginRight="0dip"/>
and this one (inner class PagedFragmentFieldInput) does not (it needs to be within a LinearLayout or else inflation exception):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ll_record_data_field_input2_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<FieldInspection.Droid.Views.ComplaintView.PagedFragmentRecordDataFieldBox.PagedFragmentFieldInput
android:id="#+id/RecordDataFieldInput"
style="#style/FieldInput"
android:layout_marginRight="0dip"/>
</LinearLayout>
Its read as PagedFragment-RecordDataFieldBox, its a RecordDataFieldBox thats within a Fragment thats within a ViewPager.
I took your first sample and tried it out here. I get no error wrapping it or not.
TextViewInherit.cs:
using Android.Content;
using Android.Util;
using Android.Widget;
namespace InflationShiz
{
public class TextViewInherit : TextView
{
public TextViewInherit(Context context, IAttributeSet attrs) :
this(context, attrs, 0)
{
}
public TextViewInherit(Context context, IAttributeSet attrs, int defStyle) :
base(context, attrs, defStyle)
{
}
}
}
One.axml:
<?xml version="1.0" encoding="utf-8"?>
<inflationshiz.TextViewInherit
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
Two.axml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<inflationshiz.TextViewInherit
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
Both work when I inflate in my Activity like so:
var one = LayoutInflater.Inflate(Resource.Layout.One, null);
var two = LayoutInflater.Inflate(Resource.Layout.Two, null);
I find it hard to reproduce your issue; Your code is scattered over 3 different SO questions and even more scattered because you have created answers to your own question where you try to elaborate on your initial questions.

Get animation from custom attribute in constructor

I have a custom view, extending LinearLayout and im trying to add some custom attributes, like this:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- other stuff -->
<declare-styleable name="UIPlayer">
<attr name="team" format="integer" />
<attr name="playAnimation" format="reference" />
</declare-styleable>
</resources>
My custom view is like this:
public class UIPlayer extends LinearLayout
{
// . . .
public UIPlayer(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
initAttrs(context, attrs);
}
public UIPlayer(Context context, AttributeSet attrs)
{
super(context, attrs);
initAttrs(context, attrs);
}
public void initAttrs(Context context, AttributeSet attrs)
{
TypedArray taPlayer = context.obtainStyledAttributes(attrs, R.styleable.UIPlayer);
int team = taPlayer.getInt(R.styleable.UIPlayer_team, -1);
player.setTeam(team);
int animPlayId = taPlayer.getResourceId(R.styleable.UIPlayer_playAnimation, -1);
try
{
animPlay = AnimationUtils.loadAnimation(context, animPlayId);
}
catch (NotFoundException e)
{
d("anim", "onPlay not found! " + animPlayId);
}
taPlayer.recycle();
}
// . . .
}
My layout file is like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:devn="http://schemas.android.com/apk/res/devN.games.mygame"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clipChildren="false"
android:orientation="vertical"
android:weightSum="3.0" >
<devN.games.UIPlayer
android:id="#+id/player"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:clipChildren="false"
android:gravity="top"
devn:playAnimation="#anim/play"
devn:team="2" >
</devN.games.UIPlayer>
<!-- ... -->
</LinearLayout>
What im doing wrong and i cant retrive the animation from within the contstructor? (the "strange fact" is that the team attribute is working fine)
After a lot of studies in android source code, i tried the overload methode:
obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
and it worked like a charm!! :)
So the correct code is:
TypedArray taPlayer = context.obtainStyledAttributes(attrs, R.styleable.UIPlayer, 0, 0);

Categories

Resources