Accessing the the attributes of the inner Views of a custom layout - android

I created a custom layout containing a TextView and a Spinner that extends LinearLayout(called LabeledSpinner).
I put several of these LabeledSpinners into my main RelativeLayout and obviously I want to set different text for each of their textviews but I don't know how to do it in the xml file.
I need to access the attributes of the inner Views somehow please help.
I tried to use [package name].LabeledSpinner.label:text but it didn't work
(label is the id of the textview)

Make LabeledSpinner a styleable (res/values/attrs.xml)
<declare-styleable name="LabeledSpinner">
<attr name="text" format="string"/>
</declare-styleable>
In your LabeledSpinner
public LabeledSpinner(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LabeledSpinner);
String text = a.getString(R.styleable.LabeledSpinner_text);
a.recycle();
}
Note that the text attribute in LabeledSpinner is to be used as LabeledSpinner_text.
Now in your layouts
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:yourpackage="http://schemas.android.com/apk/res/com.package">
<com.package.LabeledSpinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
yourpackage:text="This is where your text goes"/>
</LinearLayout>
That should do!

Related

Custom xml attribute to a #layout reference

I would like to make a custom view with own xml attributes. I would like to specify a header layout that will be inflated in my custom xml view, something like this:
<com.example.MyCustomWidget
android:layout_width="match_parent"
android:layout_height="match_parent"
app:headerLayout="#layout/my_header"
/>
Is that possible and how to i get the layout resource from TypedArray?
So at the end I would like to do something like this:
class MyCustomWidget extends FrameLayout {
public ProfileTabLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ProfileTabLayout);
int headerLayout = a.getLayout(R.styleable.MyCustomView_headerLayout, 0); // There is no such method
a.recycle();
LayoutInflater.from(context)
.inflate(headerLayout, this, true);
}
}
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyCustomWidget">
<attr name="headerLayout" format="reference" />
</declare-styleable>
</resources>
First you have to make your custom field. Yo do this by adding code below to res/values/attrs.xml
<declare-styleable name="MyCustomView">
<attr name="headerLayout" format="reference" />
</declare-styleable>
Then in your custom view you can get this value in constructor
public MyCustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
...
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.MyCustomView,
defStyle, 0
);
try {
int headerLayout = a.getResourceId(R.styleable.MyCustomView_headerLayout, 0);
} finally {
a.recycle();
}
...
}
From here on you can inflate headerLayout with LayoutInflater

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>

Custom xml attributes without attrs.xml file

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

Setting attributes of custom views in Android

I've got a class that extends a button. I want to add an additional attribute to it. Is there any way to initialize that attribute from XML? For example:
<MyButton android:id="#+id/myBtn" myValue="Hello World"></MyButton>
public class MyButton extends Button{
private String myValue = "";
public CalcButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
}
Yes. You can have custom attributes and assign values to it in XML. You can even assign methods to handle custom events on your widgets. Long press definition at XML layout, like android:onClick does
The needed steps.
Implement your custom widget.
Then add file attrs.xml in res/values/ with following content:
<xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyButton">
<attr name="myValue" format="string"/>
</declare-styleable>
</resources>
Retrieve values from XML in MyButton constructor. Remember to implement all constructors, not only one.
Use new attribute in your layout xml:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/**your.package**"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
>
<your.package.MyButton
android:id="#+id/theId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
custom:myValue="myDoSomething"
/>
<!-- Other stuff -->
</LinearLayout>

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

I have a custom layout
public class PersonView extends LinearLayout{
public PersonView(Context context, AttributeSet attrs) {
super(context, attrs);
initUi(context);
}
public PersonView(Context context) {
super(context);
initUi(context);
}
private void initUi(Context context){
LayoutInflater.from(context).inflate(R.layout.person_view, this, true);
profilePicture = (ImageView)findViewById(R.id.profile_picture);
...
}
Layout is defined in xml
<merge xmlns:android="http://schemas.android.com/apk/res/android">
...
Then I use it in other layout
Case 1
// In this case android:layout_margin is specified so it should be used
<my.package.PersonView android:layout_margin="10dp" .../>
Case 2
// In this case android:layout_margin is NOT specified so I want for my PersonView some default value should be used (say 5pt)
<my.package.PersonView .../>
What should I do for my custom layout PersonView to achieve Case 2?
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);

Categories

Resources