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
Related
I would like to set a custom attribute (in this case, app:unfocusColor) to the value of another attribute (e.g app:cardBackgroundColor), is this possible?
I try the below method, but has error
<android.support.v7.widget.CardView
.....
app:cardBackgroundColor="#android:color/holo_blue_bright"
app:unfocusColor="#{app:cardBackgroundColor}"
>
yes you can!
for example
create res/valuesmyattr.xml
use this code as content
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="myatt">
<attr name="viewName" format="string"/>
</declare-styleable>
</resources>
in your activity use this code
#Override
protected void onCreate(Bundle savedInstanceState) {
getLayoutInflater().setFactory(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
int id =attrs.getAttributeResourceValue("http://schemas.android.com/apk/res/android","id",-1);
if (id == R.id.specialViewId) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.myatt);
String viewName = typedArray.getString(R.styleable.myatt_viewName);
}
return super.onCreateView(parent, name, context, attrs);
}
and your view should lookalike this
<TextView
app:number="1"
android:id="#+id/specialViewId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:ignore="MissingPrefix"></TextView>
finally if you want use this with out overriding activity you can use LayourInflaterFactory interface
I am trying to use two properties from attrs.xml such as: content and handle. When I use them while building the layout the view goes away. I have been trying the bug for 3 days and nothing worked.
Help is much appreciated!
Code in Context
<hh.hhh.app.android.hhhh.widget.ExpandablePanel
android:id="#+id/expandablePanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:handle="#+id/expandButton"
app:content="#+id/listViewProduct"
app:collapsedHeight="50dip"
app:animationDuration="25">
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/listViewProduct"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/expandButton"/>
</hh.hhh.app.android.hhhh.widget.ExpandablePanel>
attrs.xml code
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ExpandablePanel">
<attr name="handle" format="reference" />
<attr name="content" format="reference" />
<attr name="collapsedHeight" format="dimension"/>
<attr name="animationDuration" format="integer"/>
</declare-styleable>
</resources>
I am sharing my own custom class as an example. So for all custom function you need to first check if it is in EditMode or not. isInEditMode() tells us is the layout is being render by IDE preview or by phone. IDE preview is not so much powerful to run our custom codes, so it is recommended to do not run our custom while rendering by preview. I am attaching my custom class to just understand the exact scenario.
public class DynamicImageView extends ImageView {
static DynamicImageTypeListener masterImageTypeListener;
DynamicImageTypeListener imageTypeListener;
int imageResourceArray;
public DynamicImageView(Context context, AttributeSet attr, int defStyle) {
super(context, attr, defStyle);
initValues(attr, defStyle);
}
public DynamicImageView(Context context, AttributeSet attr) {
super(context, attr);
initValues(attr, 0);
}
public DynamicImageView(Context context) {
super(context);
}
public static void setMasterImageTypeListener(DynamicImageTypeListener masterImageTypeListener) {
DynamicImageView.masterImageTypeListener = masterImageTypeListener;
}
public void setImageTypeListener(DynamicImageTypeListener imageTypeListener) {
this.imageTypeListener = imageTypeListener;
}
private void initValues(AttributeSet attr, int defStyle) {
if (!isInEditMode()) { //This code will run only on phone not by preview.
TypedArray a = getContext().obtainStyledAttributes(attr, R.styleable.DynamicImageView, defStyle, 0);
imageResourceArray = a.getResourceId(0, 0);
updateImage();
a.recycle();
}
}
public void updateImage() {
TypedArray imgs = getResources().obtainTypedArray(imageResourceArray);
int imageIndex = imageTypeListener != null ? imageTypeListener.getImageType(getContext()) : (masterImageTypeListener != null ? masterImageTypeListener.getImageType(getContext()) : 0);
setImageResource(imgs.getResourceId(imageIndex, -1));
}
}
Hope it will help :)
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.
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>
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