When I compile a project in which I use BindingAdapter, the Android Studio always show lots of warning and jump into the source file which I write code of BindingAdapter.How can I solve it, I never want to show it when I compile and also I don't want to remove the namespace app or other, can anyone help me. Thank you!!! The Warning is below:
The example source code:
As #Michael Spitsin pointed out in the comment, just remove the "app:" namespace from the annotation. You can keep the "app:" namespace in your layout XML. The namespace will be removed internally unless it is android:; all other namespaces are treated the same.
The warning serves to inform you that the namespace does not have any effect on the annotation. So, for example, you cannot have different methods for #BindingAdapter("app:src") and #BindingAdapter("foo:src") -- the namespace is removed before it's used as a key in the implementation of that annotation. The only exception is the android namespace; you can have #BindingAdapter("android:src") and also #BindingAdapter("app:src") as well.
If you look through the source code that implements the #BindingAdapter annotation, you'll see that the namespace is removed, which is the reason for the warning. For example, in this excerpt from android.databinding.tool.store.SetterStore.addBindingAdapter:
public void addBindingAdapter(ProcessingEnvironment processingEnv, String attribute,
ExecutableElement bindingMethod, boolean takesComponent) {
attribute = stripNamespace(attribute);
...you can see that the namespace is stripped, like so:
private static String stripNamespace(String attribute) {
if (!attribute.startsWith("android:")) {
int colon = attribute.indexOf(':');
if (colon >= 0) {
attribute = attribute.substring(colon + 1);
}
}
return attribute;
}
This is done because that the binding adapter needs to work across any and all source files in the project, regardless of what string was used for the namespace in a particular XML file.
Related
I'm kinda new to Android development so my question might be weird or not even possible. I wouldn't know!
Anyway, I'm building multiple apps that will have a lot of shared elements, so I decided to build a library with those components and use it in all of the apps, rather than stupid copying and pasting code.
For example, the library handles the welcome screen and login/signup flow activities, among other things. So here are the problems this approach might cause:
While the behavior is the same across the apps, but the logo that I show at the welcome screen is different. Right now I populate it with an image resource from the library resources (R class) which will be the same for all apps and is obviously not correct.
The login/signup process is based on Firebase, which will require the app to have a key to be able to use them. Right now I also populate it with a dummy string resource from the library resources.
So my question really boils down to 3 parts:
Is there anyway I could pass this info from the app to the library? can I somehow modify the R class of the library? Or can I use the app's R class from the library? I can also call this part of the library as a function passing the parameters I need. But the first solution looks maybe more clean to me?
Whatever the answer to Q1 is. Where would I do this and how? The library has the welcome activity itself which is supposed to be the first activity in the app. How and where do I do this once the app starts and before the first activity starts?
If what I'm doing is wrong or impossible, is there any other way to achieve it?
Is there anyway I could pass this info from the app to the library?
can I somehow modify the R class of the library? Or can I use the
app's R class from the library? I can also call this part of the
library as a function passing the parameters I need. But the first
solution looks maybe more clean to me?
You don't need to modify the R class because you can override the resource file by creating a file with the same name. But it's not a clean solution because you constantly need to ensure your project and library resources name are the same.
Whatever the answer to Q1 is. Where would I do this and how? The
library has the welcome activity itself which is supposed to be the
first activity in the app. How and where do I do this once the app
starts and before the first activity starts?
Instead of overriding the resources name, you're better to modify your library to receive a configuration as a contract to use the library. Here the sample:
First, create the class for holding the configuration:
public class Configuration {
private int welcomeImageDrawableId;
private int logoDrawableId;
// constructor
public Configuration(int welcomeImageDrawableId, int logoDrawableId) {
this.welcomeImageDrawableId = welcomeImageDrawableId;
this.logoDrawableId = logoDrawableId;
}
// setter and getter.
public int getLogoDrawableId() {
return logoDrawableId;
}
}
Second, use the configuration class for the library by creating a Singleton class which will be used internally by the library:
public class MyLibrary {
private static MyLibrary myLibrary;
private Configuration configuration;
private MyLibrary(){}
private MyLibrary(Configuration configuration) {
this.configuration = configuration;
}
public static MyLibrary getInstance() {
if(myLibrary == null) {
throw new RuntimeException("Need call createInstanceWith method first!!");
}
return myLibrary;
}
public static MyLibrary createInstanceWith(Configuration configuration) {
if(myLibrary == null) {
synchronized(MyLibrary.class) {
if (myLibrary == null) {
myLibrary = new MyLibrary(configuration);
}
}
}
return test;
}
public Configuration getConfiguration() {
return configuration;
}
}
Third, use the configuration class in your library via the singleton class. something like this:
// assume imvLogo is an existing ImageView
Configuration configuration = MyLibrary.getInstance().getConfiguration();
imvLogo.setImageResource(configuration.getLogoDrawableId());
Last, register the contract when the library is used with:
Configuration configuration = new Configuration(R.drawable.welcome, R.drawable.logo);
MyLibrary.createInstanceWith(configuration);
Note: all the code isn't tested yet, error is to be expected.
Apart from the solution above, I also found another way to achieve this whole thing without having to initialize libraries and whatnot.
I think the correct way to do this is to use productFlavors in the library. This allows the library to share the one main set of source code, one main set of resources, then an extra set of resource per app/flavors. This is very sufficient for my purposes.
For more info about build variants and flavors:
https://developer.android.com/studio/build/build-variants
In Java, we have the package protected (default) modifier for classes, which allows us to have many classes in a single package but exposes only a few and keeps the logic encapsulated.
With Kotlin this doesn't seem to be the case. If I want a few classes to be visible to each other but no further, I have to use a private modifier which limits visibility to a single file.
So if you want 10 classes in a package but only one of them to be public, you'd have to have one huge file with all the classes in it (and private all over the place).
Is this normal practice or there is a way to achieve some similar modularity in Kotlin?
I don't understand: if they have the notion of a package, why did they get rid of package protected access?
Update: We might have package protected visibility after all
see the discussion here
Update: If you read through the discussion and still think this is a must-have feature for the language, please vote here
Kotlin, compared to Java, seems to rely on packages model to a lesser degree (e.g. directories structure is not bound to packages). Instead, Kotlin offers internal visibility, which is designed for modular project architecture. Using it, you can encapsulate a part of your code inside a separate module.
So, on top level declarations you can use
private to restrict visibility to the file
internal to restrict visibility to the module
At this point, there is no other option for visibility restriction.
As a workaround for me on android I've created #PackagePrivate annotation and lint checks to control access. Here you can find the project.
Lint checks are obviously not that strict as compiler checks and some setup needed to fail the build on errors. But android studio picks up lint checks automatically and shows error immediately while typing. Unfortunately I don't know a way to exclude annotated members from autocomplete.
Also, as lint is a purely compile-time tool, no checks at runtime performed.
As #hotkeys points out, you can use the internal keyword in a module or you can put all classes that would otherwise belong in a package inside a single file, but sticking several classes in a file may be a questionable design decision.
For me, the package visibility is helpful for its documenting value. I want to know what public interface some package is presenting to the rest of the project, hide factory implementation classes and so on.
So even if it's possible to access package-private classes and methods in Java, I still choose to use the package modifier.
For this I created a project with a single annotation:
package com.mycompany.libraries.kotlinannotations;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Documented
#Retention(SOURCE)
#Target({ TYPE, METHOD, CONSTRUCTOR })
/**
* Use in Kotlin code for documentation purposes.
*
* Whenever a Kotlin class or method is intended to be accesible at package level only.
*
*/
public #interface PackagePrivate {
}
Then I can use this annotation in any Kotlin project.
The second step, which I haven't done yet, is creating a PMD rule to enforce this with maven (or any other build tool for that matter) and also be able to see violations of the rule in my IDE with the pmd plugin.
There no is full Kotlin support in pmd at this moment but it seems to be expected at some point.
A near-replacement for package private visibility is available using the opt-in requirements feature (credit to pdvrieze on Kotlin discussions). This is the annotation syntax typically used to flag experimental features in an API.
To use it, create an annotation denoting package private declarations:
#RequiresOptIn(message = "Only to be used in MyPackage")
#Retention(AnnotationRetention.BINARY)
annotation class MyPackagePrivate
Then annotate any methods you want to be package private with it:
#MyPackagePrivate
fun aPackagePrivateMethod() {
// do something private within a package
}
In this way a warning will be generated on any method that calls the annotated method unless the calling method is itself annotated with the corresponding #OptIn annotation, here shown at class level:
#OptIn(MyPackagePrivate::class)
class AClassInThePackage {
fun userOfPackagePrivateMethod() {
aPackagePrivateMethod()
}
}
This, then, produces a similar effect to Java's package private, except that calling methods need to explicitly opt in to using a package private declaration.
If it is desired to generate an error rather than a warning, the level parameter of #RequiresOptIn can be specified:
#RequiresOptIn(level = RequiresOptIn.Level.ERROR, message = "Only to be used in MyPackage")
// annotation declaration as before
Package-based protection is pointless in Kotlin because packages themselves are unprotected
In Java, package was tied to directory structure. So if you put your classes in com\example\yoursecretengine, any attempt (deliberate or accidental) to add a rogue class there would be easily noticeable. This is the kind of security we've depended on.
Kotlin removes the ties between directory and package, so I can put my class in "my" directory (eg.src\java\pl\agent_l\illegalaccess) yet declare its package as com.example.yoursecretengine - and gain access to all the properties you've meant as package private.
In fact, a Kotlin project works perfectly without ANY package declarations. This only highlights that packages are "more what you'd call guidelines than actual rules". They're a convenience feature, useful only to unclutter namespace and nothing more.
Relevant quotes from kotlinlang:
unlike many other languages, Kotlin packages do not require files to have any specific locations w.r.t. itself; the connection between a file and its package is established only via a package header.
And:
an absence of a package header in a file means it belongs to the special root package.
I have a class name String and a String which containing the class code. For example, "Example" is the name of the class, and
public class Example {
public void example () {System.out.println ("Hello world!"); }
}
The class code.
I looked at the Dexmaker library, but I did not understand if it's possible to compile the generated code into it. And the question is just how to compile the code string under Android?
Not sure if possible at all the compilation within the embedded system but definitelly you can parse and run the code using beanshell:
http://www.beanshell.org/
it is lightweight and easily to embed in your app. Then you can instance the generated class and run whatever you put inside.
There is only one true way: using DexMaker. All examples you can find on DexMaker wiki and especially for current problem (runtime generation code on android).
I am working on DataBinding with BindingAdapter. Here is my custom method.
#BindingAdapter("{bind:fadevisible}")
public static void setFadeVisible(LinearLayout view, int visible) {
Log.e("Bindings", "setFadeVisible: ");
}
And in xml file i am calling it like
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:fadevisible="#{1}"/>
But it is showing error
Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
java.lang.RuntimeException: Found data binding errors.
****/ data binding error ****msg:Cannot find the setter for attribute 'app:fadevisible' with parameter type int on android.widget.LinearLayout.
file:\app\src\main\res-main\layout\activity_detail.xml
loc:236:31 - 236:54
****\ data binding error ****
I have checked this and this thread but somehow it is not helping me, as you can see i am passing int from xml and in BindingAdapter also i have mentioned LinearLayout with int value.
Even i have another method, where just parameters are different and its working fine
#BindingAdapter({"bind:image_round"})
public static void loadRoundImage(ImageView imageView, String url)
Make sure in app level gradle, you have apply plugin: 'kotlin-kapt'
Your #BindingAdapter definition looks a little bit odd to me
#BindingAdapter("{bind:fadevisible}")
This is not the same like
#BindingAdapter({"bind:fadevisible"})
or
#BindingAdapter("bind:fadevisible")
which should work perfectly fine.
I had this problem with binding to ImageView and unlike your case, the definition of my binding adapter was correct but still, the IDE kept giving me this error message. After spending many hours on searching for the cause, I figured that the namespace that I use in xml layout file needs to be exactly what I declared in #BindingAdapter.
So, if my xml is like below:
<ImageView
android:id="#+id/logo"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_alignParentRight="true"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
app:image_url="#{item.logoUrl}"
/>
Then my binding method should be as below:
#BindingAdapter({"app:image_url"})
public static void loadImage(ImageView view, String logoUrl) {
if (logoUrl == null) {
view.setImageResource(R.drawable.ic_place_holder);
} else {
Glide.with(getContext()).load(logoUrl).crossFade().into(view);
}
}
Note that binding method annotation indicates the namespace in it , i.e. #BindingAdapter({"app:image_url"}) exactly as it is used in layout file app:image_url="#{item.logoUrl}"
So unlike what is said in most tutorials, don't use #BindingAdapter({"bind:image_url"}) in your binding method and app:image_url="#{item.logoUrl}" in your xml file.
You try
#BindingAdapter("bind:fadevisible")
I had initially set defined my customBindidingAdapter as private:
#BindingAdapter("setPriorityColor")
private static void getPriorityColor(TextView textView, int priority) {
}
In my particular case, my BindingAdapter had two parameters, with requireAll, and I had neglected to put one of them on the element in my layout XML. So, like this: (Kotlin, I know)
#BindingAdapter("app:arg1", "app:arg2", requireAll = true)
fun MyAdapter(view: ImageView, x: String, y: Int) {
// ...
}
<Element app:arg1="#{"foo"}"/>
The error was roughly Cannot find the setter for attribute "app:arg1" with parameter String which is perfectly true, there is no such adapter; there's only one for two args.
One hint that this was happening was that Android Studio indicated that MyAdapter was an unused function by coloring it grey.
Obviously a more eloquent error message like "there is no adapter for app:arg1 of type String but there is one for..." (when one of the attribute names matches) would be appreciated, but I won't hold my breath.
Add on to the answers if you are working on multiple modules then where you have
#BindingAdapter("fadevisible")
That module should have the following code in the module -> build.gradle.
dataBinding {
enabled = true
}
Enjoy Happy coding. :)
Apart from #BindingAdapter improvements
(mine were working fine in one build and not in another),
upgrading the Build gradle version to the latest one worked for me.
I have created a Library Project which I import into another project.
In that Library Project at some point I retrieve it's android:versionName
To do that you need to supply the package name.
The problem arises when that code is executed when the Library Project is included within another project, then it seems that that code throws an exception :
10-04 10:15:36.987: WARN/System.err(1407): getSoftwareVersion(), Caught Exception : android.content.pm.PackageManager$NameNotFoundException: mobilaria.android.LandenPlayerCodeBase.baseplayer
Thats the package name of the package of the Project Library... it seems it cannot find it even though the same code that is executing that call is part of the Library itself...
Does anyone have experienced something like this or has an idea on how to solve this ?
As far as I know android library project manifest is ignored at the moment, manifest is not merged into end application when you reference a library. Hence you cant extract any data from the library's manifest.
I just tried something similar.
I tried to add a method getLibraryVersion() to my custom Application class. So I would be able to call
MyLibrary.getLibraryVersion()
from within the code that included that library. But it seems that you can not access the String resources via getText() or getString() like this:
public class MyLibrary extends Application {
#Override
public void onCreate() {
super.onCreate();
// provide an instance for our static accessors
MyLibrary.instance = this;
}
private static void checkInstance() {
if (MyLibrary.instance == null) {
throw new IllegalStateException("Application not created yet!");
}
}
/**
* #return the library name
*/
public String getLibraryName() {
MyLibrary.checkInstance();
return MyLibrary.instance.getString(R.string.app_project_name).toString();
}
...
}
Because the onCreate() method seems not to be called, the instance is always null!
As this way was not working out, and as you saw you cannot access the version the way you tried, I just hard coded the version and the library name, into my custom application class like this:
public class MyLibrary extends Application {
/**
* #return the library name
*/
public String getLibraryName() {
return "org.yourCompany.android.lib.YourLibName";
}
/**
* #return the library version
*/
public String getLibraryVersion() {
return "1.0.0";
}
}
I know that this is kind of a dirty solution, and I would prefer a cleaner version of coding, with these Strings stored as String resources in strings.xml but I don't know any better way. So you just have to change the library name and version in your manifest or better the strings.xml AND in the Application class.
But how often do you change the library name or version?
Hope this can help somebody and save time!
PS: some of the above code is based on this:
http://blog.tomgibara.com/post/126377651/global-application-state-in-android