Bug in layout when I use properties from attrs.xml - android

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 :)

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

How to transfer android's properties to my compound view

I want to create a Compound view which consists of an ImageView a TextView and an EditText
I found this lovely tutorial on how to do that, and my compound view looks great
Now I want to copy all the properties a TextView has , all the properties the EditText has and all the properties the ImageView
so that i can style them via xml inside my custom view.
<declare-styleable name="MyCustomView">
<attr name="mcv_label" format="string" />
<attr name="mcv_fieldText" format="string" />
<attr name="mcv_fieldHint" format="string" />
<attr name="mcv_label_id" format="reference" />
<attr name="mcv_field_id" format="reference" />
</declare-styleable>
And am of course able to define them in xml
the point is I want to do
<com.lenabru.myapp.widget.MyCustomView
style="#style/tableRowStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
mcv:mcv_label_id="#+id/tvLabel"
android:text="#string/someText" <=== this line
/>
without aliasing all the properties into mcv stylable
is it possible to do ?
MyCustomView.java
public class MyCustomView extends TableRow{
private EditText editText;
private TextView label;
private ImageView image;
private int labelId;
private int formFieldId;
public FormField(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
initAttributes(context,attrs, 0);
}
public FormField(Context context) {
super(context);
initAttributes(context,null, 0);
}
private void initAttributes(Context context, AttributeSet attrs, int defStyle) {
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.form_field, this,true);
label = (TextView)findViewById(R.id.label);
editText = (EditText)findViewById(R.id.editText);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FormField, defStyle, 0);
if(a.hasValue(R.styleable.MyCustomView_mvc_label_id)) {
labelId = a.getResourceId(R.styleable.FormField_ff_label_id, 0);
if(labelId!=0) {
label.setId(labelId);
}
}
a.recycle();
}
}

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>

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);

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

Categories

Resources