I'm trying to pull out all the TextViews that have been assigned id's in an Activity in order to populate them with dynamic values. In order to do so, I'm using an XMLResourceParser to go through the tags and get the id's. Here's the code:
public int[] getElementIds(int layoutId, String viewType)
throws XmlPullParserException, IOException{
XmlResourceParser parser = activity.getResources().getLayout(layoutId);
LinkedList<Integer> idList = new LinkedList<Integer>();
while(parser.getEventType()!=XmlResourceParser.END_DOCUMENT){
parser.next();
if(parser.getEventType()==XmlResourceParser.START_TAG){
if(parser.getName().equals(viewType)){
idList.add(parser.getIdAttributeResourceValue(0)); //here's the problem
}
}
}
// returns an int[] from values collected
}
The line with the comment just gives me back zeroes, the default value I specified. The following code, however, worked, the attribute index worked out through trial and error:
idList.add(parser.getAttributeResourceValue(0, 1)); // the zero here is 'id' attribute index
Any ideas?
After additional research, it seems that I've found a bug in the API. The code that was available online looks like this:
public int getIdAttributeResourceValue(int defaultValue) {
return getAttributeResourceValue(null, "id", defaultValue);
}
public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
if (idx >= 0) {
return getAttributeResourceValue(idx, defaultValue);
}
return defaultValue;
}
public int getAttributeResourceValue(int idx, int defaultValue) {
int t = nativeGetAttributeDataType(mParseState, idx);
// Note: don't attempt to convert any other types, because
// we want to count on appt doing the conversion for us.
if (t == TypedValue.TYPE_REFERENCE) {
return nativeGetAttributeData(mParseState, idx);
}
return defaultValue;
}
Wherein the last function is the one actually doing the work. There is no documentation for this class (XMLBlock), and I have no access to the native functions, which are actually written in C. What I do know is that the offending function here is the second one, with namespace and attribute name being the parameters. For attribute name 'style' it works fine, but for 'id' (which is both the name provided by the API in another place, and the value returned by a different function that returns attribute name, provided a given index), it doesn't come up with anything, and consistently spits out the default value. Furthermore, I can access those same id values by using the function whose parameters are attribute index rather than name (the last function copied above). Conclusion: something is messed up with the way the 'native' code processes the name 'id'. I'm sending a bug report to the open source project, and I'll post any response I get.
As a workaround I've implemented this function in my own code:
private int getIdAttributeResourceValue(XmlResourceParser parser) {
final int DEFAULT_RETURN_VALUE = 0;
for (int i = 0; i < parser.getAttributeCount(); i++) {
String attrName = parser.getAttributeName(i);
if ("id".equalsIgnoreCase(attrName)) {
return parser.getAttributeResourceValue(i, DEFAULT_RETURN_VALUE);
}
}
return DEFAULT_RETURN_VALUE;
}
Related
Can anyone tell me why these is not working? If these work for me it will answer all my questions, please help me:
for (int i = 0; i < records.size(); i++) {
JSONObject jsonObject = new JSONObject();
if (Scanner.listOfBarcodes.contains(records.get(i).getsid())) {
jsonObject.put("attendance","Present");
} else {
jsonObject.put("attendance","Absent");
}
listOfBarcodes is an ArrayList which contains a bunch of scanned barcode values which are student id numbers like 3924,3922...
But it is always putting Absent in the JSONObject. Why is that happening? Please help me.
This is my serializable java class:
public class Student {
private String sid;
public void setsid(String sid){
this.sid=sid;
}
public String getsid(){
return sid;
}
}
I might be able to help better with a bit more of your code to be sure of the answer, but I believe the problem is the function contains() expects to gets as a parameter an object of the same type of the ArrayList it is called from.
You need to debug it and check the that return value of records.get(i).getsid() has the same type as the ArrayList listOfBarcodes.
Maybe try to assign it to a temp value as follow and check what it returns and compare to this temp item, the problem could be in saving the values not in retrieving it (Your call could be returning null) as follows:
String tempSid = records.get(i).getsid();
if (Scanner.listOfBarcodes.contains(tempSid)) {
jsonObject.put("attendance","Present");
}
In order to reduce the number of API calls, I'm trying to query place details by passing several place_ids at a time (up to 10). I haven't found any useful information beyond the docs.
https://developers.google.com/android/reference/com/google/android/gms/location/places/GeoDataApi.html#getPlaceById(com.google.android.gms.common.api.GoogleApiClient, java.lang.String...)
Notably, the constructor is:
public abstract PendingResult<PlaceBuffer> getPlaceById (GoogleApiClient client, **String... placeIds**)
and the doc says: Returns Place objects for each of the given place IDs.
I don't have any problem when passing a single place_id, but when I pass a comma delimted string of id's, all of which are known to be good, I get a status = "SUCCESS" but a buffer count of 0.
Does anyone know the correct way to pass multiple id's to getPlaceById()?
Here's my code if that helps at all:
Places.GeoDataApi.getPlaceById(mGoogleApiClient, searchIds)
.setResultCallback(new ResultCallback<PlaceBuffer>() {
#Override
public void onResult(PlaceBuffer places) {
int cnt = places.getCount();
if (places.getStatus().isSuccess() && places.getCount() > 0) {
for (int i=0; i < places.getCount(); i++) {
final Place myPlace = places.get(i);
Log.d("<< cache >> ", "Place found: " + myPlace.getName());
}
} else {
Log.d("<< cache >> ", "Place not found");
}
places.release();
}
});
It's a varargs argument. You call it like this:
Places.GeoDataApi.getPlaceById(mGoogleApiClient,
"placeId1", "placeId2", "placeId3");
More detail in this SO question: How does the Java array argument declaration syntax "..." work?
Although I've read that String... can be passed as either a comma delimited string or a string array, for one reason or other, getPlaceById appears to require an array. When I use this code to prepare the place id parameter, it works fine:
String search[] = new String[idsToSearch.size()];
search = idsToSearch.toArray(search);
I'm building an Android app with the Realm database.
I have a RealmObject subclass called Article which has an id field (it's and int and also a #PrimaryKey). I would like to pass to a query a list of ints (a Set, int[], or whatever) of article id's and retrieve only those articles.
In SQL would be like this:
SELECT *
FROM `table`
where ID in (5263, 5625, 5628, 5621)
I've seen it's possible to do this in iOS in this StackOverflow question.
How can I do this in Android? Thanks!
Edit: Just to inform, I also asked this on the GitHub repo here.
Update:
Realm 1.2.0 has added RealmQuery.in() for a comparison against multiple values. The documentation details all the available overloads. This one is the method we can use if our ids are Integers:
public RealmQuery<E> in(String fieldName, Integer[] values)
Original answer:
The answer from #ChristianMelchior returns all articles if the list of ids is empty. I want it to return an empty RealmResults<Article>. That's what I've ended up doing:
Set<Integer> articleIds = this.getArticleIds();
RealmQuery<Article> query = realm.where(Article.class);
if (articleIds.size() == 0) {
// We want to return an empty list if the list of ids is empty.
// Just use alwaysFalse
query = query.alwaysFalse();
} else {
int i = 0;
for (int id : articleIds) {
// The or() operator requires left hand and right hand elements.
// If articleIds had only one element then it would crash with
// "Missing right-hand side of OR"
if (i++ > 0) {
query = query.or();
}
query = query.equalTo("id", id);
}
}
return query.findAll();
Now realm v 1.2.0 support RealmQuery.in() for a comparison against multiple values.
The Realm Java API's doesn't support this yet unfortunately. You can follow the feature request here https://github.com/realm/realm-java/issues/841
The current work-around would be to build up the query yourself in a for-loop:
RealmResults<Article> articles = realm.allObjects(Article.class);
RealmQuery q = articles.where();
for (int id : ids) {
q = q.equalTo("id", id);
}
RealmResults<Article> filteredArticles = q.findAll();
This is the way Realm does it since 1.2.0:
public RealmQuery<E> in(String fieldName, String[] values) {
if (values == null || values.length == 0) {
throw new IllegalArgumentException(EMPTY_VALUES);
}
beginGroup().equalTo(fieldName, values[0]);
for (int i = 1; i < values.length; i++) {
or().equalTo(fieldName, values[i]);
}
return endGroup();
}
Previously this is how I did it
I just came across this post and I thought I could throw in my 2 cents on this. As much as I appreciate Christian Melchior and his answers I think in this case his answer is not working (at least in the current version).
I prefer to do it like this - I personally think it's more readable than Albert Vila's answer:
List<String> listOfIds = [..];
RealmQuery<SomeClass> query = realm.where(SomeClass.class);
boolean first = true;
for (String id : listOfIds) {
if (!first) {
query.or();
} else {
first = false;
}
query.equalTo("id", id);
}
RealmResults<SomeClass> results = query.findAll();
I'm using the below code to find a resource by id;
setContentView(R.layout.golders);
for (int i=1; i<hm.size()+1;i++)
{
int id = getStringIdentifier("Bus"+i);
view = (TextView)findViewById(id);
view.setText(hm.get(i).toString());
}
My getStringIdentifier() is working but when I try to set the text I'm getting a NullPointerException.
I've used the setContentView to focus on the golders.xml file which has the ids that I want to update.
I've tried Cleaning the project but that hasn't done anything either, any ideas?! Thanks!
EDIT:
public int getStringIdentifier(String aString)
{
String packageName = "com.example.bustimetable.Robbos";
int resId = getResources().getIdentifier(aString, "string", packageName);
return resId;
}
Your getStringIdentifier(String) method returns a string ID (something from R.string). You need a new method, something like getIdentifier(String), that will return soemthing from R.id. I can't see the XML, so I don't know what your TextView's ID is, but... you'll want to verify that the ID is, in fact, Bus_ where the _ is some number.
public int getIdentifier(String aString)
{
String packageName = "com.example.bustimetable.Robbos";
int resId = getResources().getIdentifier(aString, "id", packageName); // Get from R.id, not R.string
return resId;
}
The problem is that getStringIdentifier() is returning an id for a string resource. Apparently the id which is returned is not a valid id for a view resource. Even if was, I don't know how you can guarantee that you will get the view that you want. You need to either add another method to return id's for view resources, or modify the one you have so that it will return id's for either string or view resources
I would like to see if I can avoid a lengthy switch or if block by directly converting some strings into an object name. For example, I have a class called Example and I want to [edit] have up to 10 instances of the class Example1, Example2, so on. Can I use something like:
int ExampleNum = 2;
// can be changed to any 1-10 value corresponding to instances
String s = "Example" + String.valueOf(ExampleNum);
Refresh(s);
public void Refresh(Example example){
...
}
Thus I would create a string with the value of Example2 and pass that to my Refresh method.
[edit]
I don't want to use all the instances at once, but rather have other methods that change the int ExampleNum so that when I try to refresh it refreshes the appropriate Example instance.
Rather than saying:
if (ExampleNum == 2)
Refresh(Example2);
I would use the ExampleNum and String to use the right instance name;
Why not use array's instead??
Example[] e = null;
for(int i=1;i<=10;i++)
{
e[i] = new Example();
Refresh(e[i]);
}
Well, your code, as it stands now, doesn't make any sense since you're passing a String to Refresh, which takes an Example object as an argument.
However, if you're asking how you can create the strings Example1, Example2, ... Example 10, you can do this:
for (int i = 1; i <= 10; i++) {
s = "Example" + i;
refresh(s); // assuming this takes a string
}