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>
Related
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 am having trouble understanding how to instantiate a custom layout in code. Specifically I do not know how to define the required AttributeSet parameter.
I have gone through the official docs but could not wrap my head around it :
http://developer.android.com/training/custom-views/create-view.html
http://developer.android.com/reference/android/util/AttributeSet.html
In my activity, i instantiate it with :
new MyCustomLayout(getActivity(), attrs)
But how do i define attrs?
MyCustomLayout.java
public class MyCustomLayout extends LinearLayout implements
OnClickListener {
...
...
public MyCustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.mycustomxmllayout, this, true);
...
}
...
}
mycustomxmllayout.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
<TextView
android:id="#+id/txt_1"
android:layout_width="60dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:lines="1"/>
<Button
android:id="#+id/btn_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="0dp"
android:minWidth="0dp"
android:background="#null"
android:text="x"
android:layout_weight="1"
/>
</merge>
Firstly, you should define your custom attributes in the res/values/attrs.xml, just add <declare-styleable>, for example:
<declare-styleable name="MyCustomLayout">
<attr name="showText" format="boolean" />
<attr name="buttonPosition" format="enum">
<enum name="left" value="0"/>
<enum name="right" value="1"/>
</attr>
</declare-styleable>
Now you can set your custom attrs via xml, like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto">
<your.package.name.MyCustomLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
custom:showText="true"
custom:buttonPosition="left" />
</LinearLayout>
And in your constructor you can grab that attrs:
public MyCustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.MyCustomLayout,
0, 0);
try {
mShowText = a.getBoolean(R.styleable.MyCustomLayout_showText, false);
mButtonPos = a.getInteger(R.styleable.MyCustomLayout_buttonPosition, 0);
} finally {
a.recycle();
}
}
If you want to set your attrs programmatically, you should create public getters/setters for that:
private boolean mShowText;
private Integer mButtonPos;
public void setButtonPos(int pos) {
mButtonPos = pos;
}
public void setShowText(boolean showText) {
mShowText = showText;
}
After that you can set your attrs programmatically:
MyCustomLayout layout = new MyCustomLayout(getActivity());
layout.setButtonPos(0);
layout.setShowText(true);
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);
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
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>