Why Textview is not updating data everytime? - android

I'm trying to get text from db then update text in my textView of recyclerView adapter class . Although I'm getting text from db without any problem but my textview is not updating text everytime . Sometimes it's updating and sometimes it's not I wonder what's the problem . I tried to refer a lot of SO questions but none of them could solve my issue . I even tried to use textView.invalidate() but it was of no use . Could anyone please help me to solve this issue.
Code:
#Override
public void onBindViewHolder( CustomRecyclerViewHolder holder, int position) {
JSONObject ov=new JSONObject();
try {
ov.put("prof_email",timelineData.getEmail());
socket.disconnect();
socket.connect();
socket.emit("data",ov);
socket.on("name_full", new Emitter.Listener() {
#Override
public void call(Object... args) {
final JSONObject ob = (JSONObject)args[0];
try {
final String st = ob.getString("fullname");
Needle.onMainThread().execute(new Runnable() {
#Override
public void run() {
if (st.length() > 0) {
//I'm toasting just to test that text is alright or not but it's alright
Toast.makeText(context,st,Toast.LENGTH_SHORT).show();
holder.userName.invalidate();//here not useful
holder.userName.setText(st);
holder.userName.invalidate();//even here also not useful
socket.disconnect();
}
}
});
}catch (Exception e){
e.printStackTrace();
}
}
});
} catch (JSONException e) {
e.printStackTrace();
}
}

OnBindViewHolder will be called every time when user scroll the recyclerview.
So corresponding to your source code, socket connection and disconnection is done many times when user scroll the list. (This seems not a good way)
So I recommend to remove socket related code from onBindViewHolder.
After that just listening to the socket. When data changed, replace old data with new one from your collection and call Adapter#notifyDatasetChanged().

Related

android studio: java.lang.RuntimeException: An error occured while executing doInBackground()

i'm trying to make an app by using calimero library for KNX. In my app, i use some buttons, switches, togglebuttons, etc to switch on/off the lights.
With 'Button', everything works well. I can switch on/off a light or open/close curtains.
private class button9OnClickListener implements View.OnClickListener {
public void onClick(View view){
try {
falseTask dt = new falseTask();
String adr = "5/1/0";
dt.execute(adr);
} catch (Exception e) {
}
}
}
But when i use 'Switch' with either OnClickListener or OnCheckedChangeListenser, it crash.
private class switch1OnCheckedChangeListener implements CompoundButton.OnCheckedChangeListener {
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
if(compoundButton.isChecked()) {
try {
trueTask dt = new trueTask();
String adr = "5/4/6";
dt.execute(adr);
} catch (Exception e) {
e.printStackTrace();
}
} else {
try {
falseTask dt = new falseTask();
String adr = "5/4/6";
dt.execute(adr);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
the problem is below:
uncaughtException java.lang.RuntimeException: An error occured while executing doInBackground()
and here's my doInBackground
protected String doInBackground(String...strings){
String adr = strings[0];
try {
final InetSocketAddress localEP = new InetSocketAddress(InetAddress.getByName(hostip), 0);
final InetSocketAddress remoteEP = new InetSocketAddress(remoteip, knxServerPort);
knxLink = new KNXNetworkLinkIP(KNXNetworkLinkIP.TUNNEL, localEP, remoteEP, true, TPSettings.TP1);
pc = new ProcessCommunicatorImpl(knxLink);
pc.write(new GroupAddress(adr), true);
knxLink.close();
}
catch (final KNXException e) {
}
catch (final UnknownHostException e) {
}
return null;
}
i'm new to java and android programming. Can you help with this problem? i can't understand why this work for 'button' but not for 'switch'.
Thank you in advance
UPDATE
I copy my code and try with eclipse. Everything work fine. I can switch on/off the light i want and there is no error.
With android studio, it did not work. Someone know the reason?
I tried with ToggleButton and Switch in Eclipse. All work good. I put exactly what I used in android studio. I copy all the code without any change. Can't understand the problem.
Finally I find the problem. I use 2 Bundle in the MainActivity and I use these 2 Bundle in doInBackground for my AsyncTask in the SecondActivity. But I just pass on the first Bundle to the SecondActivity and I forget to pass on the second Bundle. So I got error while executing doInBackground() What foolish mistake I have made...

AutocompleteTextView is not updated dynamically

I am getting data (List) from an API and I am trying to update my AutcompleteTextView with this data.
This is how I currently do :
I have a TextWatcher which calls a the method to get the data in afterTextChanged, so every time the user stops typing the method is called, and the adapter is notified with ``notifyDataSetChanged :
//in onCreate
addressAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_dropdown_item_1line,suggestions_address);
at_address.setAdapter(addressAdapter);
...
#Override
public void afterTextChanged(Editable s) {
autoComplete(s);
addressAdapter.notifyDataSetChanged();
//suggestions_address is the updated list, and when I print it I can see the
//results so it is not empty
Log.i("addresses",suggestions_address.toString());
}
...
class SuggestionQueryListener implements ResultListener<List<String>> {
#Override
public void onCompleted(List<String> data, ErrorCode error) {
if (error != ErrorCode.NONE) {
Toast.makeText(MainActivity2.this,error.toString(),Toast.LENGTH_LONG).show();
} else {
suggestions_address.clear();
for(int i = 0;i<data.size();i++){
suggestions_address.add(data.get(i));
}
}
}
}
public void autoComplete(CharSequence s) {
try {
String term = s.toString();
TextSuggestionRequest request = null;
request = new TextSuggestionRequest(term).setSearchCenter(new GeoCoordinate(48.844900, 2.395658));
request.execute(new SuggestionQueryListener());
if (request.execute(new SuggestionQueryListener()) != ErrorCode.NONE) {
//Handle request error
//...
}
} catch (IllegalArgumentException ex) {
//
}
}
But it seems that the adapter is not really updated because it doesn't show the suggestions when I type something.
Also, before doing this with an AutoCompleteTextView I did it with a listView, with the same process, and everything worked well.
Any ideas or solutions would be really appreciated
EDIT : I noticed something really strange : the data is not binded to the adapter, because adapter#getCount always returns 0, even if the list is not empty. But when I remove at_address.setAdapter(addressAdapter), the data adapter is updated and adapter#getCount returns the right number of elements.
I am really confused right now, please help !
Instead of this:
for(int i = 0;i<data.size();i++){
suggestions_address.add(data.get(i));
}
you can use just this:
suggestions_address.addAll(data);
you are calling notifyDataSetChanged after you start the request, you should call it after you get the result and update the suggestions_address, so call notifyDataSetChanged inside onCompleted

RecyclerView element update + async network call

I have a recyclerview which works as expected. I have a button in the layout that fills the list. The button is supposed to make a async call, and on result, I change the button's look. All this happens fine.
But when I click on the button and scroll down the list fast, the async call's result updates the new view's button(the view that is in place of the old one). How do I handle this? Can I have a handle on when a particular view gets reused?
Update :
Code piece of the adapter class that does the async call and the updation of ui.
#Override
public void onBindViewHolder(CommentsViewHolder holder, int position) {
try {
Comments comment = comments.get(position);
holder.bindView(comment,position);
}
catch(Exception ex){ex.printStackTrace();}
}
#Override
public int getItemCount() {
if(comments==null)
{return 0;}
return comments.size();
//return comments.length();
}
public class CommentsViewHolder extends RecyclerView.ViewHolder {
TextView score ;
TextView commentText;
TextView commentTime;
TextView avatarId;
ImageButton minusOne;
ImageButton plusOne;
ParseObject model;
public CommentsViewHolder(View itemView) {
super(itemView);
//itemView.setBackgroundColor(Color.DKGRAY);
minusOne =(ImageButton)itemView.findViewById(R.id.decScore);
plusOne =(ImageButton)itemView.findViewById(R.id.incScore);
commentText = (TextView)itemView.findViewById(R.id.comment);
score = (TextView)itemView.findViewById(R.id.commentScore);
commentTime =(TextView)itemView.findViewById(R.id.commentTime);
avatarId = (TextView)itemView.findViewById(R.id.ivUserAvatar);
}
public void bindView(Comments comment, int position) {
commentText.setText(comment.getCommentText());
score.setText(Integer.toString(comment.getScore()));
String timeText = DateUtils.getRelativeTimeSpanString( comment.getCreatedAt().getTime(), System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS).toString();
timeText = timeText.replace("hours","hrs");
timeText = timeText.replace("seconds","secs");
timeText = timeText.replace("minutes","mins");
commentTime.setText(timeText);
int commentHandler = comment.getCommenterHandle();
String commenterNumber = "";
if(commentHandler==0)
{
commenterNumber = "OP";
}
else{
commenterNumber = "#"+commentHandler;
}
avatarId.setText( commenterNumber);
model = comment;
String choice = "none";
minusOne.setEnabled(true);
plusOne.setEnabled(true);
minusOne.setVisibility(View.VISIBLE);
plusOne.setVisibility(View.VISIBLE);
for (ParseObject choiceIter : choices) {
if ((choiceIter.getParseObject("comment").getObjectId()).equals(comment.getObjectId())) {
choice = choiceIter.getString("userChoice");
break;
}
}
Log.i("debug",comment.getCommentText()+" "+comment.getScore()+" "+choice);
switch (choice) {
case "plusOne":
Log.i("darkplus","setting darkplus");
plusOne.setImageResource(R.drawable.ic_add_circle_black_18dp);
plusOne.setOnClickListener(reversePlusOneOnClickListener);
//minusOne.setOnClickListener(minusOneOnClickListener);
minusOne.setVisibility(View.GONE);
break;
case "minusOne":
Log.i("darkminus","setting darkminus");
minusOne.setImageResource(R.drawable.ic_remove_circle_black_18dp);
minusOne.setOnClickListener(reverseMinusOneOnClickListener);
//plusOne.setOnClickListener(plusOneOnClickListener);
plusOne.setVisibility(View.GONE);
break;
case "none":
Log.i("darkregular","setting regular");
minusOne.setImageResource(R.drawable.ic_remove_black_18dp);
plusOne.setImageResource(R.drawable.ic_add_black_18dp);
plusOne.setOnClickListener(plusOneOnClickListener);
minusOne.setOnClickListener(minusOneOnClickListener);
break;
}
}
View.OnClickListener reversePlusOneOnClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!FourUtils.isConnected(v.getContext())) {
return;
}
minusOne.setEnabled(false);
plusOne.setEnabled(false);
model.increment("plusOne", -1);
model.increment("score", -1);
model.saveEventually(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
ParseQuery<ParseObject> query = ParseQuery.getQuery("CommentChoice");
query.whereEqualTo("user", ParseUser.getCurrentUser());
query.whereEqualTo("comment", model);
query.fromPin(Four.COMMENT_CHOICE);
query.getFirstInBackground(new GetCallback<ParseObject>() {
#Override
public void done(ParseObject parseObject, ParseException e) {
if (e == null) {
if (parseObject == null) {
parseObject = ParseObject.create("CommentChoice");
parseObject.put("comment", model);
parseObject.put("user", ParseUser.getCurrentUser());
}
parseObject.put("userChoice", "none");
parseObject.pinInBackground(Four.COMMENT_CHOICE, new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
score.setText(Integer.toString(model.getInt("score")));
//votes.setText((model.getInt("minusOne") + model.getInt("plusOne")) + " votes");
minusOne.setVisibility(View.VISIBLE);
plusOne.setImageResource(R.drawable.ic_add_black_18dp);
plusOne.setOnClickListener(plusOneOnClickListener);
minusOne.setEnabled(true);
plusOne.setEnabled(true);
// minusOne.setOnClickListener(minusOneOnClickListener);
BusProvider.getInstance().post(new NewCommentChoicesAdded());
} else {
e.printStackTrace();
}
}
});
}
else{e.printStackTrace();}
}
});
} else {
e.printStackTrace();
Log.i("plus1 error", e.getMessage());
}
}
});
}
};
When the async code is done, you should update the data, not the views. After updating the data, tell the adapter that the data changed. The RecyclerView gets note of this and re-renders your view.
When working with recycling views (ListView or RecyclerView), you cannot know what item a view is representing. In your case, that view gets recycled before the async work is done and is assigned to a different item of your data.
So never modify the view. Always modify the data and notify the adapter. bindView should be the place where you treat these cases.
Chet Haase from Google discusses your exact issue in this DevBytes video.
In short, the framework need to be notified that one of the Views is in "transient" state. Once notified, the framework will not recycle this View until its "transient" flag cleared.
In your case, before you execute the async action, call setHasTransientState(true) on the child View that should change when the async action completes. This View will not be recycled until you explicitly call setHasTransientState(false) on it.
Offtopic:
It looks like you might be manipulating UI elements from background threads. Don't do that! If you can have a reference to Activity then use its runOnUiThread(Runnable action) API instead. If getting a reference to Activity is difficult, you can obtain UI thread's Handler and use its post(Runnable action) API.
Without code to look at, this is going to be difficult (if not impossible) for people to provide an exact answer. However, based on this description it sounds as though your async network loading (using an AsyncTask or custom Loader?) is not specifically tied to an element being tracked by your adapter. You'll need to have some way of tying the two together since the child View objects shown by the RecyclerView are re-used to be more efficient. This also means that if a View is being reused and there is an active async operation tied to it, that async operation will need to be canceled. Otherwise you'll see what you see now: the wrong child View being updated with content from an older async call.

Connect a client app Android to a server written with Sails.js using socket.io

Here's my situation: i have a server written with sails.js where i have a user model. I have a dashboard where i can see all the users, create new, delete them and so on...Now i want to create an android app where i can get, using socket.io, notification about the events that occour to the user model.
Example: if i delete a user from my dashboard i want the app to recive a notification from the server that the user has been deleted.
Here's my code for the app:
public class MainActivity extends Activity {
//socket instance
private Socket mSocket;
{
try {
mSocket = IO.socket("http://server_url:port/user");
} catch (URISyntaxException e) {
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); //set the layout for this activity
final TextView tv = (TextView) findViewById(R.id.textView);
mSocket.on("user", new Emitter.Listener() {
#Override
public void call(Object... args) {
tv.setVisibility(View.INVISIBLE);
}
});
mSocket.connect();
final Button btn_createUser = (Button)findViewById(R.id.button);
btn_createUser.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mSocket.connected()) {
//tv.setVisibility(View.INVISIBLE);
}
}
});
}
}
Like i did with the dashboard to connect the socket to the server i did the same thing here, but it seems like the socket doesn't connect, in fact, when i delete a user from the dashboard i get no notification.
Here's the code (that works) i used in my dashboard to connect my socket to the server and listen to updates from the user model:
//connect to the server and listen to updates from user model
io.socket.get('/user', function(data, jwres) {
$scope.users = data;
$scope.$apply(); //write users in the table...
});
//wait for updates...
io.socket.on('user', function serverResponded (data) {
if(data.verb == "updated")
{
//get the user index in the array and update it
var index = getIndex(data.id, $scope.users);
$scope.users[index].online = data.data.online;
$scope.$apply();
}
if(data.verb == "destroyed")
{
//get the user index in the array and remove it
var index = getIndex(data.id, $scope.users);
$scope.users.splice(index, 1);
$scope.$apply();
}
if(data.verb == "created")
{
//simply update the array
$scope.users.push(data.data);
$scope.$apply();
}
});
Now, i think all i'm missing out in the android app is the GET request which automatically subscribe my socket to the model and get notified if something happen...but i don't know hot to do it.
I hope i was clear enough...thank to everyone who will answer me!
PS: i don't want to use AndroidAsync because i need the ultimate version of socket.io!
In case someone need it, i found the solution: https://github.com/balderdashy/sails/issues/2640 here you can find out how to solve the issue! :)

Android not waiting for DB response before finishing statement

I have an interesting problem that I've never run into in programming before. I have an onClickListener that does a lot of username and password checks (makes sure the username is proper length, not taken, etc). I'm using MobDB, and I was using a conditional statement that would return a row if the username already existed. The problem is that the Listener skips the DB and goes to the final check that, if everything works, posts a new username and password to my DB. How can I make it wait for a response from the DB before skipping to the last check?
Here is the relevant code:
usernamecheck3 = true;
MobDB.getInstance().execute(APP_KEY, null, rd, null, false, new MobDBResponseListener() {
#Override public void mobDBSuccessResponse() {
usernamecheck3 = false;
Log.e("mobdbSuccess:", "success");
}
#Override public void mobDBResponse(Vector<HashMap<String, Object[]>> row) {
}
#Override public void mobDBResponse(String jsonObj) {
/*Log.e("mobdbSuccess:", "jsonObj");
Log.e("mobdbSuccess:", jsonObj);
JSONObject mainObject;
try {
mainObject = new JSONObject(jsonObj);
// need to parse the json object.
} catch (JSONException e1) {
e1.printStackTrace();
} */
}
#Override public void mobDBFileResponse(String fileName, byte[] fileData) {
//get file name with extension and file byte array
}
#Override public void mobDBErrorResponse(Integer errValue, String errMsg) {
usernamecheck3 = false;
Log.e("doesnt", "work");
}
});
if(usernamecheck3 == false){
Toast.makeText(getApplicationContext(), "Username is taken, please choose another", Toast.LENGTH_SHORT).show();
}
Basically the check always returns true, and then logcat will say mobdbSuccess: success, which should have set the Bool to false.
Thanks.
MobDBResponseListener is executing on a different thread. What happens here is that the processing is split, while a thread is doing the query, the main thread on which you added the listener, skips right ahead to the validation. Your best bet is to place the validation inside the MobDBResponseListener, on the mobDBResponse method.
Try to debug your code and calls, the Listener may be using an async task. If so, you may do anything you please from the response method, as it will be executing in the main thread again. Otherwise, you should look at solutions that handle threaded execution like Handlers

Categories

Resources