Basically I'm using a RecyclerView with gridAdapter to load images from server. But the problem is items of RecyclerView are blinking in a quirky manner. I've tried all the possible solutions but none of them worked so far.
I tried to update glide version from 4.4.0 to 4.8.0 but it's futile. I also tried to disable the animations of RecyclerView but that couldn't help. Can anyone please help me to solve this issue?
Code:
GridLayoutManager gridLayoutManager = new GridLayoutManager(v.getContext(),3);
gridLayoutManager.setSmoothScrollbarEnabled(true);
gridLayoutManager.setItemPrefetchEnabled(true);
gridLayoutManager.setInitialPrefetchItemCount(20);
posts_rView.setLayoutManager(gridLayoutManager);
posts_rView.setItemAnimator(null);
posts_rView.setHasFixedSize(true);
gridPostAdapter=new StarredAdapter(timelineDataList);
posts_rView.setAdapter(gridPostAdapter);
Data Updation Code:
private Emitter.Listener handlePosts = new Emitter.Listener(){
#Override
public void call(final Object... args){
try {
JSONArray jsonArray=(JSONArray)args[0];
Needle.onMainThread().execute(() -> {
timelineDataList.clear();
swipeRefreshLayout.setRefreshing(false);
for(int i=0;i<jsonArray.length();i++){
try {
//JSONArray arr=jsonArray.getJSONArray(i);
JSONObject ob=jsonArray.getJSONObject(i);
post_username=ob.getString("_pid");
post_fullname=ob.getString("owner_fullname");
if(ob.has("owner_profPic"))post_profPic=ob.getString("owner_profPic");
else post_profPic="";
post_time=ob.getString("time");
post_link=ob.getString("img_link");
likes_counter=ob.getString("likes_counter");
comments_counter=ob.getString("comments_counter");
if(ob.has("caption")) post_caption=ob.getString("caption");
else post_caption=null;
//Skipping Private Posts
if(ob.getString("private_post_stat").equals("yes")&&!post_username.equals(my_username)) {
continue;
}
else
private_post_stat = ob.getString("private_post_stat");
comment_disabled=ob.getString("comment_disabled");
share_disabled=ob.getString("share_disabled");
download_disabled=ob.getString("download_disabled");
if(ob.has("short_book_content")) short_book_content=ob.getString("short_book_content");
else short_book_content=null;
society_name_adp=ob.getString("society");
addTimelineData(post_username,post_fullname,post_profPic,post_time,post_link,post_caption,
private_post_stat,comment_disabled,share_disabled,download_disabled,likes_counter,comments_counter,short_book_content,society_name_adp);
} catch (JSONException e) {
e.printStackTrace();
}
}
RecyclerView.Adapter adapter=posts_rView.getAdapter();
posts_rView.setAdapter(null);
posts_rView.setAdapter(adapter);
});
} catch (Exception e) {
Log.e("error",e.toString());
}
}
};
private void addTimelineData(String username,String fullname,String post_profPic,String time,String img_link,String caption,
String private_post_stat,String comment_disabled,String share_disabled,String download_disabled,String likes_counter,
String comments_counter,String short_book_content,String society_name_adp){
boolean isRepeated = false;
for(TimelineData data:timelineDataList){
if (data.getTime().equals(time)) {
isRepeated = true;
}
}
if(!isRepeated){
timelineDataList.add(new TimelineData(username,fullname,post_profPic,time,img_link,caption,private_post_stat,comment_disabled,share_disabled,download_disabled,likes_counter,comments_counter,short_book_content,society_name_adp));
gridPostAdapter.notifyDataSetChanged();
// posts_rView.scrollToPosition(gridPostAdapter.getItemCount()-1);
// posts_rView.scrollToPosition(0);
}
//gridPostAdapter.notifyItemInserted(timelineDataList.size()-1);
}
Adapter Class:
#Override
public void onBindViewHolder(StarViewHolder holder, int position) {
//loading img
Glide.with( parent.getContext()).asBitmap().load(arrayList.get(position)).apply(new RequestOptions()
.override(200, 200)
.dontAnimate()
.placeholder(new ColorDrawable(Color.parseColor("#20001919"))))
.thumbnail(0.1f)
.into(holder.img);
}
#Override
public int getItemCount() {
return arrayList.size();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
I do not quite understand yet why the flickering and repositioning to the first element is occurring in your case, as I do not see the updating policy of your RecyclerView. However, I would like to suggest something which might remove the problem.
I would like to suggest you remove the setInitialPrefetchItemCount and setItemPrefetchEnabled configurations while setting GridLayoutManager. This will aid in case of you are having a nested RecyclerView which is not your case.
You are setting adapter each time you are updating your data from the server. I guess you are calling the update action code when you are scrolling the items. Which you should not. Moreover, please remove setting up the adapter of your RecyclerView each time you are updating the dataset. Just use, notifyDataSetChanged after each of your updates. Do not clear the timelineDataList and append the new items instead, and then call notifyDataSetChanged on your adapter.
You are calling notifyDataSetChanged in addTimelineData after each insert to your list. This should be done only after adding all the elements to the list.
So I would like to propose the following code for setting your adapter and updating your RecyclerView. Please note that I have not tested the code and you might have to modify some errors which might occur.
The code for setting up the layout manager and the adapter.
GridLayoutManager gridLayoutManager = new GridLayoutManager(v.getContext(),3);
posts_rView.setLayoutManager(gridLayoutManager);
posts_rView.setHasFixedSize(true);
gridPostAdapter = new StarredAdapter(timelineDataList);
posts_rView.setAdapter(gridPostAdapter);
The code for updating the list.
private Emitter.Listener handlePosts = new Emitter.Listener(){
#Override
public void call(final Object... args){
try {
JSONArray jsonArray = (JSONArray)args[0];
Needle.onMainThread().execute(() -> {
// Do not clear the list. Just append the new data in the list instead
// timelineDataList.clear();
swipeRefreshLayout.setRefreshing(false);
for(int i = 0; i < jsonArray.length(); i++){
try {
JSONObject ob = jsonArray.getJSONObject(i);
post_username = ob.getString("_pid");
post_fullname = ob.getString("owner_fullname");
if(ob.has("owner_profPic")) post_profPic = ob.getString("owner_profPic");
else post_profPic = "";
post_time = ob.getString("time");
post_link = ob.getString("img_link");
likes_counter = ob.getString("likes_counter");
comments_counter = ob.getString("comments_counter");
if(ob.has("caption")) post_caption = ob.getString("caption");
else post_caption = null;
//Skipping Private Posts
if(ob.getString("private_post_stat").equals("yes")&&!post_username.equals(my_username)) {
continue;
}
else
private_post_stat = ob.getString("private_post_stat");
comment_disabled = ob.getString("comment_disabled");
share_disabled = ob.getString("share_disabled");
download_disabled = ob.getString("download_disabled");
if (ob.has("short_book_content")) short_book_content = ob.getString("short_book_content");
else short_book_content = null;
society_name_adp = ob.getString("society");
addTimelineData(post_username, post_fullname, post_profPic, post_time, post_link,post_caption, private_post_stat, comment_disabled, share_disabled, download_disabled, likes_counter, comments_counter, short_book_content, society_name_adp);
} catch (JSONException e) {
e.printStackTrace();
}
}
gridPostAdapter.notifyDataSetChanged();
});
} catch (Exception e) {
Log.e("error",e.toString());
}
}
};
private void addTimelineData(String username, String fullname, String post_profPic, String time, String img_link, String caption, String private_post_stat, String comment_disabled, String share_disabled, String download_disabled, String likes_counter, String comments_counter, String short_book_content, String society_name_adp) {
boolean isRepeated = false;
for(TimelineData data:timelineDataList){
if (data.getTime().equals(time)) {
isRepeated = true;
}
}
if(!isRepeated){
timelineDataList.add(new TimelineData(username,fullname,post_profPic,time,img_link,caption,private_post_stat,comment_disabled,share_disabled,download_disabled,likes_counter,comments_counter,short_book_content,society_name_adp));
// Do not call notifyDataSetChanged each time you are adding an item. This will be called in the call function above. So remove this line.
// gridPostAdapter.notifyDataSetChanged();
}
}
Try to add some pagination in the server side API if it is not there already so that you might fetch the next 20 dataset instead of fetching the whole data again when you are scrolling down.
Hope that helps.
Related
I am not particularly sure how to ask this question, so I will just use images to explain
In my App, onClick of the download icon calls a download method for the video in the row. When it is completely downloaded, the Icon changes from black to green. A boolean flag is used to save this state in SharedPreference. This saved state is called again in my RecyclerView Adapter so the downloaded state can reflect when the app is relaunched.
THE CHALLENGE IS...
When the app relaunches, instead only the downloaded row icon to show green, the Icon turns green for every row even when they have not been downloaded. below is my code.
class DownloadReceiver extends ResultReceiver { //DownloadReceiver class
public DownloadReceiver(Handler handler) {
super(handler);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
if (resultCode == TichaDownloadService.UPDATE_PROGRESS) {
int progress = resultData.getInt("progress"); //get the progress
//Set the progress to progressBarCir
progressBarCir.setProgress(progress);
icon_download.setVisibility(View.GONE);
progressBarCir.setVisibility(View.VISIBLE);
Log.i("STATUS", "DOWNLOADING>>>");
if (progress == 100) { // Downloade process is completed
isNotDownloaded = false; // Flagging that the video has been downloaded
progressBarCir.setVisibility(View.GONE);
// Setting the Download Icon to reflect the New color state
icon_download.setColorFilter(itemView.getContext().getResources().getColor(R.color.funnygreen));
icon_download.setVisibility(View.VISIBLE);
// Saving the boolean flag in SharedPreferece
SharedPreferences sharedPreferences = getSharedPreferences("com.example.instagramclone",MODE_PRIVATE);
sharedPreferences.edit().putBoolean("isDownloadedState",isNotDownloaded).commit();
// Logging of the save state to confirm state is saved.
boolean newState= sharedPreferences.getBoolean("isDownloadedState",true);
Log.i("STATE XCHANGE", "DOWNLOADED HENCE, "+String.valueOf(newState));
}
} else {
Log.i("STATUS", " NOT DOWNLOADING,BOSS");
}
}
}
Below is a snippet of Holder section of my Adapter Class
public LectureClassesHolder(#NonNull final View itemView) {
super(itemView);
// Now we ref each custom layout view item using the itemView
textViewTitle = itemView.findViewById(R.id.subject_topic);
textViewDescription = itemView.findViewById(R.id.subject_description);
textViewDuration = itemView.findViewById(R.id.subject_duration);
imageViewDownload = itemView.findViewById(R.id.download);
textViewUrl = itemView.findViewById(R.id.url_link);
SharedPreferences sharedPreferences = itemView.getContext().getSharedPreferences("com.example.instagramclone",Context.MODE_PRIVATE);
isNotDownloaded = sharedPreferences.getBoolean("isDownloadedState",true);
if (isNotDownloaded){
imageViewDownload.setColorFilter(itemView.getContext().getResources().getColor(R.color.black));
Log.i("DOWNLOAD STATE ","NOT Downloaded State is "+ isNotDownloaded);
}else {
imageViewDownload.setColorFilter(itemView.getContext().getResources().getColor(R.color.funnygreen));
Log.i("DOWNLOAD STATE ","NOT Downloaded State is "+ isNotDownloaded);
}
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && listener != null) {
listener.onItemClick(getSnapshots().getSnapshot(position), position, itemView);
}
}
});
// Incase e no work
imageViewDownload.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && listener != null) {
listener.onViewItemClick(getSnapshots().getSnapshot(position), position, itemView);
}
}
});
}
I NEED a way to make the change effective for only the row concerned. Would appreciate any assistance on how to achieve this.
The problem is there is of course no association between the individual records and what is stored .The best solution would be to use a database with a table containing atleast a column for the download url and another for status,store the records with the desired status (for example use 0 for not downloaded status and 1 for downloaded status ) and update status column of the row with your url after download then you can query to check the status of the record .however a quick fix to your solution while still using shared prefrence would be to store the links in an array and check against that array see:
public static String all_records(Context act)
{
SharedPreferences prefs = act.getSharedPreferences("SHARED_PREFS_NAME", act.MODE_PRIVATE);
return prefs.getString("all_records", "[]");
}
public static void add_download(Activity act,String url)
{
JSONArray ja=new JSONArray();
try {
ja=new JSONArray(all_records(act));
JSONObject jo=new JSONObject();
jo.put("url",url);
ja.put(jo);
} catch (JSONException e) {
e.printStackTrace();
}
SharedPreferences.Editor saver =act.getSharedPreferences("SHARED_PREFS_NAME", act.MODE_PRIVATE).edit();
saver.putString("all_records",ja.toString());
saver.commit();
}
public static boolean is_downloaded(Activity act,String url)
{
JSONArray ja=new JSONArray();
try {
ja=new JSONArray(all_records(act));
for (int i=0;i<ja.length();i++)
{
try {
Log.e("Check ", "" + ja.getJSONObject(i).getString("url") + " Against " + url);
if (ja.getJSONObject(i).getString("url").equalsIgnoreCase(url)) {
return true;
}
}catch (Exception ex){}
}
} catch (JSONException e) {
e.printStackTrace();
}
return false;
}
in this case you would use it like this to when downloading
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
if (resultCode == TichaDownloadService.UPDATE_PROGRESS) {
int progress = resultData.getInt("progress"); //get the progress
//Set the progress to progressBarCir
progressBarCir.setProgress(progress);
icon_download.setVisibility(View.GONE);
progressBarCir.setVisibility(View.VISIBLE);
Log.i("STATUS", "DOWNLOADING>>>");
if (progress == 100) { // Downloade process is completed
isNotDownloaded = false; // Flagging that the video has been downloaded
progressBarCir.setVisibility(View.GONE);
// Setting the Download Icon to reflect the New color state
icon_download.setColorFilter(itemView.getContext().getResources().getColor(R.color.funnygreen));
icon_download.setVisibility(View.VISIBLE);
// Saving the boolean flag in SharedPreferece
add_download(/*your context*/,/*your url*/);
}
} else {
Log.i("STATUS", " NOT DOWNLOADING,BOSS");
}
}
on your recycle view adapter onbindview or wherever you want to get retrieve the status of whether downloaded call is_downloaded(/*YOUR CONTEXT*/,"YOUR URL");
i'm trying to download a list of cases from a server and then populate an arraylist to use it in my RecyclerView Adapter but every time i try to populate the array list from asynctask i can print the data in every single step from the populating bu not from the Arraylist i'm using (i'm trying to store in an arrayList that contains objects of the type Case which i created)
this is the part of the code with the problem
this is my AsyncTask...
`
public class downloadingNewCases extends AsyncTask<Void,Void ,Boolean>{
private String userid ;
private String state ;
public downloadingNewCases(String userid ,String state ){
this.userid = userid ;
this.state = state ;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
Log.d(AppController.DEBUG, "download New Cases Started");
}
#Override
protected Boolean doInBackground(Void... params){
Log.d(AppController.DEBUG , "user id in the get cases link.." + userid);
String url = AppController.ApiUrl + "GetCases?UserName="+ userid +"&stat="+state;
Log.d(AppController.DEBUG_LINK,url);
try {
JsonArrayRequest request = new JsonArrayRequest(url, new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
Log.d("TEST", String.valueOf(response.length()));
for (int i = 0; i < response.length(); i++) {
try {
Log.d("TEST","downloading data" );
JSONObject object = response.getJSONObject(i);
JSONArray specs = object.getJSONArray("Spe");
ArrayList<Specialities> spe = new ArrayList<>();
for (int j = 0; j < specs.length(); j++) {
Log.d("TEST",specs.getJSONObject(j).getString("SName") );
spe.add(new Specialities(null, specs.getJSONObject(j).getString("SName")));
}
Case ca = new Case(object.getString("ID")
, object.getString("Serial")
, object.getString("Name")
, object.getString("gender")
, object.getString("Age")
, object.getString("NatioanlityID")
, object.getString("HealthProblem")
, object.getString("CityID")
, object.getString("Problem")
, object.getString("Date")
, object.getString("Status")
, spe);
Log.d("TEST",ca.toString());
cases.add(ca);
spe.clear();
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
Log.d(AppController.DEBUG , volleyError.toString());
}
});
request.setRetryPolicy(new DefaultRetryPolicy(
0,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
MySingleton.getmInstance(getActivity()).addToRequestQueue(request);
}catch (Exception e){
Log.d(AppController.DEBUG,"error in the asynctask in the new cases freagment... ");
Log.d(AppController.DEBUG ,e.toString());
}
Log.d(AppController.DEBUG, "Cases size ::" + cases.size());
return true;
}
#Override
protected void onPostExecute(Boolean bool) {
super.onPostExecute(bool);
/* if(listener != null){
listener.getCases(cases);
}*/
adapter.notifyDataSetChanged();
// Log.d(AppController.DEBUG, "new Cases Downloaded...");
}
}
`
(I'm sure that the adapter and recycler view code is correct because I could use the same code now but when I came to test it again it did not work anymore and I can't remember if I changed some thing in it or not - I think not)..
this is my onViewCreate Method .
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.doctor_new_cases_fragment,container,false);
Log.d(AppController.DEBUG_SHARED,"userID in the new case activity ..." + userID);
SharedPreferences prefs = getActivity().getSharedPreferences(AppController.PREFERENCES_NAME , Context.MODE_PRIVATE);
userID = prefs.getString(DoctorProfileSubmitActivity.Shared_userid,null) ;
downloadingNewCases tast = new downloadingNewCases(userID ,"");
tast.execute();
recyclerView = (RecyclerView)view.findViewById(R.id.recyclervew_new_cases);
adapter = new CasesAdapter(view.getContext(), cases);
recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext()));
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(adapter);
ViewGroup parent = (ViewGroup) view.getParent();
if (parent != null){
parent.removeView(view);
}
return view;
}
and finally the Array list is declared as an instance variable of the class (the class extends Fragment + it is a fragment that I'm using in my view pager as a tab layout for my tabbed Activity .)
here's the ArrayList declaration
private final ArrayList<Case> cases = new ArrayList<>();
Since you say you can print the data every step of the way, the problem is probably not related to your data. The problem might be your Arraylist of case And your CaseAdapter.
Let's try a simple test, instead of filling your adapter with case objects, create a new ArrayList of string and fill it with object.getString("name") in your on response. Then change your oncreateview to display this list of names using a normal string arrayadapter.
If you can successfully display the names, the it means the bug is in your case and caseadapter, you would have to show more code for us to help.
sorry guys it was not worth it to add a question on the site right away
i fixed the problem . (it appears that my problem was here)
Case ca = new Case(object.getString("ID")
, object.getString("Serial")
, object.getString("Name")
, object.getString("gender")
, object.getString("Age")
, object.getString("NatioanlityID")
, object.getString("HealthProblem")
, object.getString("CityID")
, object.getString("Problem")
, object.getString("Date")
, object.getString("Status")
, spe);
i accidentally changed object.getString("Gender") to object.getString("gender")
which caused me a JSONException .
i fixed that and everything is working again .
thanks for your help and i'm sorry if i rushed to ask the question without a proper debugging session first .
I compile the list: titleList.add(0, title), apply it in sharedpreferences: prefs.putString(TITLES, title).apply() and now need to retrieve it.
I have looked at a lot of the solutions here and none seem to fit my problem well.
The program is suppose to take text a user inputs and save it using SharedPreferences, so it can be used in a ListActivity later. This list is currently an ArrayList (I believe I need it in an array list because I am using AutoCompleteEditText for suggestions from the array list, so I need the adapter).
Based on the above logic,prefs is a sharedpreference object full of string objects. I have tried using prefs.getAll().values.toArray(new String[0...100]). I found that in an "Android" book. It works, but only gets the first item. After trying methods, Set<?> and a few others, that was the method that got anything at all.
It is all I need to have the program working PERFECTLY. Can someone please help getting this list to save in sharedpreferences, retrieving it as a complete, split, list (that can be indexed) and passing it to a ListActivity?
ArrayList<String> titleList = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_make_lyric);
autoCompleteAdapter = new ArrayAdapter<>(
this,
android.R.layout.simple_list_item_1,
titleList
);
lyricTitle.setAdapter(autoCompleteAdapter);
lyricTitle.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// load in song when selected from auto-complete list
lyricHolder.setText(openSongFile(lyricTitle.getText().toString()));
}
});
saveBtn = (Button) findViewById(R.id.saveBtn);
saveBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
performSave();
}
});
titlePref = getSharedPreferences(titlePrefFile, MODE_PRIVATE);
//titleList = titlePref.getAll().values().toArray();
}
private void performSave() {
String title = lyricTitle.getText().toString();
String song = lyricHolder.getText().toString();
if(!areFieldsNull(title, song)) {
saveSongFile(title, song);
warnSave.show();
}
else
warnEmpty.show();
}
private void saveSongFile(String title, String song) {
BufferedWriter bufferWriter = null;
try {
FileOutputStream fos = openFileOutput(title, Context.MODE_PRIVATE);
bufferWriter = new BufferedWriter(new OutputStreamWriter(fos));
bufferWriter.write(song);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bufferWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//update song title list adapter
autoCompleteAdapter = new ArrayAdapter<>(
this,
android.R.layout.simple_list_item_1,
titleList
);
lyricTitle.setAdapter(autoCompleteAdapter);
titleList.add(0,title);
prefEditor = titlePref.edit();
prefEditor.putString("titleList", title).apply();
}
Sorry, formatting the code just wont work for me.
Thank you and Happy Holidays!
I think you need to use ObjectSerializer.
Save :
ArrayList<String> strings = new ArrayList<String>();
string.add("Hello!");
//save list into SP
SharedPreferences prefs = getSharedPreferences(SHARED_PREFS_FILE, Context.MODE_PRIVATE);
Editor editor = prefs.edit();
try {
editor.putString("LIST", ObjectSerializer.serialize(strings));
} catch (IOException e) {
e.printStackTrace();
}
editor.commit();
Restore :
// load list from preference
SharedPreferences prefs = getSharedPreferences(SHARED_PREFS_FILE, Context.MODE_PRIVATE);
ArrayList<String> strings = new ArrayList<String>();
try {
strings = (ArrayList<String>) ObjectSerializer.deserialize(prefs.getString("LIST", ObjectSerializer.serialize(new ArrayList<String>())));
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Or use parcelable wrapping to save/retrieve your data
Sergey's answer may be just fine. Also, take a look at JPM's answer and class, on this thread. I used it yesterday.
So, using JPM's class, here's writing myBigArrayList:
// write data file for later use
String ser = SerializeObject.objectToString(myBigArrayList);
if (ser != null && !ser.equalsIgnoreCase("")) {
SerializeObject.WriteSettings(c, ser, "myobject.dat");
} else {
SerializeObject.WriteSettings(c, "", "myobject.dat");
}
And, here's a method I adapted, that returns a complete, intact arraylist:
private ArrayList<yabbaData> getYabbaData() {
String ser = SerializeObject.ReadSettings(getActivity().getApplicationContext(), "myobject.dat");
ArrayList<yabbaData> give = null;
if (ser != null && !ser.equalsIgnoreCase("")) {
Object obj = SerializeObject.stringToObject(ser);
// Then cast it to your object and
if (obj instanceof ArrayList) {
// Do something
give = (ArrayList<yabbaData>) obj;
}
}
return give;
}
In the write, I use c as my application context, where I had passed in getApplicationContext().
In the read, I used getActivity().getApplicationContext() because I was in a fragment. Sub in String for my yabbaData object, in ArrayList, and I think it's ready to use.
I am using a Listview of two graphs plotted using androidplot library. I don't know how to constantly update the listview whenever I have new incoming data.
I have a method which receives random data from a device continuously. I have to update the two graphs with this new data. I use a custom view adapter built from Array adapter as found below.
class MyViewAdapter extends ArrayAdapter<View> {
public MyViewAdapter(Context context, int resId, List<View> views) {
super(context, resId, views);
}
#Override
public int getCount() {
return 2;
}
#Override
public View getView(int pos, View convertView, ViewGroup parent) {
LayoutInflater inf = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = convertView;
if (v == null) {
v = inf.inflate(R.layout.listview_example_item, parent, false);
}
p = (XYPlot) v.findViewById(R.id.xyplot);
Random generator = new Random();
p.setTitle("plot" + pos);
for (int k = 0; k < NUM_SERIES_PER_PLOT; k++) {
double rl = Math.random();
double gl = Math.random();
double bl = Math.random();
double rp = Math.random();
double gp = Math.random();
double bp = Math.random();
if(setArrayValues != null){
if(pos == 0) {
XYSeries series = new SimpleXYSeries(setArrayValues.getSeries1Numbers(), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "RPM");
//XYSeries series = new SimpleXYSeries(nums, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "RPM");
p.addSeries(series, new LineAndPointFormatter(
Color.rgb(new Double(rl * 255).intValue(), new Double(gl * 255).intValue(), new Double(bl * 255).intValue()),
Color.rgb(new Double(rp * 255).intValue(), new Double(gp * 255).intValue(), new Double(bp * 255).intValue()),
null, null));
}
else{
XYSeries series = new SimpleXYSeries(setArrayValues.getSeries2Numbers(), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "SPEED");
//XYSeries series = new SimpleXYSeries(nums, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "SPEED");
p.addSeries(series, new LineAndPointFormatter(
Color.rgb(new Double(rl * 255).intValue(), new Double(gl * 255).intValue(), new Double(bl * 255).intValue()),
Color.rgb(new Double(rp * 255).intValue(), new Double(gp * 255).intValue(), new Double(bp * 255).intValue()),
null, null));
}
}
}
p.redraw();
return v;
}
}
The below method receives data from a device and updates relevant Arraylists.
public void receivePackage() throws SocketException {
new Thread() {
byte[] packet = new byte[64];
DatagramPacket data = new DatagramPacket(packet,
packet.length);
DatagramSocket socket = new DatagramSocket(null);
JSONObject json;
#Override
public void run() {
try {
isRunning = true;
socket.setReuseAddress(true);
socket.bind(new InetSocketAddress(50009));
} catch (SocketException e1) {
e1.printStackTrace();
}
try {
while (isRunning) {
socket.receive(data);
final String string = new String(
data.getData(), 0,
data.getLength(), "UTF-8");
json = new JSONObject(string);
runOnUiThread(new Runnable() {
#Override
public void run() {
try {
int wiper = json.getInt("wiper");
int speed = json.getInt("speed");
speed = speed + 2000;
setArrayValues.addValues(wiper,speed);
lv.invalidateViews();
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
socket.close(); // keep this out of while
}
}.start();
}
Actually I cannot plot the data points in the graph. I have used
setArrayValues.addValues(wiper,speed);
I have a separate helper class which sets the values to two different ArrayLists which I use in the line
XYSeries series = new SimpleXYSeries(setArrayValues.getSeries1Numbers(), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "RPM");
XYSeries series = new SimpleXYSeries(setArrayValues.getSeries2Numbers(), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "RPM");
But the values are not displayed in the plot. Can you suggest any solution for this.
The notifyDatasetChanged will retrigger the getView for your visible views.
Be careful that you'll have to reset the graphs before setting the new data.
It's not gonna be efficient tho.
Why are you using a listView if you only have two graphs? A ScrollView with a linearlayout would be much easier to manage.
Without seeing all of your code, I would guess that one of the following is the case.
1) You never initialize setArrayValues, so that setArrayValues == null, and hence the series are never created.
2) You are not calling notifyDataSetChanged on your adapter. For instance, if you want the graphs to repeatedly update, then you need to call notifyDataSetChanged on your adapter, which will trigger a call to getView(), where you are creating the plots.
The problem seems to be outside of the code you have displayed. Something is not being done properly. Its hard to even tell if you actually set up an adapter. For instance, it seems like the easiest way to set up your code would be to have an ArrayAdapter, and then call notifyDataSetChanged. It seems like you are trying to accomplish the same thing by using lv.invalidateViews();, where I'm guessing that lv stands for ListView. If this is the case, then that is the problem... You need to have some type of adapter for the listView, and it seems like you don't have one. Try setting up an arrayAdapter, then in place of lv.invalidateViews();, use
myAdapter.notifyDataSetChanged();
SEE REVISION AT BOTTOM This is a fight card, so it has two people fighting one another, a red vs blue. It has to be a dynamic list that is populated information from parse.com. The first Query is fightOrder. This is a class on Parse.com that has two objectId's on a row. The redCorner and blueCorner find this information in my database (also on parse.com) and display the information accordingly. My problem, is my progressDialog box appears, and it never goes away. My list is never populated. I tried doing it without the dialog box, and populating my list with ever query and had same results.
NOTE: the list is working properly. This is a list I have used successfully before when I would load my information differently. I am just changing the way I load information because I need to have a database of all fighters, and load my fight card from that list.
NOTE: GetCallBack and FindCallBack are asynchronous, that is why this is an odd loop. I have to wait for the done().
Here is the java
public class databaseFightCard extends Activity {
int I;
int size;
private HomeListAdapter HomeListAdapter;
private ArrayList<HomeItem> HomeItemList;
private SeparatedListAdapter adapter;
//this int is to test for main and coMain events. If one is TRUE, It will assign the array position to main or coMain.
int main, coMain;
ParseQuery<ParseObject> blueCorner = ParseQuery.getQuery("FightersDB");
ParseQuery<ParseObject> redCorner = ParseQuery.getQuery("FightersDB");
String name1, name2;
List<String> red = new ArrayList<String>();
List<String> blue = new ArrayList<String>();
private ListView listView;
ProgressDialog progressDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_list);
progressDialog = ProgressDialog.show(this, "", "Loading bout...", true);
initialization();
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
HomeItem homeItem = (HomeItem) adapter.getItem(position);
AlertDialog.Builder showFighter = new AlertDialog.Builder(databaseFightCard.this, android.R.style.Theme_DeviceDefault_Dialog);
showFighter.setTitle(homeItem.getHomeItemLeft().toString() + " and " + homeItem.getHomeItemRight().toString());
showFighter.setMessage("166 - 165\nLogan Utah - Richmond Utah");
showFighter.setPositiveButton("DONE", null);
showFighter.setNegativeButton("Cancel", null);
AlertDialog dialog = showFighter.show();
TextView messageView = (TextView) dialog.findViewById(android.R.id.message);
messageView.setGravity(Gravity.CENTER);
Toast.makeText(getBaseContext(), homeItem.getHomeItemLeft().toString() + " " + homeItem.getHomeItemRight().toString(), Toast.LENGTH_LONG).show();
System.out.println("Selected Item : " + homeItem.getHomeItemID());
}
});
HomeListAdapter = new HomeListAdapter(getApplicationContext(), 0, HomeItemList);
//find the fight card, and read the ids
ParseQuery<ParseObject> fightOrder = ParseQuery.getQuery("FightCard");
fightOrder.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> parseObjects, ParseException e) {
if (e == null) {
size = parseObjects.size();
int i = 0;
while (i < size) {
if (parseObjects.get(i).getBoolean("main")) {
main = i;
}
if (parseObjects.get(i).getBoolean("coMain")) {
coMain = i;
}
red.add(i, parseObjects.get(i).getString("redCorner"));
blue.add(i, parseObjects.get(i).getString("blueCorner"));
i++;
}
displayRed();
} else {
e.printStackTrace();
}
}
});
}
private void displayRed() {
adapter = new SeparatedListAdapter(this);
//find one fighter at a time. in the done() method, start the second fighter.
redCorner.getInBackground(red.get(I), new GetCallback<ParseObject>() {
#Override
public void done(ParseObject parseObject, ParseException e) {
if (e == null) {
HomeItemList = new ArrayList<HomeItem>();
HomeItem homeItem = new HomeItem();
homeItem.setHomeItemID(I);
name1 = parseObject.getString("Name");
homeItem.setHomeItemLeft(name1);
HomeItemList.add(homeItem);
if (HomeListAdapter != null) {
if (I == main) {
adapter.addSection(" MAIN EVENT ", HomeListAdapter);
} else if (I == coMain) {
adapter.addSection(" Co-MAIN EVENT ", HomeListAdapter);
} else {
adapter.addSection(" FIGHT CARD ", HomeListAdapter);
}
}
displayBlue();
} else {
e.printStackTrace();
}
I++;
while (I < size){
displayRed();
}
if (size == I) {
listView.setAdapter(adapter);
progressDialog.dismiss();
}
}
});
}
private void displayBlue() {
//find the red fighters then call the dismiss();
blueCorner.getInBackground(blue.get(I), new GetCallback<ParseObject>() {
#Override
public void done(ParseObject parseObject, ParseException e) {
if (e == null) {
HomeItemList = new ArrayList<HomeItem>();
HomeItem homeItem = new HomeItem();
homeItem.setHomeItemID(I);
name2 = parseObject.getString("Name");
homeItem.setHomeItemLeft(name2);
HomeItemList.add(homeItem);
if (HomeListAdapter != null) {
if (I == main) {
adapter.addSection(" MAIN EVENT ", HomeListAdapter);
} else if (I == coMain) {
adapter.addSection(" Co-MAIN EVENT", HomeListAdapter);
} else {
adapter.addSection(" FIGHT CARD ", HomeListAdapter);
}
}
} else {
e.printStackTrace();
}
//if it is done running through all the IDS, set the listView, and dismiss the dialog.
I++;
while (I < size){
displayRed();
}
if (size == I) {
listView.setAdapter(adapter);
progressDialog.dismiss();
}
}
});
}
private void initialization() {
listView = (ListView) findViewById(R.id.Listview);
}
LogCat
java.lang.RuntimeException: This query has an outstanding network
connection. You have to wait until it's done.
That is pointing to this line:
while (I < size){
displayRed();
}
EDIT
I believe that it is the async tasks that are causing this.
On a previous build: I would call for one line item at a time, add it to my list, repeat until finished, then display list.
On the this build: I want to call for redCorner add it to my list, call blueCorner add it to the same line, repeat until finished, then display the list. Here is what it would look like (previous build):
Revised My question is still unanswered. Maybe I need to simplify it. I will have +-20 objectId's from one class. I took out all the code that is irrelevant. Still getting unexpected results with this code.
redCorner.getInBackground(red.get(i), new GetCallback<ParseObject>() {
#Override
public void done(ParseObject parseObject, ParseException e) {
if (e == null) {
Log.d("NAME " + i, name1 + " ");
i++;
while (i < size) {
redCorner.cancel();
displayRed();
}
if (i == size) {
progressDialog.dismiss();
}
} else {
e.printStackTrace();
}
}
});
This is yet another case of not understanding the nature of Async coding (I've seen a lot of questions with the same issue).
In your case you are calling the displayRed() method that fires off some async code, then returns.
Here's how your code might run:
First call to displayRed() (dr1)
(dr1) Async redCorner.getInBackground(..) (async1) started
(dr1) returns
.. some time passes ..
(async1) getInBackground(..) call returns with data, runs code block
calls displayBlue() (db1)
(db1) blueCorner.getInBackground(..) (async2) started
(db1) returns
begins the while loop
calls displayRed() (dr2)
(dr2) Async redCorner.getInBackground(..) (async3) started
(dr2) nothing has touched I yet, tries to start another async redCorner.getInBackgroud(..) (async4)
ERROR
You're writing your code as if the async blocks are running sync instead. Keep in mind that getInBackground means "make a web call to get this data, and when something happens (error or success) run this block of code I'm giving you, possibly on another thread".
Think about the order you want to achieve things, realise that you're asking it to start a process that takes some time, and adjust your code accordingly.