I've been working on a Custom Control for Android and although I tried to do what's suggested here there seems to be something I'm doing wrong.
Here's my code to see if anyone can spot the problem:
MyComponent.java
public MyComponent(Context context, AttributeSet attrs)
{
super(context);
TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.MyComponent);
CharSequence myId = arr.getString(R.styleable.MyComponent_identifier);
if (myId != null)
{
this.setIdentifier(myId.toString());
}
Integer cds = arr.getInteger(R.styleable.MyComponent_cd_number, 0);
if(cds != null)
{
this.setCds(cds);
}
arr.recycle();
}
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyComponent">
<attr name="cd_number" format="integer" />
<attr name="identifier" format="string" />
</declare-styleable>
</resources>
main.xml
<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bgl="http://schemas.android.com/apk/res/my.test.package.components"
android:id="#+id/table"
android:layout_width="match_parent"
android:layout_height="match_parent">
...
<my.test.package.MyComponent
android:id="#+id/hand"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_span="2"
bgl:cd_number="4"
bgl:identifier="plr"/>
...
</TableLayout>
When I put this I get the following errors:
error: No resource identifier found for attribute 'cd_number' in package 'my.test.package'
error: No resource identifier found for attribute 'identifier' in package 'my.test.package'
If I change my namespace to something like:
xmlns:bgl="http://schemas.mywhatever.com/apk/res/my.test.package"
...the errors go way and the thing runs but myId is null and cds is 0 (the default value!) back on the MyComponent.java constructor.
I'd say it's some very basic mistake but I not being able to spot it and since there's not much documentation on this I decided to ask here.
Thanks in advance!
Ok. I got it solved!
On the original post I had:
xmlns:bgl="http://schemas.android.com/apk/res/my.test.package
...but in my source I had:
xmlns:bgl="http://schemas.android.com/apk/res/my.test.package.components
...because I thought one should put the URI to the components package.
THIS IS WRONG!
On the xmlns it should be the application name as is declared on the Manifest!
When I removed the "components" part of the xmlns it "matched" the application name in the Manifest and the errors went away and when I ran the thing in debug I could actually see the values I was passing to the parameters in the XML!
Hope this helps someone else! :-)
UPDATE
Later on I had the need to move the control into a library and faced the problem again. It seems that when you put the component in a library and use it on a client app you must declare the xmlns as below:
xmlns:myns="http://schemas.android.com/apk/res-auto"
If you do so (and have the library declared as an Android dependency) Eclipse (or is it Android?) will search the dependencies for the appropriate attribute bindings.
I had a problem similar to this, turned out it was calling a different constructor
Try with the constructor that takes in the defStyle parameter
public MyComponent(Context context, AttributeSet attrs, int defStyle)
Related
I am declaring a styleable view with attrs, and I created the file myview_attrs.xml this simple way:
<resources>
<declare-styleable name="TestStyleable">
<attr name="testAttr" format="integer"/>
</declare-styleable>
</resources>
Then, I want to load this attr in my code:
void loadAttributes(AttributeSet attrs) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.);
}
After R.styleable. , with ctrl-SPACE, these are the options I am given:
That is obviously wrong. Indeed, I can't select R.styleable.TestStyleable, because it's not proposed in the menu, but only R.styleable.TestStyleable_testAttr, that does not make sense.
Of course, if I choose the second one it gives me error, because a int[] is expected and I put an int. On the other side, if I manually write R.styleable.TestStyleable fails with "Cannot resolve symbol".
What am I missing here??
Update: this ended to be an Android Studio bug.
As #Yurii Tsap stated, this is an Android Studio bug, and it finally got fixed by making
File -> Invalidate Caches / Restart
I have only recently had to set the xmlns attribute in an Android layout file. Initially when I was adding a third-party control, certain attributes in the control's XML didn't have a prefix to identify the namespace. When I ran my app, the control was displayed but those attributes that didn't have the namespace prefix were ignored. Only after adding the xmlns to the top of my file and adding the prefix to the attributes did those attributes get recognized at run time. Here is what the corrected code looks like:
xmlns:fab="http://schemas.android.com/apk/res-auto"
<com.getbase.floatingactionbutton.FloatingActionButton
android:id="#+id/ivFAB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fab:fab_icon="#drawable/ic_fab_star"
fab:fab_colorNormal="#color/pink_500"
fab:fab_colorPressed="#color/pink_100"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginRight="15dp"
android:layout_marginBottom="15dp"
android:visibility="visible"
/>
The xmlns prefix her is 'fab'. What I don't understand is that without the namespace and prefix, the app compiles without any errors. Why doesn't Android Studio complain that it cannot find fab_icon? Why does it just ignore these attributes? I have seen a number of posts throughout stackoverflow on different topics where someone has indicated to leave out the prefix and then the code worked. So I'm at a loss to understand what's going on. In some problems (like mine) having the prefix is required but in others it isn't? Is this an issue with different versions of Android Studio or the SDK versions?
Yes. Even you can define your own custom layout attributes.
Step 1: Create a subclass of a view.
class PieChart extends View {
public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
Step 2: Define Custom Attributes with <declare-styleable> in res/values/attrs.xml .
<resources>
<declare-styleable name="PieChart">
<attr name="showText" format="boolean" />
<attr name="labelPosition" format="enum">
<enum name="left" value="0"/>
<enum name="right" value="1"/>
</attr>
</declare-styleable>
</resources>
Step 3: Using those attributes inside your layout xml.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.example.customviews">
<com.example.customviews.charting.PieChart
custom:showText="true"
custom:labelPosition="left" />
</LinearLayout>
Step 4: Applying custom attributes to your view.
public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.PieChart,
0, 0);
try {
mShowText = a.getBoolean(R.styleable.PieChart_showText, false);
mTextPos = a.getInteger(R.styleable.PieChart_labelPosition, 0);
} finally {
a.recycle();
}
}
Step 5: Adding properties and events
Attributes are a powerful way of controlling the behavior and appearance of views, but they can only be read when the view is initialized. To provide dynamic behavior, expose a property getter and setter pair for each custom attribute. The following snippet shows how PieChart exposes a property called showText
public boolean isShowText() {
return mShowText;
}
public void setShowText(boolean showText) {
mShowText = showText;
invalidate();
requestLayout();
}
For more information and details, read this link.
Here is the code I am using:
public ASSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray sharedTypedArray = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.ASSwitch,
0, 0);
try {
onText = sharedTypedArray.getText(R.styleable.ASSwtich_onText, null);
} finally {
sharedTypedArray.recycle();
}
}
Here is the attrs.xml file (added to values folder):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ASSwitch">
<attr name="onText" format="string" />
<attr name="offText" format="string" />
<attr name="onState" format="boolean" />
<attr name="toogleDrawable" format="string" />
<attr name="frameDrawable" format="string" />
</declare-styleable>
</resources>
The answers in these questions couldn't fix the problem. Please don't consider my question as duplicate.
Android Hello, Gallery tutorial -- "R.styleable cannot be resolved"
Android: How to Declare Styleable in R.java?
R.styleable can not be resolved, why?
R.styleable cannot be resolved
Update: It seems that I was importing the wrong R class. It shall be the application's R class not android.R.
Check your imports:
Wrong: Android.R
Correct: com.example.yourproject.R
I had the same error when made this customized view. Maybe when follow the guiding step, the helper tool automatically inserts this wrong import.
It seems that I was importing the wrong R class. It shall be the application's R class not android.R
Facebook Sdk for Android does not take login_text and logout_text values passed from XML. It just ignores it. There is no documentation/example available on earth which uses this customization of the button.
<com.facebook.widget.LoginButton
xmlns:fb="http://schemas.android.com/apk/res-auto"
android:id="#+id/connectWithFbButton"
style="#style/com_facebook_loginview_default_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center_horizontal"
android:text="#string/connect_with_facebook"
fb:login_text="#string/connect_with_facebook"
fb:logout_text="Connecting with facebook" />
It always says Login In or Log Out. How can I use this property? I tried debugging through the LoginButton code but in vain. It never goes through any of the constructors of LoginButton() and hence cannot parse my custom parameters.
Did someone face this issue?
Solved!!
Add string with name "com_facebook_loginview_log_in_button" to strings.xml located in res>values>strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="com_facebook_loginview_log_in_button">Connect</string>
<string name="com_facebook_loginview_log_out_button">Your Logout</string>
</resources>
Your first solution is correct.
It didn't worked because you've defined the style atrribute:
style="#style/com_facebook_loginview_default_style"
From the LoginButton source code:
public LoginButton(Context context, AttributeSet attrs) {
super(context, attrs);
if (attrs.getStyleAttribute() == 0) {
do stuff ...
}
}
So, it will only look at the defined attribute "login_text" if you leave "style" attribute undefined.
Gabriel is spot on. To fix this, go to LoginButton.java in Facebook SDK and move the parseAttributes call
parseAttributes(attrs)
to the bottom so that it always runs.
Whole method will look like this :
public LoginButton(Context context, AttributeSet attrs) {
super(context, attrs);
if (attrs.getStyleAttribute() == 0) {
// apparently there's no method of setting a default style in xml,
// so in case the users do not explicitly specify a style, we need
// to use sensible defaults.
this.setTextColor(getResources().getColor(R.color.com_facebook_loginview_text_color));
this.setTextSize(TypedValue.COMPLEX_UNIT_PX,
getResources().getDimension(R.dimen.com_facebook_loginview_text_size));
this.setPadding(getResources().getDimensionPixelSize(R.dimen.com_facebook_loginview_padding_left),
getResources().getDimensionPixelSize(R.dimen.com_facebook_loginview_padding_top),
getResources().getDimensionPixelSize(R.dimen.com_facebook_loginview_padding_right),
getResources().getDimensionPixelSize(R.dimen.com_facebook_loginview_padding_bottom));
this.setWidth(getResources().getDimensionPixelSize(R.dimen.com_facebook_loginview_width));
this.setHeight(getResources().getDimensionPixelSize(R.dimen.com_facebook_loginview_height));
this.setGravity(Gravity.CENTER);
if(isInEditMode()) {
// cannot use a drawable in edit mode, so setting the background color instead
// of a background resource.
this.setBackgroundColor(getResources().getColor(R.color.com_facebook_blue));
// hardcoding in edit mode as getResources().getString() doesn't seem to work in IntelliJ
loginText = "Log in";
} else {
this.setBackgroundResource(R.drawable.com_facebook_loginbutton_blue);
initializeActiveSessionWithCachedToken(context);
}
}
parseAttributes(attrs);
}
both style and login_text are taken under consideration now. Works fine for me.
I ended up just having to change the text in the FacebookSDK source.
The login string is com_facebook_loginview_log_in_button located in FacebookSDK/res/values/strings.xml
As a shortcut, you can change the login button text directly in FB SDK 4.0 in com.facebook.login.widget.LoginButton class->setButtonText() method
I tried many of these solutions but nothing works, finally I came to an end with :
Locale loc = Locale.FRENCH;
loginButton = (LoginButton)findViewById(R.id.fb_login_button);
loginButton.setTextLocale(loc);
This changes the text to the locale you set and also the confirm dialogs, at least what I need.
Hope this helps.
My problem is that loading an array of strings defined in XML works in the app but will result in an error in the ADT Graphical Layout preview.
Now I can't see any graphics in the Graphical Layout because of this error, and it's difficult to work with other graphics.
But the view is loading and displaying the strings fine if I build and run my app.
So I suppose my code is correct but either:
I am missing some limitations of the Graphical Layout preview and some workaround
or perhaps I'm missing something obvious and doing things wrong even if it seems to work in the app
I have a custom view where I get an array defined by me in an array.xml file.
public class ScoreTable extends View {
[...]
#Override
protected void onDraw(Canvas canvas) {
[...]
int score_vals[] = getResources().getIntArray(R.array.score_vals);
[...]
}
[...]
}
My array is defined in res/values/array.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<array name="score_vals">
<item >10</item>
<item >20</item>
<item >50</item>
</array>
</resources>
Graphical Layout is blank and says:
Int array resource ID #0x7f050000
Exception details are logged in Window > Show View > Error Log
But of course I have "public static final int score_vals=0x7f050000;" in R.java!
The details of this error are in a 50-deep stack, but resumes to this:
android.content.res.Resources$NotFoundException: Int array resource ID #0x7f050000
at android.content.res.Resources.getIntArray(Resources.java:405)
at com.threecats.poker.ScoreTable.onDraw(ScoreTable.java:53)
at android.view.View.draw(View.java:6740)
[...]
So, should getResources().getXXXArray() work in the context of a ADT Graphical Layout preview?
I would like to mention that I tried with both "array" and "array-integer" in the XML, and both work in the app but not in the preview.
Also I tried to save the Context from the constructor of the view in a private Context member... didn't help either.
Your code is alright but unfortunately there are still some bugs in ADT plugin and there is one of them. Layout Editor has troubles with rendering custom views. I had the same issue and the only workout I have found is checking View.isInEditMode and initializing int array in some other way but not from resources. So your code will look like this:
int score_vals[];
if (isInEditMode()) {
score_vals = { 10, 20, 50 };
} else {
score_vals = getResources().getIntArray(R.array.score_vals);
}
And by the way don't create or load any resources in your onDraw methods. I suppose getResources().getIntArray uses some sort of caching but anyway your perfomance may suffer.
I found a kind of a workaround whereby you have to hijack android's own attributes to get access to resources in the designer.
The following should provide the idea, but you would have to find a native android property of type int[]
This custom view XML should render in the graphical layout preview while using resources
<!-- Could override individual attributes here too rather than using a style -->
<com.github.espiandev.showcaseview.ShowcaseView
style="#style/ShowcaseView"/>
styles.xml - Style specifying some of the resources to use
<style name="ShowcaseView" parent="match_fill">
<!--# Cling drawable -->
<item name="android:src">#drawable/cling</item>
<!--# Title #-->
<item name="android:contentDescription">#string/showcase_title</item>
<!--# Description #-->
<item name="android:description">#string/showcase_description</item>
<!--# Button Text #-->
<item name="android:text">#string/ok</item>
<item name="sv_titleTextColor">#33B5E5</item>
<item name="sv_detailTextColor">#FFFFFF</item>
<item name="sv_backgroundColor">#3333B5E5</item>
<item name="sv_buttonBackgroundColor">#3333B5E5</item>
<item name="sv_buttonForegroundColor">#33B5E5</item>
</style>
attrs.xml - Custom attribute definition compatible with design-time preview
<!-- The android attrs assume the corresponding android format / data type -->
<declare-styleable name="ShowcaseView">
<!--# Cling drawable -->
<attr name="android:src"/>
<!--# Title #-->
<attr name="android:contentDescription"/>
<!--# Description #-->
<attr name="android:description"/>
<!--# Button Text #-->
<attr name="android:text"/>
<attr name="sv_backgroundColor" format="color|reference" />
<attr name="sv_detailTextColor" format="color|reference" />
<attr name="sv_titleTextColor" format="color|reference" />
<attr name="sv_buttonBackgroundColor" format="color|reference" />
<attr name="sv_buttonForegroundColor" format="color|reference" />
</declare-styleable>
ShowcaseView.java - Using the custom attributes in the custom view
public ShowcaseView(Context context) {
this(context, null, R.styleable.CustomTheme_showcaseViewStyle);
}
public ShowcaseView(Context context, AttributeSet attrs) {
this(context, attrs, R.styleable.CustomTheme_showcaseViewStyle);
}
public ShowcaseView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// Get the attributes for the ShowcaseView
final TypedArray styled = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ShowcaseView, 0, 0);
showcase = styled.getDrawable(R.styleable.ShowcaseView_android_src);
titleText = styled.getString(R.styleable.ShowcaseView_android_contentDescription);
subText = styled.getString(R.styleable.ShowcaseView_android_description);
buttonText = styled.getString(R.styleable.ShowcaseView_android_text);
backColor = styled.getInt(R.styleable.ShowcaseView_sv_backgroundColor, Color.argb(128, 80, 80, 80));
detailTextColor = styled.getColor(R.styleable.ShowcaseView_sv_detailTextColor, Color.WHITE);
titleTextColor = styled.getColor(R.styleable.ShowcaseView_sv_titleTextColor, Color.parseColor("#49C0EC"));
styled.recycle();
// Now make use of the fields / do further initialization ..
}