I apologize upfront if the question is too long. Here we go:
There are two activities: Main and Detail Activity.
Main Activity is basically a GridView.
Detail Activity is basically shows the clicked item's detail information. I am passing selected item's id (pid) from the Main to the Detail Activity.
I am facing an issue as follows. Initially, I have 3G connection (cellular connection) and clicked on the first item and see the corresponding item detail in the Detail Activity, it works perfectly fine, and go back to the Main Activity, then clicked on the second item, then unfortunately it still shows me the first item in the DetailActivity that I clicked initially.
I switched from 3g to wifi while app is on the active and open. No matter what I click, it still shows me the first item that I clicked initially.
But when I delete the app and reinstall it and get either wifi access only, the app works perfectly fine.
In the following implementation, Connection URL (PRODUCT_DETAIL_URL) is http, not https. I am using Volley library for the network connection.
DetailActivity.java
private void productDetailInit() {
// it is http
StringRequest postRequest = new StringRequest(Request.Method.POST, Config.PRODUCT_DETAIL_URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
jsonObject = response;
loadJsonData();
} catch (Exception e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
}
) {
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("id", productID);
return params;
}
};
RetryPolicy policy = new DefaultRetryPolicy(1000, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
postRequest.setRetryPolicy(policy);
CustomVolleyRequest.getInstance(this).getRequestQueue().add(postRequest);
}
CustomVolleyRequest.java
public class CustomVolleyRequest {
private static CustomVolleyRequest customVolleyRequest;
private static Context context;
private RequestQueue requestQueue;
private ImageLoader imageLoader;
private CustomVolleyRequest(Context context) {
this.context = context;
this.requestQueue = getRequestQueue();
imageLoader = new ImageLoader(requestQueue,
new ImageLoader.ImageCache() {
private final LruCache<String, Bitmap>
cache = new LruCache<String, Bitmap>(20);
#Override
public Bitmap getBitmap(String url) {
return cache.get(url);
}
#Override
public void putBitmap(String url, Bitmap bitmap) {
cache.put(url, bitmap);
}
});
}
private class BitmapCache implements ImageLoader.ImageCache {
private LruCache<String, Bitmap> mCache;
public BitmapCache() {
mCache = new LruCache<>(20);
}
#Override
public Bitmap getBitmap(String url) {
return mCache.get(url);
}
#Override
public void putBitmap(String url, Bitmap bitmap) {
// scaling bitmap for avoiding too much big images
Bitmap scaled = ImageUtil.getInstance().scaleBitmap(bitmap);
mCache.put(url, scaled);
}
}
public static synchronized CustomVolleyRequest getInstance(Context context) {
if (customVolleyRequest == null) {
customVolleyRequest = new CustomVolleyRequest(context);
}
return customVolleyRequest;
}
public RequestQueue getRequestQueue() {
if (requestQueue == null) {
Cache cache = new DiskBasedCache(context.getCacheDir(), 10 * 1024 * 1024);
Network network = new BasicNetwork(new HurlStack());
requestQueue = new RequestQueue(cache, network);
requestQueue.start();
}
return requestQueue;
}
public ImageLoader getImageLoader() {
return imageLoader;
}
}
Adapter.java
class ProductMainAdapter extends ArrayAdapter<ImageRecord> {
private ImageLoader mImageLoader;
private String jsonObject;
ProductMainAdapter(Context context) {
super(context, R.layout.grid_item);
mImageLoader = CustomVolleyRequest.getInstance(this.getContext()).getImageLoader();
}
#NonNull
#Override
public View getView(final int position, View convertView, #NonNull ViewGroup parent) {
final ViewHolder holder;
if(convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(getContext()).inflate(R.layout.grid_item, parent, false);
convertView.setBackgroundResource(R.drawable.round_gridview);
holder.priceTagImage = (ImageView) convertView.findViewById(R.id.priceTag_IV);
holder.textView = (TextView) convertView.findViewById(R.id.text);
holder.imageView = (NetworkImageView) convertView.findViewById(R.id.picture);
holder.priceTagRL = (RelativeLayout) convertView.findViewById(R.id.priceTag_RL);
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
ImageRecord imageRecord = getItem(position);
holder.imageView.setImageUrl(imageRecord != null ? imageRecord.getUrl() : null, mImageLoader);
holder.imageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openProductDetail(position);
}
});
holder.textView.setText(imageRecord != null ? imageRecord.getTitle() : null);
holder.priceTagRL.setRotation(0);
return convertView;
}
private class ViewHolder{
TextView textView;
ImageView priceTagImage;
NetworkImageView imageView;
RelativeLayout priceTagRL;
}
private void openProductDetail(int position) {
try {
ImageRecord imr = getItem(position);
String productID = imr != null ? imr.getId() : "0";
Intent intent = new Intent(getContext(), ProductDetailActivity.class);
intent.putExtra("pid", productID);
getContext().startActivity(intent);
} catch (Exception e) {
Log.e("openProductDetail", "exception", e);
}
}
I wonder what I am missing/ doing wrong in the provided implementation. It has been taking almost 2-3 months, I could not able to handle that issue. Has anyone ever faced a similar situation? Any suggestion or comment is highly appreciated.
You could just kill the activities with finish(); when the other one loads.
I was also having the same problem. In my case in onClick method the position was not correct.
Then I used to set the position as a tag to the specific view which has OnClickListener set and my problem solved.
It may be due to the position is declared as final. So remove the final keyword and try like this.
holder.imageView.setImageUrl(imageRecord != null ? imageRecord.getUrl() : null, mImageLoader);
holder.imageView.setTag(position);
holder.priceTagRL.setRotation(0);
And in onClick method
openProductDetail((int)view.getTag());
Check if the below link can be helpful:
https://stackoverflow.com/a/33682366/2798289 (3G connection might actually be slow that you to have increase the threshold of timeout)
Also, there is a chance the position reference in the adapter can come wrong in some edge case (irrespective of your actual issue). You can try the below alternative method..
Use gridView.setOnItemClickListener
gridview.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
//do your logic based on the position
}
});
Note: In case if GridView is not able to observe the item click event, you have to set android:descendantFocusability="blocksDescendants" to the child root view under gridview. This might happen basically because the GridView items should not be clickable by itself as it will observe the click event.
Reference: https://developer.android.com/guide/topics/ui/layout/gridview.html
Related
I am using retrofit to search the images and displaying. I am using SearchView to search and recyclerview and adapter to display the item.
It's working for the very first time. Second time when I search, it's displaying the same item. It's not clearing the view to display.
Here is my code:
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getName();
ImagesViewModel newsViewModel;
List<Hits> newsArticles;
List<Hits> articleArrayList;
ImagesAdapter newsAdapter;
RecyclerView rvHeadline;
SearchView searchView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rvHeadline = (RecyclerView) findViewById(R.id.rvNews);
searchView=(SearchView) findViewById(R.id.searchView);
searchView.setQueryHint("Search Images");
searchView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
searchView.setIconified(false);
}
});
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
callPixabayImages(query);
Toast.makeText(getBaseContext(), query, Toast.LENGTH_LONG).show();
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
callPixabayImages(newText);
Toast.makeText(getBaseContext(), newText, Toast.LENGTH_LONG).show();
return false;
}
});
articleArrayList = new ArrayList<>();
}
private void callPixabayImages(String query){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ImageApiInterface.PIXABAY_URL)
.addConverterFactory(GsonConverterFactory.create()) //Here we are using the GsonConverterFactory to directly convert json data to object
.build();
ImageApiInterface api = retrofit.create(ImageApiInterface.class);
Call<MyImages> call = api.getMovieDetails(query);
call.enqueue(new Callback<MyImages>() {
#Override
public void onResponse(Call<MyImages> call, Response<MyImages> response) {
Log.e("response","response"+response.code());
if(response.isSuccessful()){
Log.e("response","response"+response.code());
if((response.body() != null ? response.body().getHits() : null) !=null){
List<Hits> imagesList = response.body().getHits();
articleArrayList.addAll(imagesList);
newsAdapter.notifyDataSetChanged();
}
}
}
#Override
public void onFailure(Call<MyImages> call, Throwable t) {
System.out.println("Error: "+ t.getMessage());
Log.e("response","response"+t);
}
});
}
private void setupRecyclerView() {
if (newsAdapter == null) {
newsAdapter = new ImagesAdapter(MainActivity.this, articleArrayList);
rvHeadline.setLayoutManager(new LinearLayoutManager(this));
rvHeadline.setAdapter(newsAdapter);
rvHeadline.setItemAnimator(new DefaultItemAnimator());
rvHeadline.setNestedScrollingEnabled(true);
} else {
newsAdapter.notifyDataSetChanged();
}
}
Here where I need to reset to search the new item inorder to avoid to display the previous item. List is not clearing to display the new items.
Here is Adapter class:
public class ImagesAdapter extends RecyclerView.Adapter<ImagesAdapter.NewsViewHolder> {
private Context context;
private List<Hits> articles;
public ImagesAdapter(Context context, List<Hits> articles) {
this.context = context;
this.articles = articles;
}
#NonNull
#Override
public ImagesAdapter.NewsViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.images_item, parent, false);
return new NewsViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ImagesAdapter.NewsViewHolder holder, final int position) {
String image = articles.get(position).getLargeImageURL();
Log.e("imagestest",image);
if (image != null) {
Glide.with(context) //passing context
.load(image) //passing your url to load image.
.dontAnimate()
.placeholder(context.getDrawable(R.drawable.image_not_available)) //this would be your default image (like default profile or logo etc). it would be loaded at initial time and it will replace with your loaded image once glide successfully load image using url.
//.centerCrop()//this method help to fit image into center of your ImageView
.into(holder.ivNews); //pass imageView reference to appear the image.*/
}
}
#Override
public int getItemCount() {
return articles.size();
}
class NewsViewHolder extends RecyclerView.ViewHolder {
ImageView ivNews;
private NewsViewHolder(#NonNull View itemView) {
super(itemView);
ivNews = itemView.findViewById(R.id.ivNews);
}
}
}
I am clueless where exactly I need to clear the view to fetch the newly searched item.
Trial 1: I tried to clear the list object articleArrayList but it throws crash that adapter size is null.
Clean the
articleArrayList
before loading the searched items again.... by executing this line articleArrayList.clear();
public void onResponse(Call<MyImages> call, Response<MyImages> response) {
Log.e("response","response"+response.code());
if(response.isSuccessful()){
Log.e("response","response"+response.code());
if((response.body() != null ? response.body().getHits() : null) !=null){
List<Hits> imagesList = response.body().getHits();
articleArrayList.clear();
articleArrayList.addAll(imagesList);
newsAdapter.notifyDataSetChanged();
}
}
You're adding generated imagesList to the articleArrayList, you should be replacing the values.
articleArrayList = imagesList;
I do some request from a WordPress page API to get some content and for every content, I get a picture. And I display them on RecyclerView. But they do not show without doing first a scroll of RecyclerView then they start to show one by one.
Here is the NewsActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_news);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Window w = getWindow();
w.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
w.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
back_button = findViewById(R.id.toolbar_back_button);
back_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onBackPressed();
}
});
toolbarTxt = findViewById(R.id.tolbar_text_view);
toolbarTxt.setText("News");
recyclerView = findViewById(R.id.news_recyclerview);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(mAdapter);
recyclerView.setHasFixedSize(true);
ConnectivityManager connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
if(connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState() == NetworkInfo.State.CONNECTED ||
connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState() == NetworkInfo.State.CONNECTED) {
merrLajmet();
} else {
Toast.makeText(this, "You do not have internet connection", Toast.LENGTH_SHORT).show();
}
}
Here's how I get the contents.
public void merrLajmet(){
Uri baseUri = Uri.parse(NEWS_REQUEST_URL);
Uri.Builder uriBuilder = baseUri.buildUpon();
JsonArrayRequest jsonObjectRequest = new JsonArrayRequest(
Request.Method.GET, uriBuilder.toString(), null, new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
listLajmet = Query.shfaqLajmet(response);
for (Lajmi lajmi : listLajmet) {
merrFoton(lajmi);
}
mAdapter.setLajmi(listLajmet);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// Error
}
});
if (listLajmet.isEmpty()) {
MySingleton.getInstance(this).addToRequestQueue(jsonObjectRequest);
}
}
And the get the photo of from specific content.
public void merrFoton(final Lajmi lajmi) {
Uri baseUri = Uri.parse(IMAGE_REQUEST_URL);
Uri.Builder uriBuilder = baseUri.buildUpon();
uriBuilder.appendPath(String.valueOf(lajmi.getFeatureMedia()));
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(
Request.Method.GET, uriBuilder.toString(), null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
String imageUrl = Query.shfaqFoton(response);
if (imageUrl == ""){
imageUrl = String.valueOf(R.drawable.news_photo1);
}
lajmi.setImage(imageUrl);
//mAdapter.setLajmi(listLajmet);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
//Toast.makeText(NewsActivity.this, "Nuk ka te image " +
//error.networkResponse.toString(), Toast.LENGTH_SHORT).show();
}
});
MySingleton.getInstance(this).addToRequestQueue(jsonObjectRequest);
}
And finally, here's my adapter class.
public class LajmiAdapter extends RecyclerView.Adapter<LajmiAdapter.MyViewHolder>{
private List<Lajmi> mLajmiList = new ArrayList<>();
private Context ctx;
public LajmiAdapter(Context ctx) {
this.ctx = ctx;
}
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
TextView mTitle, mCategory;
ImageView mImage,mColor;
public MyViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
mTitle = itemView.findViewById(R.id.news_text_view_titulli);
mCategory = itemView.findViewById(R.id.news_text_view_kategoria);
mImage = itemView.findViewById(R.id.news_image_main);
mColor = itemView.findViewById(R.id.news_image_small);
}
#Override
public void onClick(View v) {
int position = getAdapterPosition();
Lajmi lajmi = mLajmiList.get(position);
Intent intent = new Intent(ctx, SinglenewsActivity.class);
intent.putExtra("title", lajmi.getTitle());
intent.putExtra("category", lajmi.getCategory());
intent.putExtra("image", lajmi.getImage());
intent.putExtra("color",lajmi.getColor());
intent.putExtra("description",lajmi.getDescription());
ctx.startActivity(intent);
}
}
#Override
public LajmiAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView;
itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.news_item, parent, false);
return new LajmiAdapter.MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(LajmiAdapter.MyViewHolder holder, int position) {
Lajmi lajmi = mLajmiList.get(position);
holder.mTitle.setText(lajmi.getTitle());
Picasso.get()
.load(lajmi.getImage())
.resize(400, 300)
.onlyScaleDown()
.into(holder.mImage);
}
public void setLajmi(List<Lajmi> lajmiList) {
mLajmiList = lajmiList;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
if(mLajmiList == null)
return 0;
else
return mLajmiList.size();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
}
When I open NewsActivity, I expect all the images to show on the item of RecyclerView without doing a scroll first.
When you are scrolling the list, it gets the updated items and hence gets the updated list of images which you fetched from your server. I think you are missing a notifyDataSetChanged call here.
#Override
public void onResponse(JSONObject response) {
String imageUrl = Query.shfaqFoton(response);
if (imageUrl == ""){
imageUrl = String.valueOf(R.drawable.news_photo1);
}
lajmi.setImage(imageUrl);
// Add the notifyDataSetChanged here
mAdapter.notifyDataSetChanged();
}
However, in this case, as you are fetching your images one by one, each time you are fetching an image, the adapter will be notified and hence the RecyclerView will be reloaded accordingly. Its kind of annoying in the case when you are scrolling through items and the list gets to the top each time a new image is fetched from the server as you are calling notifyDataSetChanged() each time.
I would like to suggest implementing the whole image fetching a bit differently. Get the count of the images to be downloaded first. Then get all the images URLs and save them in your list. Then call notifyDataSetChanged once you are finished downloading all the image URLs.
Hope that helps!
notifyDataSetChanged(): Notifies the attached observers that the underlying data has been changed and any View reflecting the data set should refresh itself.(https://developer.android.com/reference/android/widget/BaseAdapter)
If you are fetching images one by one, refreshing the view might not be the best solution. If you don't want it to scroll to top of the page whenever a new image downloaded, you can use notifyItemRangeInserted(int positionStart, int itemCount)
Also please have a look at this question
I have an ImageView in a ListView, the ImageView has a default image and while rendering, I asynchronously download images and load them into ImageViews. It works perfectly, but when I scroll down two items and scroll back, then the default images in the first and second row changing to the image of the sixth and seventh row.
I read these topics, but didn't find the solution.
Image change when i scroll gridview
My images changing during scroll in listview android
ListView images changing During Scroll
public class RssAdapter extends BaseAdapter {
private final List<SyndEntry> items;
private static Context context;
private static Map<String, Bitmap> mBitmapCache = new HashMap<String, Bitmap>();
public RssAdapter(Context context, List<SyndEntry> items) {
this.items = items;
this.context = context;
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int position) {
return items.get(position);
}
#Override
public long getItemId(int id) {
return id;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = new ViewHolder();
if (convertView == null) {
convertView = View.inflate(context, R.layout.rss_item, null);
holder.itemTitle = (TextView) convertView.findViewById(R.id.itemTitle);
holder.itemPubDate = (TextView) convertView.findViewById(R.id.itemPubDate);
holder.itemImg = (ImageView) convertView.findViewById(R.id.itemImg);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.itemTitle.setText(items.get(position).getTitle());
holder.itemPubDate.setText(items.get(position).getPublishedDate().toString());
List<SyndEnclosure> encls = items.get(position).getEnclosures();
if(!encls.isEmpty()){
holder.imageUrl = encls.get(0).getUrl();
}
if (!encls.isEmpty() && holder.imageUrl != null && !holder.imageUrl.equals("null")) {
holder.setImage(holder.imageUrl);
}
return convertView;
}
private static class ViewHolder {
TextView itemTitle;
TextView itemPubDate;
ImageView itemImg;
String imageUrl;
public void setImage(String imageUrl) {
this.imageUrl = imageUrl;
Bitmap imageBitmap = mBitmapCache.get(imageUrl);
if(imageBitmap!=null){
itemImg.setImageBitmap(imageBitmap);
} else {
AsyncHttpClient client = new AsyncHttpClient();
client.get(imageUrl, null, fileHandler);
}
}
FileAsyncHttpResponseHandler fileHandler = new FileAsyncHttpResponseHandler(context) {
#Override
public void onFailure(int statusCode, Header[] headers, Throwable throwable, File file) {
}
#Override
public void onSuccess(int statusCode, Header[] headers, File response) {
Bitmap imageBitmap = BitmapFactory.decodeFile(response.getPath());
itemImg.setImageBitmap(imageBitmap);
mBitmapCache.put(imageUrl, imageBitmap);
}
};
}
I tried to set a boolean variable to check whether my image was set or not and also tried to use SharedPreferences, but this part of my code didn't run when the image changing.
if(!encls.isEmpty()){
holder.imageUrl = encls.get(0).getUrl();
}
if (!encls.isEmpty() && holder.imageUrl != null && !holder.imageUrl.equals("null")) {
holder.setImage(holder.imageUrl);
}
It seems I solved the problem finally, I post the solution for the future. So, at this part of code
} else {
holder = (ViewHolder) convertView.getTag();
}
the holder object is not that which has to be rendered for the given row. Thus we have to set the parameters of the holder object for the required values. And it was ok, but I didn't set the image related parameters to null if there was no image. So, the two else part were missing:
if(!encls.isEmpty()){
holder.imageUrl = encls.get(0).getUrl();
} else {
holder.imageUrl = null;
}
if (!encls.isEmpty() && holder.imageUrl != null && !holder.imageUrl.equals("null")) {
holder.setImage(holder.imageUrl);
} else {
holder.itemImg.setImageResource(R.mipmap.news_icon);
}
There are two activities: Main and Detail Activity.
Main Activity is basically a GridView.
Detail Activity is basically shows the clicked item's detail information. I am passing selected item's id (pid) from the Main to the Detail Activity.
I am facing an issue as follows. Initially, I have 3G connection (cellular connection) and clicked on the first item and see the corresponding item detail in the Detail Activity, it works perfectly fine, and go back to the Main Activity, then clicked on the second item, then unfortunately it still shows me the first item in the DetailActivity that I clicked initially.
I switched from 3g to wifi while app is on the active and open. No matter what I click, it still shows me the first item that I clicked initially.
But when I delete the app and reinstall it and get either wifi access only, the app works perfectly fine.
In the following implementation, Connection URL (PRODUCT_DETAIL_URL) is http, not https. I am using Volley library for the network connection.
DetailActivity.java
private void productDetailInit() {
// it is http
StringRequest postRequest = new StringRequest(Request.Method.POST, Config.PRODUCT_DETAIL_URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
jsonObject = response;
loadJsonData();
} catch (Exception e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
}
) {
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("id", productID);
return params;
}
};
RetryPolicy policy = new DefaultRetryPolicy(1000, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
postRequest.setRetryPolicy(policy);
CustomVolleyRequest.getInstance(this).getRequestQueue().add(postRequest);
}
CustomVolleyRequest.java
public class CustomVolleyRequest {
private static CustomVolleyRequest customVolleyRequest;
private static Context context;
private RequestQueue requestQueue;
private ImageLoader imageLoader;
private CustomVolleyRequest(Context context) {
this.context = context;
this.requestQueue = getRequestQueue();
imageLoader = new ImageLoader(requestQueue,
new ImageLoader.ImageCache() {
private final LruCache<String, Bitmap>
cache = new LruCache<String, Bitmap>(20);
#Override
public Bitmap getBitmap(String url) {
return cache.get(url);
}
#Override
public void putBitmap(String url, Bitmap bitmap) {
cache.put(url, bitmap);
}
});
}
private class BitmapCache implements ImageLoader.ImageCache {
private LruCache<String, Bitmap> mCache;
public BitmapCache() {
mCache = new LruCache<>(20);
}
#Override
public Bitmap getBitmap(String url) {
return mCache.get(url);
}
#Override
public void putBitmap(String url, Bitmap bitmap) {
// scaling bitmap for avoiding too much big images
Bitmap scaled = ImageUtil.getInstance().scaleBitmap(bitmap);
mCache.put(url, scaled);
}
}
public static synchronized CustomVolleyRequest getInstance(Context context) {
if (customVolleyRequest == null) {
customVolleyRequest = new CustomVolleyRequest(context);
}
return customVolleyRequest;
}
public RequestQueue getRequestQueue() {
if (requestQueue == null) {
Cache cache = new DiskBasedCache(context.getCacheDir(), 10 * 1024 * 1024);
Network network = new BasicNetwork(new HurlStack());
requestQueue = new RequestQueue(cache, network);
requestQueue.start();
}
return requestQueue;
}
public ImageLoader getImageLoader() {
return imageLoader;
}
}
Adapter.java
class ProductMainAdapter extends ArrayAdapter<ImageRecord> {
private ImageLoader mImageLoader;
private String jsonObject;
ProductMainAdapter(Context context) {
super(context, R.layout.grid_item);
mImageLoader = CustomVolleyRequest.getInstance(this.getContext()).getImageLoader();
}
#NonNull
#Override
public View getView(final int position, View convertView, #NonNull ViewGroup parent) {
final ViewHolder holder;
if(convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(getContext()).inflate(R.layout.grid_item, parent, false);
convertView.setBackgroundResource(R.drawable.round_gridview);
holder.priceTagImage = (ImageView) convertView.findViewById(R.id.priceTag_IV);
holder.textView = (TextView) convertView.findViewById(R.id.text);
holder.imageView = (NetworkImageView) convertView.findViewById(R.id.picture);
holder.priceTagRL = (RelativeLayout) convertView.findViewById(R.id.priceTag_RL);
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
ImageRecord imageRecord = getItem(position);
holder.imageView.setImageUrl(imageRecord != null ? imageRecord.getUrl() : null, mImageLoader);
holder.imageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openProductDetail(position);
}
});
holder.textView.setText(imageRecord != null ? imageRecord.getTitle() : null);
holder.priceTagRL.setRotation(0);
return convertView;
}
private class ViewHolder{
TextView textView;
ImageView priceTagImage;
NetworkImageView imageView;
RelativeLayout priceTagRL;
}
private void openProductDetail(int position) {
try {
ImageRecord imr = getItem(position);
String productID = imr != null ? imr.getId() : "0";
Intent intent = new Intent(getContext(), ProductDetailActivity.class);
intent.putExtra("pid", productID);
getContext().startActivity(intent);
} catch (Exception e) {
Log.e("openProductDetail", "exception", e);
}
}
I have created an app with a listview with arrayadapter that the user can dynamically populate. I store the info entered as a json string in the preferences and when I refresh the app, I get the list with the entries. The thing is that I want the image next to each entry to change after a network operation. The problem I'm facing seems to be that since the elements in the list are added dynamically, I dont seem to find a good way neither to update the imageview on the onPostExecute() method, either to be able to target each entry specifically since they share the same layout ids.
Here is my getView() method inside my adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.product_list_item, null);
holder = new ViewHolder();
holder.deviceName = (TextView) convertView
.findViewById(R.id.txt_pc_name);
holder.deviceIp = (TextView) convertView
.findViewById(R.id.txt_pdt_desc);
holder.devicePort = (TextView) convertView
.findViewById(R.id.txt_pdt_price);
holder.favoriteImg = (ImageView) convertView
.findViewById(R.id.imgbtn_favorite);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Devices device = (Devices) getItem(position);
holder.deviceName.setText(device.getName());
holder.deviceIp.setText(device.getIpOnline());
holder.devicePort.setText(device.getPortOnline() + "");
return convertView;
}
Here is my AsyncTask:
public class Connection extends AsyncTask<String, Void, String> {
private String ipOnline;
private int portOnline;
private String ipWol;
private int portWol;
private String macAddress;
private ImageView img;
private Context context;
public interface AsyncResponse {
void processFinish(String output);
}
public AsyncResponse delegate = null;
public Connection(Context mContext, ImageView Img,String IpOnline, int PortOnline, String IpWol, int PortWol, String MacAddress, AsyncResponse delegate) {
ipOnline = IpOnline;
portOnline = PortOnline;
ipWol = IpWol;
portWol = PortWol;
macAddress = MacAddress;
context = mContext;
// inflate = Inflate;
img = Img;
// spin = spinner;
this.delegate = delegate;
}
public int status;
#Override
protected String doInBackground(String... arg0) {PreferenceManager.getDefaultSharedPreferences(lastContext);
try {
Socket echoSocket = new Socket();
echoSocket.connect(new InetSocketAddress(ipOnline,portOnline),2000);
if(echoSocket.isConnected())
status = 1;
} catch (Exception e) {
status = 0;
}
if(status == 0)
return "0";
else
return "1";
}
#Override
protected void onProgressUpdate(Void... values) {}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
ImageView img = (ImageView) activity.findViewById(R.id.imgbtn_favorite);
if (status == 0)
img.setImageResource(android.R.drawable.presence_offline);
else
img.setImageResource(android.R.drawable.presence_online);
delegate.processFinish(result);
}
}
And here is my call to it:
new Connection(activity, img, product.getIpOnline(), Integer.parseInt(product.getPortOnline()), product.getIpWol(), Integer.parseInt(product.getPortWol()), product.getMacAddress(), new Connection.AsyncResponse() {
#Override
public void processFinish(String output) {
}
}).execute();
You can use view holder pattern or recyclerview .
You need to store the reference of image view in holder and can update the image with the help of this reference instead of id of view
I came up with a solution.
I created a public variable on my adapter and I'm adding all the images:
public List<ImageView> allImages = new ArrayList<ImageView>();
public List<ImageView> getAllImages(){
return this.allImages;
}
this.allImages.add((ImageView) convertView
.findViewById(R.id.imgbtn_favorite));
Then on my fragmented activity onCreateView method I deployed a delayed runnable:
(new Handler()).postDelayed(new Runnable() {
#Override
public void run() {
updateStatus();
}
}, 500);
the updateStatus() method initializes the images variable and begins network checks to determine which image to use. Then it applies it accordingly.
public void updateStatus() {
List<ImageView> images = deviceListAdapter.getAllImages();
if(count > 0 && images.size() > 0) {
for (int i = 0; i < deviceListAdapter.getCount() ; i++) {
Devices product = (Devices) deviceListAdapter.getItem(i);
if((TextUtils.isDigitsOnly(product.getPortOnline()) && TextUtils.isDigitsOnly(product.getPortWol())) && (!product.getPortOnline().isEmpty() && !product.getPortWol().isEmpty())) {
new Connection(activity, images.get(i), product.getIpOnline(), Integer.parseInt(product.getPortOnline()), product.getIpWol(), Integer.parseInt(product.getPortWol()), product.getMacAddress(), new Connection.AsyncResponse() {
#Override
public void processFinish(Boolean output) {
}
}).execute();
}
}
}
}
It might not be optimal but feel free to add a better solution.