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");
Related
please I need your help after searching a lot without issues.
I have an demostration app to use an second screen attached to my device.
I have the source code of the app, they use the Mediarouter class and an class named LauncherSecondScreen extended from the Presentation class
I have tried to make the app as an service to keep runnig the app in background, but the mediarouter callback seems running only on the princpal thread ( I'm not sure I am just a beginner in android dev).
I have the full code of the app : there is two layout activity one showed on the princpal screen and the other on the second screen:
public class MainActivity extends Activity {
private final String TAG = "PresentationWithMediaRouterActivity";
private MediaRouter mMediaRouter;
private LauncherSecondScreen mPresentation;
private boolean mPaused;
/**
* Initialization of the Activity after it is first created. Must at least
* call {#link android.app.Activity#setContentView setContentView()} to
* describe what is to be displayed in the screen.
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
// Be sure to call the super class.
super.onCreate(savedInstanceState);
// Get the media router service.
mMediaRouter = (MediaRouter)getSystemService(Context.MEDIA_ROUTER_SERVICE);
// See assets/res/any/layout/presentation_with_media_router_activity.xml for this
// view layout definition, which is being set here as
// the content of our screen.
setContentView(R.layout.activity_main);
}
#Override
protected void onResume() {
// Be sure to call the super class.
super.onResume();
// Listen for changes to media routes.
mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_LIVE_VIDEO, mMediaRouterCallback);
// Update the presentation based on the currently selected route.
mPaused = false;
updatePresentation();
}
private void updatePresentation() {
// Get the current route and its presentation display.
MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(
MediaRouter.ROUTE_TYPE_LIVE_VIDEO);
Display presentationDisplay = route != null ? route.getPresentationDisplay() : null;
// Dismiss the current presentation if the display has changed.
if (mPresentation != null && mPresentation.getDisplay() != presentationDisplay) {
Log.i(TAG, "Dismissing presentation because the current route no longer "
+ "has a presentation display.");
mPresentation.dismiss();
mPresentation = null;
}
// Show a new presentation if needed.
if (mPresentation == null && presentationDisplay != null) {
Log.i(TAG, "Showing presentation on display: " + presentationDisplay);
mPresentation = new LauncherSecondScreen(this, presentationDisplay);
mPresentation.setOnDismissListener(mOnDismissListener);
try {
mPresentation.show();
} catch (WindowManager.InvalidDisplayException ex) {
Log.w(TAG, "Couldn't show presentation! Display was removed in "
+ "the meantime.", ex);
mPresentation = null;
}
}
// Update the contents playing in this activity.
updateContents();
}
private void updateContents() {
// Show either the content in the main activity or the content in the presentation
// along with some descriptive text about what is happening.
if (mPresentation != null) {
if (mPaused) {
mPresentation.dismiss();//getSurfaceView().onPause();
} else {
mPresentation.show();//getSurfaceView().onResume();
}
} else {
/* mInfoTextView.setText("presentation_with_media_router_now_playing_locally");
mSurfaceView.setVisibility(View.VISIBLE);
if (mPaused) {
mSurfaceView.onPause();
} else {
mSurfaceView.onResume();
}*/
}
}
private final MediaRouter.SimpleCallback mMediaRouterCallback =
new MediaRouter.SimpleCallback() {
#Override
public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
Log.d(TAG, "onRouteSelected: type=" + type + ", info=" + info);
updatePresentation();
}
#Override
public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
Log.d(TAG, "onRouteUnselected: type=" + type + ", info=" + info);
updatePresentation();
}
#Override
public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
Log.d(TAG, "onRoutePresentationDisplayChanged: info=" + info);
updatePresentation();
}
};
/**
* Listens for when presentations are dismissed.
*/
private final DialogInterface.OnDismissListener mOnDismissListener =
new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
if (dialog == mPresentation) {
Log.i(TAG, "Presentation was dismissed.");
mPresentation = null;
updateContents();
}
}
};
#SuppressLint({"NewApi"})
public class LauncherSecondScreen extends Presentation
{
public LauncherSecondScreen(Context paramContext, Display paramDisplay)
{
super(paramContext, paramDisplay/*,android.R.style.Theme_Holo_Light_Dialog_NoActionBar*/);
}
protected void onCreate(Bundle paramBundle)
{
super.onCreate(paramBundle);
setContentView(R.layout.dialog_second_screen_content);
//// this.iv_secondScreen_banner = ((ImageView)findViewById(R.id.titleImage));
}
}
}
the app is well, it make one view in the princpale screen and a second view in the second screen , but when i resume the app to background the second screen take the same view of the first screen.
I want to keep the second view showing in the second screen even i resume the app to use another app
In my application I have draft messages which I can edit. Some of my drafts include attachments which I try to send to my edit activity and show it at recyclerview. In general I have managed to send my string arraylist and get it at my activity. But I can't show my attached files, especially their names at the recyclerview. I tried to make smth like that:
adapter.notifyDataSetChanged();
but it didn't help me.
So, firstly I get from my message names of attached files:
file_name = Objects.requireNonNull(response.body()).getAttachesNames();
then put this names into arraylist:
nameList = new ArrayList<>(Arrays.asList(file_name));
such result I can see in my logs:
W: [eZV9f.jpg, index.html]
and then I send my list via intent to another activity:
intent2.putStringArrayListExtra("attached_files", (ArrayList<String>) nameList);
receiving data from intent:
Intent intent = getIntent();
extras = intent.getExtras();
if (extras != null) {
if (extras.containsKey("attached_files")) {
draft_files = getIntent().getStringArrayListExtra("attached_files");
Log.w("MY_TAG", String.valueOf(draft_files));
}
}
results from logcat:
W: [eZV9f.jpg, index.html]
initialising of my adapter and recyclerview:
adapter = new AttachedFileAdapter(mNames);
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(WriteResponseMess.this, LinearLayoutManager.VERTICAL, false));
and then I try to get single element from this list and add to my ArrayList<>:
for (int i = 0; i < draft_files.size(); i++) {
mNames.addAll(Collections.singleton(draft_files.get(i)));
adapter.notifyDataSetChanged();
recyclerView.setAdapter(adapter);
Log.w("MY_TAG", draft_files.get(i));
Log.w("MY_TAG", String.valueOf(mNames));
}
all previous pieces of code are used at my onCreate() method and as a result of all these actions I would like to see income data from another activity. Sometimes I managed to do it, but one element contained all income data and looked like this:
[eZV9f.jpg, index.html]
and it was wrong for me. I try to create the list which will contain all elements separately. I also tried to use some info from this link which is connected with Collections, but I didn't manage to reach the goal of my task. In general I'm sure that the solution is very simple but I can't see it.
update
all my activity class has more than 1000 lines and I will share all code which is connected with adding attachments and show attached data at my writing form:
Here is my dialog for getting directory list and choosing some files:
#Override
protected Dialog onCreateDialog(int id) {
Dialog dialog = null;
switch (id) {
case CUSTOM_DIALOG_ID:
dialog = new Dialog(WriteResponseMess.this, android.R.style.Theme_DeviceDefault_Light_NoActionBar_Fullscreen);
dialog.setContentView(R.layout.dialog_layout);
dialog.setCanceledOnTouchOutside(true);
Toolbar toolbar = dialog.findViewById(R.id.toolbar_d);
toolbar.setTitle("Add a new file.");
textFolder = dialog.findViewById(R.id.folder);
buttonUp = dialog.findViewById(R.id.up);
buttonUp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ListDir(curFolder.getParentFile());
}
});
dialog_ListView = dialog.findViewById(R.id.dialoglist);
final Dialog finalDialog1 = dialog;
dialog_ListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#RequiresApi(api = Build.VERSION_CODES.O)
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
File selected = new File(curFolder, fileList.get(position));
if (selected.isDirectory()) {
ListDir(selected);
}
if (selected.isFile()) {
if (array.size() == 0) {
array = uploadFiles(array, selected.getName(), convertFileToString(selected.getPath()));
adapter.notifyDataSetChanged();
getImages();
} else {
if (array.toString().contains(selected.getName())) {
Toast.makeText(WriteResponseMess.this, R.string.attaching_message, Toast.LENGTH_SHORT).show();
adapter.notifyDataSetChanged();
getImages();
} else {
array = uploadFiles(array, selected.getName(), convertFileToString(selected.getPath()));
adapter.notifyDataSetChanged();
getImages();
}
}
finalDialog1.dismiss();
ms.setArray(array);
}
}
});
break;
}
return dialog;
}
and method for showing attached list, and in general this method works when I create a new message and attach a new file, it works fine:
private void getImages() {
mNames.clear();
adapter.notifyDataSetChanged();
for (int i = 0; i < array.size(); i++) {
JsonObject object = array.get(i).getAsJsonObject();
if (extras != null) {
if (extras.containsKey("attached_files")) {
for (int j = 0; j < draft_files.size(); j++) {
mNames.clear();
mNames.add(draft_files.get(j));
adapter.notifyDataSetChanged();
//Log.w("MY_TAG", draft_files.get(i));
Log.w("MY_TAG", String.valueOf(mNames));
}
mNames.add(object.get("filename").toString().substring(1, object.get("filename").toString().length() - 1));
adapter.notifyDataSetChanged();
//Log.w("MY_TAG", Arrays.toString(draft_files));
Log.w("MY_TAG", Arrays.toString(new ArrayList[]{mNames}));
} else {
mNames.add(object.get("filename").toString().substring(1, object.get("filename").toString().length() - 1));
adapter.notifyDataSetChanged();
Log.w("MY_TAG", String.valueOf(mNames));
}
}
}
}
no need to set Adapter twice . remove this line
recyclerView.setAdapter(adapter);
From
for (int i = 0; i < draft_files.size(); i++) {
mNames.addAll(Collections.singleton(draft_files.get(i)));
adapter.notifyDataSetChanged();
//recyclerView.setAdapter(adapter); no need
Log.w("MY_TAG", draft_files.get(i));
Log.w("MY_TAG", String.valueOf(mNames));
}
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.
In my main activity I display a ListView which uses a custom BaseAdapter (ThoughtListAdapter).
listView = (ListView) findViewById(R.id.list);
adapter = new ThoughtListAdapter(this, resultingThoughts);
listView.setAdapter(adapter);
Every item in the ListView has a custom layout containing a TextView and two Button.
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.list_item_thought, null);
}
thoughtText = (TextView) convertView.findViewById(R.id.thought_text_view);
likeButton = (Button) convertView.findViewById(R.id.thought_like_button);
dislikeButton = (Button) convertView.findViewById(R.id.thought_dislike_button);
When a Button is clicked an AsyncTask (AsyncPost) is called which connects to my database and makes some changes.
likeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
System.out.println("LIKE CLICKED");
Thought t = thoughtItems.get(position);
thoughtId = t.getId();
opinion = 1;
AsyncPost asyncPost = new AsyncPost(activity,ThoughtListAdapter.this);
asyncPost.execute(SHARE_THOUGHT_URL,
TAG_PERSON_EMAIL, "m#b.it",
TAG_THOUGHT_ID, thoughtId.toString(),
TAG_OPINION, opinion.toString());
}
});
What I need is making both Button-s of a list item disappear after the AsyncTask is done with a successful outcome. I have a method onComplete(JSONObject json) which elaborates the JSONObject returned by the AsyncTask. I try to make the buttons non visible inside the onComplete method, but this doesn't work because onComplete() doesn't know which exact button has been clicked.
How can I pass an instance of the exact clicked button inside onComplete() and make disappear only the Like and Dislike buttons of the concerned list item?
AsyncPost is a global AsyncTask used by all my other activities. I would strongly prefer to leave it alone. The onComplete() method functions as the onPostExecute() method of the AsyncTask.
Here are the getView() and onComplete() methods inside my BaseAdapter, which contain all the code shown above.
Thank you.
public View getView(final int position, View convertView, ViewGroup parent) {
if (layoutInflater == null) {
layoutInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.list_item_thought, null);
}
thoughtText = (TextView) convertView.findViewById(R.id.thought_text_view);
likeButton = (Button) convertView.findViewById(R.id.thought_like_button);
dislikeButton = (Button) convertView.findViewById(R.id.thought_dislike_button);
//thoughtItems is a list of custom ojbects (Thought)
Thought t = thoughtItems.get(position);
//Here i set the content of the current TextView
thoughtText.setText(t.getText());
//the two buttons do basically the same thing when get clicked
likeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Thought t = thoughtItems.get(position);
thoughtId = t.getId();
opinion = 1;
AsyncPost asyncPost = new AsyncPost(activity,ThoughtListAdapter.this);
asyncPost.execute(SHARE_THOUGHT_URL,
TAG_PERSON_EMAIL, "m#b.it",
TAG_THOUGHT_ID, thoughtId.toString(),
TAG_OPINION, opinion.toString());
}
});
dislikeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Thought t = thoughtItems.get(position);
thoughtId = t.getId();
opinion = 0;
AsyncPost asyncPost = new AsyncPost(activity,ThoughtListAdapter.this);
asyncPost.execute(SHARE_THOUGHT_URL,
TAG_PERSON_EMAIL, "m#b.it",
TAG_THOUGHT_ID, thoughtId.toString(),
TAG_OPINION, opinion.toString());
}
});
return convertView;
}
#Override
public void onComplete(JSONObject json) {
if (json != null) {
try {
if (json.getInt(TAG_SUCCESS) == 0) {
Toast.makeText(activity, "Operazione non riuscita.", Toast.LENGTH_LONG).show();
} else {
//if everything is good i try to make the buttons of that particular list item disappear
likeButton.setVisibility(View.GONE);
dislikeButton.setVisibility(View.GONE);
}
}
catch (JSONException e) {
Log.e(TAG_LOG, "JSONException", e);
}
}
else Toast.makeText(activity, "Errore connessione!", Toast.LENGTH_LONG).show();
}
One solution to this would be to have something on your Thought object to indicate whether or not to show the buttons.
So in your getView() method you check this
likeButton = (Button) convertView.findViewById(R.id.thought_like_button);
dislikeButton = (Button) convertView.findViewById(R.id.thought_dislike_button);
Thought t = thoughtItems.get(position);
if (t.hideButtons()) {
likeButton.setVisibility(View.GONE);
dislikeButton.setVisibility(View.GONE);
}
else {
likeButton.setVisibility(View.VISIBLE);
dislikeButton.setVisibility(View.VISIBLE);
}
Then you would need to have your onComplete method return the id of the Thought object that it related to. Then inside your onComplete you could do
int id = //get your id from your JSON response
for(Thought t : thoughtItems) {
if (t.getId() == id) {
t.setHideButtons(true);
notifyDataSetChanged();
break;
}
}
By calling notifyDataSetChanged() it will redraw your list and when it does the check for whether it should show the buttons or not it will not show them because it was set on that thought item
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.