How to apply #IntRange() support annotation to Kotlin property setter - android

I have been trying to find out how to apply #IntRange(from = 1) to my Kotlin property. After several failed attempts I finally just created the class I wanted in Java and converted it to Kotlin inside Android Studio. Here is my Java class:
import android.support.annotation.IntRange;
public class SimpleThing {
private int val;
#IntRange(from = 1)
public int getVal() {
return val;
}
public void setVal(#IntRange(from = 1) int val) {
this.val = val;
}
}
and this is the automatic conversion I got from Android Studio:
import android.support.annotation.IntRange
class SimpleThing {
#get:IntRange(from = 1)
var `val`: Int = 0
}
The #IntRange appears to get applied to the getter but not to the setter. Is it possible to apply this annotation to the setter also so that the appropriate lint warning will appear. Currently I have just overridden the set method to throw an IllegalArgumentException like this:
#get:IntRange(from = 1)
var rowSize: Int = 3
set(value) {
if (value < 1) throw IllegalArgumentException("row size must be at least 1")
field = value
notifyDataSetChanged()
}
I have already tried adding #set:IntRange(from = 1) but I get the error This annotation does not apply for type void because it is trying to apply #IntRange to the return value (which is void in the case of the setter) as opposed to the setter argument.

The #setparam annotation appears to be what I'm looking for, but no lint warning is raised in Android Studio when I try to assign a value outside the range.
Here is my new code:
#get:IntRange(from = 1)
#setparam:IntRange(from = 1)
var rowSize: Int = 3
set(value) {
if (value < 1) throw IllegalArgumentException("row size must be at least 1")
field = value
notifyDataSetChanged()
}
and here is where I would normally expect to see a lint warning telling me I am assigning a value outside the int range
However the lint warning does appear when using the SimpleThing class in Kotlin code
The lint warning also does not appear when calling the Kotlin method as Java code
It would appear that this functionality is just not implemented yet.

Not in the position to test this currently, but have you tried adding a #set like so:
#set:IntRange(from = 1)
#get:IntRange(from = 1)
var `val`: Int = 0

Related

I don't understand why "return" doesn't return a value [duplicate]

This question already has answers here:
Kotlin: Variable 'result' must be initialized
(3 answers)
Kotlin variable usage "variable must be initialize"
(1 answer)
Closed last month.
I'm new to kotlin and I don't know English well so "Google Translate hello!".
I want to return the values that I received during the execution of the if condition. This function iterates through the columns in the excel table until it finds the one I need and returns its number
I tried to write return#getTableValue to indicate where to return the value, but it didn't give anything, I don't understand, help
My code:
private fun getTableValue(xlWs: Sheet, groupe: String, dopgroupe: String): Int {
var gr: String
var cellNumb: Int
var res: Int
for (i in 0..20){
gr = xlWs.getRow(0).getCell(i).toString()
if (gr == groupe){
cellNumb = i
if (dopgroupe == "1") {
res = cellNumb
}
if (dopgroupe == "2") {
res = cellNumb + 2
}
}
}
return res // Error Variable 'res' must be initialized
}
You only set res inside if statements, so the compiler cannot be sure that you ever set a value to res. You can only use a variable’s value after it is guaranteed to have been set to something. In this case, the easiest solution is to give the variable an initial value at the declaration site, like this:
var res: Int = 0

Insert int type variable from EditText into SQL Database in Android

I got an EditText and I want to insert an int type variable into my database. If I insert a number, everything's ok. But if I left the EditText empty, the app crashes. This is the code that handles this and I think it's an Integer.toString() problem or something like this.
int target;
if (targetNumber.getText().toString().isEmpty()) {
target = Integer.parseInt("");
} else {
target = Integer.parseInt(targetNumber.getText().toString());
}
You can't parse "" to int. You can store 0 for example
int target;
if (targetNumber.getText().toString().isEmpty()) {
target = 0;
} else {
target = Integer.parseInt(targetNumber.getText().toString());
}
Replace this
target = Integer.parseInt("");
with
target = 0;
If you'd like to make use of a ternary operator:
int target = targetNumber.getText().toString().isEmpty() ? 0 : Integer.parseInt(targetNumber.getText().toString())

Resources.GetIdentifier() returning 0 for layout (Android)

I'm attempting to get the int resource id for a layout resource by name, using Resources.GetIdentifier() of the Android API, but it returns 0. I'm using c#/monodroid/Xamarin, but regular java Android knowledge would apply too I suspect. Here's my code:
int resId = Resources.GetIdentifier(typeName, "layout", _activity.PackageName);
Where typeName = "FrmMain", and in my project I have the file "Resources/Layout/FrmMain.axml". Any ideas?
This is old, but for everyone getting this problem, I think it is because the resource name should be in lower case, so:
int resId = Resources.GetIdentifier("FrmMain", "layout", _activity.PackageName);
does not work, but:
int resId = Resources.GetIdentifier("frmmain", nameof(Resource.Layout).ToLower(), _activity.PackageName);
should work
I don't know why that's failing, but wouldn't something like Resource.Layout.FrmMain achieve what you're after?
edit:
According to this answer, you can (and should) use reflection to achieve what you're after, so I think you would try something like this:
var resourceId = (int)typeof(Resource.Layout).GetField(typeName).GetValue(null);
which does seem to work on my app and should get what you're after.
In my case, this issue came up when I had to upgrade the target SDK due to google's new policy since November, 2018.
I had to display some strings according to the server response code (ex : api_res_001_suc), but it did not work on the upgraded version.
The overall version, about 22 as I recall, had to be changed to 27.
The cause of the issue seems to be the default translation stuff. When I put all the default translation for every string, it worked.
My code is,
getResources().getIdentifier(resName, "string", "packageName");
I've created a ResourceHelper class to handle this situation. Here is the code:
public static class ResourceHelper
{
public static int FindId(string resourceId)
{
var type = typeof(Resource.Id);
var field = type.GetField(resourceId);
return (int)field.GetRawConstantValue();
}
public static int FindLayout(string layoutName)
{
var type = typeof(Resource.Layout);
var field = type.GetField(layoutName);
return (int)field.GetRawConstantValue();
}
public static int FindMenu(string menuName)
{
var type = typeof(Resource.Menu);
var field = type.GetField(menuName);
return (int)field.GetRawConstantValue();
}
}
Actually I'm improving it because I need to use it from another Assembly and it's restricted to work in the same Assembly of the Droid App. I'm thinking about put a generic method (or an Extension one) to do this. Here is a draft of my idea:
public static int FindResource<T>(string resourceName)
{
var type = typeof(T);
var field = type.GetField(resourceName);
return (int)field.GetRawConstantValue();
}
Hope it can help you.

Unity3d BootCamp Tutorial UnityScript Array Order Anomaly in ImageEffectsOrder.js

I am new to Unity and grabbed the BootCamp project and ran it within Unity 4.1.5f1 as a Windows Build without any modification
I then tried to build to Android and had a bunch of errors (mostly variables not being declared)
But I have one remaining that I just don't understand...
In the following code in the file ImageEffectsOrder.js the javascript references an order method of the array sorted[] as sorted[i].order
The compiler errors with 'order' is not a member of object.
So I'm a little confused as to why the windows build supports this member but not android.
This makes me wonder what other surprises await when converting from platform to platform.
But for now can anyone point me to a workaround for the order member? And I'm not quite clear on what it is actually returning...it seems the variable i should give you the order.
The order just seems intrinsic from the code, it is never set to any value, so what 'order' is it? I can't seem to find any docs on this 'member' of the Array class.
Here is the code:
var sorted : Array = new Array();
var i : int = 0;
for (var fx : PostEffectsBase in GetComponents(PostEffectsBase))
{
if(fx && fx.enabled)
{
sorted[i++] = fx;
}
}
while (sorted.length)
{
var indexToUse : int = 0;
var orderValue : int = -1;
for(i = 0; i < sorted.length; i++) {
if(sorted[i].order > orderValue) {
orderValue = sorted[i].order;
indexToUse = i;
}
}
...more code...
I solved it. The problem is not with the Array Class as the fx that is being assigned to the sorted[] array is an object of class PostEffectsBase.
So the actual problem is one of casting when we try to use sorted[i].order
I changed the reference from sorted[i].order to (sorted[i] as PostEffectsBase).order and it worked.
I have to remember this. It seems there are a lot of these casts that have to be done between platforms.

Android: View.setID(int id) programmatically - how to avoid ID conflicts?

I'm adding TextViews programmatically in a for-loop and add them to an ArrayList.
How do I use TextView.setId(int id)? What Integer ID do I come up with so it doesn't conflict with other IDs?
From API level 17 and above, you can call: View.generateViewId()
Then use View.setId(int).
If your app is targeted lower than API level 17, use ViewCompat.generateViewId()
You can define the ID's you'll use later in R.id class using an xml resource file, and let Android SDK set the actual unique values during compile time.
res/values/ids.xml
<item name="my_edit_text_1" type="id"/>
<item name="my_button_1" type="id"/>
<item name="my_time_picker_1" type="id"/>
To use it in the code:
myEditTextView.setId(R.id.my_edit_text_1);
According to View documentation
The identifier does not have to be unique in this view's hierarchy. The identifier should be a positive number.
So you can use any positive integer you like, but in this case there can be some views with equivalent id's. If you want to search for some view in hierarchy calling to setTag with some key objects may be handy.
Also you can define ids.xml in res/values. You can see an exact example in android's sample code.
samples/ApiDemos/src/com/example/android/apis/RadioGroup1.java
samples/ApiDemp/res/values/ids.xml
Since API 17, the View class has a static method generateViewId() that will
generate a value suitable for use in setId(int)
This works for me:
static int id = 1;
// Returns a valid id that isn't in use
public int findId(){
View v = findViewById(id);
while (v != null){
v = findViewById(++id);
}
return id++;
}
(This was a comment to dilettante's answer but it got too long...hehe)
Of course a static is not needed here. You could use SharedPreferences to save, instead of static. Either way, the reason is to save the current progress so that its not too slow for complicated layouts. Because, in fact, after its used once, it will be rather fast later. However, I dont feel this is a good way to do it because if you have to rebuild your screen again (say onCreate gets called again), then you probably want to start over from the beginning anyhow, eliminating the need for static. Therefore, just make it an instance variable instead of static.
Here is a smaller version that runs a bit faster and might be easier to read:
int fID = 0;
public int findUnusedId() {
while( findViewById(++fID) != null );
return fID;
}
This above function should be sufficient. Because, as far as I can tell, android-generated IDs are in the billions, so this will probably return 1 the first time and always be quite fast.
Because, it wont actually be looping past the used IDs to find an unused one. However, the loop is there should it actually find a used ID.
However, if you still want the progress saved between subsequent recreations of your app, and want to avoid using static. Here is the SharedPreferences version:
SharedPreferences sp = getSharedPreferences("your_pref_name", MODE_PRIVATE);
public int findUnusedId() {
int fID = sp.getInt("find_unused_id", 0);
while( findViewById(++fID) != null );
SharedPreferences.Editor spe = sp.edit();
spe.putInt("find_unused_id", fID);
spe.commit();
return fID;
}
This answer to a similar question should tell you everything you need to know about IDs with android: https://stackoverflow.com/a/13241629/693927
EDIT/FIX: Just realized I totally goofed up the save. I must have been drunk.
The 'Compat' library now also supports the generateViewId() method for API levels prior 17.
Just make sure to use a version of the Compat library that is 27.1.0+
For example, in your build.gradle file, put :
implementation 'com.android.support:appcompat-v7:27.1.1
Then you can simply use the generateViewId() from the ViewCompat class instead of the View class as follow:
//Will assign a unique ID
myView.id = ViewCompat.generateViewId()
Happy coding !
Just an addition to the answer of #phantomlimb,
while View.generateViewId() require API Level >= 17,
this tool is compatibe with all API.
according to current API Level,
it decide weather using system API or not.
so you can use ViewIdGenerator.generateViewId() and View.generateViewId() in the
same time and don't worry about getting same id
import java.util.concurrent.atomic.AtomicInteger;
import android.annotation.SuppressLint;
import android.os.Build;
import android.view.View;
/**
* {#link View#generateViewId()}要求API Level >= 17,而本工具类可兼容所有API Level
* <p>
* 自动判断当前API Level,并优先调用{#link View#generateViewId()},即使本工具类与{#link View#generateViewId()}
* 混用,也能保证生成的Id唯一
* <p>
* =============
* <p>
* while {#link View#generateViewId()} require API Level >= 17, this tool is compatibe with all API.
* <p>
* according to current API Level, it decide weather using system API or not.<br>
* so you can use {#link ViewIdGenerator#generateViewId()} and {#link View#generateViewId()} in the
* same time and don't worry about getting same id
*
* #author fantouchx#gmail.com
*/
public class ViewIdGenerator {
private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
#SuppressLint("NewApi")
public static int generateViewId() {
if (Build.VERSION.SDK_INT < 17) {
for (;;) {
final int result = sNextGeneratedId.get();
// aapt-generated IDs have the high byte nonzero; clamp to the range under that.
int newValue = result + 1;
if (newValue > 0x00FFFFFF)
newValue = 1; // Roll over to 1, not 0.
if (sNextGeneratedId.compareAndSet(result, newValue)) {
return result;
}
}
} else {
return View.generateViewId();
}
}
}
In order to dynamically generate View Id form API 17 use
generateViewId()
Which will generate a value suitable for use in setId(int). This value will not collide with ID values generated at build time by aapt for R.id.
int fID;
do {
fID = Tools.generateViewId();
} while (findViewById(fID) != null);
view.setId(fID);
...
public class Tools {
private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
public static int generateViewId() {
if (Build.VERSION.SDK_INT < 17) {
for (;;) {
final int result = sNextGeneratedId.get();
int newValue = result + 1;
if (newValue > 0x00FFFFFF)
newValue = 1; // Roll over to 1, not 0.
if (sNextGeneratedId.compareAndSet(result, newValue)) {
return result;
}
}
} else {
return View.generateViewId();
}
}
}
inspired by #dilettante answer, here's my solution as an extension function in kotlin:
/* sets a valid id that isn't in use */
fun View.findAndSetFirstValidId() {
var i: Int
do {
i = Random.nextInt()
} while (findViewById<View>(i) != null)
id = i
}
public String TAG() {
return this.getClass().getSimpleName();
}
private AtomicInteger lastFldId = null;
public int generateViewId(){
if(lastFldId == null) {
int maxFld = 0;
String fldName = "";
Field[] flds = R.id.class.getDeclaredFields();
R.id inst = new R.id();
for (int i = 0; i < flds.length; i++) {
Field fld = flds[i];
try {
int value = fld.getInt(inst);
if (value > maxFld) {
maxFld = value;
fldName = fld.getName();
}
} catch (IllegalAccessException e) {
Log.e(TAG(), "error getting value for \'"+ fld.getName() + "\' " + e.toString());
}
}
Log.d(TAG(), "maxId="+maxFld +" name="+fldName);
lastFldId = new AtomicInteger(maxFld);
}
return lastFldId.addAndGet(1);
}
My Choice:
// Method that could us an unique id
int getUniqueId(){
return (int)
SystemClock.currentThreadTimeMillis();
}

Categories

Resources