Okay, so far my MainActivity for this simple app looks like this:
public class MainActivity extends AppCompatActivity {
ListView moviesListView;
ArrayList <Movie> moviesList;
MoviesAdapter moviesAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
moviesListView = (ListView) findViewById(R.id.moviesListView);
moviesList = new ArrayList<Movie>();
runProgram();
moviesAdapter = new MoviesAdapter(getApplicationContext(), R.layout.movies_layout, moviesList);
moviesListView.setAdapter(moviesAdapter);
}
private void runProgram() {
String url = "https://api.themoviedb.org/3/" +
"discover/movie?primary_release_date.gte=2016-02-01&primary_release_date.lte=2016-02-18" +
"&api_key=...............................";
JsonObjectRequest jsObjRequest = new JsonObjectRequest
(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
getMovies(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// TODO Auto-generated method stub
}
});
// Access the RequestQueue through your singleton class.
MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);
}
private void getMovies(JSONObject response) {
JSONArray jsonArray = null;
String imageBaseURL = "http://image.tmdb.org/t/p/w500";
try {
jsonArray = response.getJSONArray("results");
for (int i = 0; i < 5; i++){
Movie movie = new Movie(jsonArray.getJSONObject(i).getString("title"),
jsonArray.getJSONObject(i).getString("overview"),
imageBaseURL + jsonArray.getJSONObject(i).getString("poster_path")
+ "?&api_key=6b54bd769063ac68b99fac57aa334eae");
moviesList.add(movie);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
What I am trying to do here is request movies from TMDB, and display them in a ListView. But my problem is that the first time you run the app, it doesn't display anything, you would only see white screen.
Now, if you try running it again, it would display the movies in the ListView. But if you clear the cache of the application and try running it one more time, you would see the white screen again... . I am really confused of what is going on here.
I've tried debugging it, and it seems like it is getting the data really fast, so whatever is happening is happening after that.
Also, here is the code of my Adapter in case:
public class MoviesAdapter extends ArrayAdapter<Movie> {
ArrayList<Movie> moviesList;
int adapterResource;
Context adapterContext;
LayoutInflater layoutInflator;
public MoviesAdapter(Context context, int resource, ArrayList<Movie> objects) {
super(context, resource, objects);
moviesList = objects;
adapterResource = resource;
adapterContext = context;
layoutInflator = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder viewHolder;
if (convertView == null){
convertView = layoutInflator.inflate(adapterResource, null);
viewHolder = new ViewHolder();
viewHolder.movieImage = (ImageView) convertView.findViewById(R.id.movieImage);
viewHolder.movieName = (TextView) convertView.findViewById(R.id.movieName);
viewHolder.movieOverview = (TextView) convertView.findViewById(R.id.movieOverview);
convertView.setTag(viewHolder);
}
else {
viewHolder = (ViewHolder) convertView.getTag();
}
new DownloadImageTask(viewHolder.movieImage).execute(moviesList.get(position).getImageURL());
viewHolder.movieName.setText(moviesList.get(position).getName());
viewHolder.movieOverview.setText(moviesList.get(position).getOverview());
return convertView;
}
public static class ViewHolder{
public ImageView movieImage;
public TextView movieName;
public TextView movieOverview;
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
public DownloadImageTask(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap mIcon11 = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
mIcon11 = BitmapFactory.decodeStream(in);
} catch (Exception ex) {
Log.e("Error", ex.getMessage());
ex.printStackTrace();
}
return mIcon11;
}
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
}
Any help would be much appreciated.
Each time your getView() is called, you would start a new DownloadTask. That is simply wrong because the same image may already be downloaded/in the process of being downloaded
Use Universal Image loader or Picasso :
Instead of this :
new DownloadImageTask(viewHolder.movieImage).execute(moviesList.get(position).getImageURL());
Use :
Picasso.with(context)
.load(moviesList.get(position).getImageURL())
.into(viewHolder.movieImage);
Also, it seems that your getMovies() function is executed on main thread. Since you are parsing a JSON here, move it to background thread by using an AsyncTask
Thirdly, after you do getMovies(response);, you need to call notifyDataSetChange() on your moviesAdapter
Related
This is my code:
public class GetAllCategoriesListViewAdapter extends BaseAdapter{
private JSONArray dataArray;
private Activity activity;
private static final String baseUrlForCategoryImage = "link here";
private static LayoutInflater inflater = null;
public GetAllCategoriesListViewAdapter(JSONArray jsonArray, Activity a){
this.dataArray = jsonArray;
this.activity = a;
inflater = (LayoutInflater) this.activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return this.dataArray.length();
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ListCell cell;
if(convertView == null){
convertView = inflater.inflate(R.layout.get_all_categories_list_view_cell, null);
cell = new ListCell();
cell.category_name = (TextView) convertView.findViewById(R.id.category_name);
cell.category_image = (ImageView) convertView.findViewById(R.id.category_image);
cell.category_image.setTag(cell);
convertView.setTag(cell);
}else{
cell = (ListCell) convertView.getTag();
}
try{
JSONObject jsonObject = this.dataArray.getJSONObject(position);
cell.category_name.setText(jsonObject.getString("category_name"));
String nameOfImage = jsonObject.getString("category_image");
String urlForImageInServer = baseUrlForCategoryImage + nameOfImage;
new AsyncTask<String, Void, Bitmap>(){
protected Bitmap doInBackground(String... params){
String url = params[0];
Bitmap icon = null;
try{
InputStream in = new java.net.URL(url).openStream();
icon = BitmapFactory.decodeStream(in);
}catch (MalformedURLException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
return icon;
}
#Override
protected void onPostExecute(Bitmap result) {
cell.category_image.setImageBitmap(result);
}
}.execute(urlForImageInServer);
}catch (JSONException e){
e.printStackTrace();
}
return convertView;
}
private class ListCell{
private ImageView category_image;
private TextView category_name;
}
}
The code gets the images from my webhost and place it in every cell in my listvew. The problem is everytime I scroll, the images are shuffled and returns in few seconds. How to stop the images from changing when I scroll? I tried to use the solution on other post but it won't work. Please help.
Looks like you are new to android. So you are fetching the images in the getView method. The getView method is called every time a new list item is drawn. So For every image, a new request is made to internet. SO that will be a lot of requests . You should firstly get your images and get them in some ArryayList . Then pass that Arraylist to your adapter. Here is tutorial for you
Using AsyncTask
http://www.devexchanges.info/2015/04/android-custom-listview-with-image-and.html
Using Volley
https://www.androidhive.info/2014/07/android-custom-listview-with-image-and-text-using-volley/
Go for Volley for better performance. Cheers!
I have a listView where I put a Title and a description with a picture and the pictures reloads when I scroll up, how could I resolve this problem? I tried some solutions but they failed...
Here is my code:
GETVIEW
public class ViewHolder extends ArrayAdapter<ListViewModel> {
public ViewHolder(Context context, List<ListViewModel> model) {
super(context, 0, model);
}
#Override
public View getView(int position, View cView, ViewGroup parent) {
if(cView == null){
cView = LayoutInflater.from(getContext()).inflate(R.layout.view_list_holder,parent, false);
}
ListViewHolder viewHold = (ListViewHolder) cView.getTag();
if(viewHold == null){
viewHold = new ListViewHolder();
viewHold.title = (TextView) cView.findViewById(R.id.title);
viewHold.content = (TextView) cView.findViewById(R.id.content);
viewHold.picture = (ImageView) cView.findViewById(R.id.picture);
cView.setTag(viewHold);
}
ListViewModel model = getItem(position);
viewHold.title.setText(model.getTitle());
viewHold.content.setText(model.getContent());
if (viewHold.picture != null)
new DownloadImageTask(viewHold.picture).execute(model.getPicture());
return cView;
}
private class ListViewHolder{
public TextView title;
public TextView content;
public ImageView picture;
}
}
ImageDownloader
public class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
public DownloadImageTask(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap myImage = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
myImage = BitmapFactory.decodeStream(in);
} catch (Exception e) {
e.printStackTrace();
}
return myImage;
}
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
Try using Volley instead of AsyncTask:
http://developer.android.com/training/volley/request.html
It has built in support for image caching as well as cancelling requests if a particular set of images are no longer needed if the user has scrolled away.
Could you not just save the images in a bitmap array in the activity? Then just display those. Therefore they never reload, they are just in activity to be used in the view?
I have a ListView of elements composed with ImageView. I get a new image using an AsyncTask and in the onPostExecute(Object result) method I set the image using setImageUri(Uri uri) but it doesn't gets updated.
If I change of activity or between apps, image is shown perfectly, but I want to show the image immediately.
I tried calling invalidate() with all the combinations of the ImageView, the extended BaseAdapter, the parent ListView, but nothing worked. I tried many other techniques like calling setImageResource(0), setImageUri(null), but no results...
EDITED:
Here, part of the code:
public class ThingItemAdapter extends BaseAdapter {
protected List<Thing> things;
LayoutInflater inflater;
public ThingItemAdapter(Context context, List<Thing> things) {
this.things = things;
this.inflater = LayoutInflater.from(context);
}
#Override
public int getCount() {
return things.size();
}
#Override
public Thing getItem(int position) {
return things.get(position);
}
#Override
public long getItemId(int position) {
return things.get(position).getId();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final int pos = position;
final ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = this.inflater.inflate(R.layout.thing_list_item, parent, false);
holder.thingImageView = (ImageView) convertView.findViewById(R.id.thing_preview);
holder.button = (ImageButton) convertView.findViewById(R.id.apply_button);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final Thing thing = things.get(position);
final long thingId = thing.getId();
final Uri thingUri = thing.getPicture();
holder.thingImageView.setImageURI(thingUri);
holder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// generate new file
final TypedFile typedFile = new TypedFile("multipart/form-data", new File(thingUri.getPath()));
new ReadAndStorePictureTask()
.execute(new Object[] { typedFile, holder.thingImageView, thing });
}
});
// item detailed view listener
holder.thingImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent((ThingApplication) ThingApplication.getContext(), ThingActivity.class);
intent.putExtra(ThingActivity.EXTRA_THING_ID, thingId);
context.startActivity(intent);
}
});
return convertView;
}
private class ViewHolder {
ImageView thingImageView;
ImageButton button;
}
private class ReadAndStorePictureTask extends AsyncTask<Object, Void, Void> {
ImageView imageView;
Thing thing;
ViewGroup parent;
protected Void doInBackground(Object... params) {
final TypedFile typedFile = (TypedFile) params[0];
imageView = (ImageView) params[1];
thing = (Thing) params[2];
((ThingApplication) ThingApplication.getContext()).getClient().apply(typedFile,
new Callback<Response>() {
#Override
public void failure(RetrofitError error) {
...
}
#Override
public void success(Response nothing, Response response) {
try {
byte[] bytes = ThingApplication.getBytesFromStream(response.getBody().in());
Uri newImageURI = Uri.parse("uri://valid_uri"); // whatever, it exists in real code
thing.setPicture(newImageURI);
File file = ((ThingApplication) ThingApplication.getContext())
.getFileFromURI(newImageURI); // this method works
ThingApplication.saveBytesToFile(bytes, file.getAbsolutePath());
thingService.storeThing(thing);
} catch (Exception e) {
...
}
}
});
return null;
}
#Override
protected void onPostExecute(Void result) {
imageView.setImageURI(thing.getPicture());
// force redraw. FIXME not working
/*
* ANSWER HERE, PLEASE
*/
}
}
}
How can I show the updated URI immediately inside onPostExecute(Object result) method?
onPostExecute you update the list of images that it's linked to the ListView adapter and after that you notify the adapter that you changed the items in the list by calling:
adapter.notifyDataSetChanged();
You can do something like this:
-Change third parameter in asynctask call.
new ReadAndStorePictureTask().execute(
new Object[] { typedFile, holder.thingImageView, pos });
-Then, modify the list items inside asynctask and refresh.
private class ReadAndStorePictureTask extends AsyncTask<Object, Void, Void> {
ImageView imageView;
int position;
ViewGroup parent;
protected Void doInBackground(Object... params) {
final TypedFile typedFile = (TypedFile) params[0];
imageView = (ImageView) params[1];
position = (Integer) params[2];
((ThingApplication) ThingApplication.getContext()).getClient().apply(typedFile,
new Callback<Response>() {
#Override
public void failure(RetrofitError error) {
...
}
#Override
public void success(Response nothing, Response response) {
try {
byte[] bytes = ThingApplication.getBytesFromStream(response.getBody().in());
Uri newImageURI = Uri.parse("uri://valid_uri"); // whatever, it exists in real code
things.get(position).setPicture(newImageURI);
File file = ((ThingApplication) ThingApplication.getContext())
.getFileFromURI(newImageURI); // this method works
ThingApplication.saveBytesToFile(bytes, file.getAbsolutePath());
thingService.storeThing(things.get(position));
} catch (Exception e) {
...
}
}
});
return null;
}
#Override
protected void onPostExecute(Void result) {
notifyDataSetChanged();
}
}
Good luck!
Someone, help me with this problem. i have stuck in 3 days because this problem -_-!
i can make listview with image and text using volley library, its works in emulator (i use genymotion emulator) the image and text show. but when i run it in my device (android Jelly 4.3.0) the listview is empty. the layer is blank (empty). i don't know why.
heres my piece code
public class DaftarBarang_Layout extends Activity{
private List<Produk> produkList = new ArrayList<Produk>();
private ListView listView;
private CustomListAdapter adapter;
private ProgressDialog pDialog;
private ServerRequest serverRequest;
JSONArray member = null;
private static final String url = "http://192.168.117.1:808/Koen_CI/index.php/daftar_barang_control";
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.daftarbarang_layout);
listView = (ListView) findViewById(R.id.list);
adapter = new CustomListAdapter(this, produkList);
listView.setAdapter(adapter);
btnBack = (Button)findViewById(R.id.btnBackDaftarBarang);
setBehavior();
// Creating volley request obj
JsonArrayRequest produkReq = new JsonArrayRequest(url,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
Log.d("TAG", response.toString());
hidePDialog();
// Parsing json
for (int i = 0; i < response.length(); i++) {
try {
JSONObject obj = response.getJSONObject(i);
Produk produk = new Produk();
produk.setNamaProduk(obj.getString("nama_produk"));
produk.setHargaProduk(obj.getString("harga_produk"));
produk.setFotoProduk(obj.getString("foto_produk"));
Log.d("TAG", "TAG : " + produk.getNamaProduk());
// adding movie to movies array
produkList.add(produk);
} catch (JSONException e) {
e.printStackTrace();
}
}
// notifying list adapter about data changes
// so that it renders the list view with updated data
adapter.notifyDataSetChanged();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d("TAG", "Error: " + error.getMessage());
hidePDialog();
}
});
// Adding request to request queue
AppController.getInstance().addToRequestQueue(produkReq);
}
im sure the url is fine, and image return is http://192.168.117.1:808/Koen_CI/gambarbaju/batik.jpg.
the problem is, why in real device the listview is not show, but in emulator the listview is show..
sorry for my bad english, but thanks anyway.. :)
heres my CustomListAdapter:
public class CustomListAdapter extends BaseAdapter {
private Activity activity;
private LayoutInflater inflater;
private List<Produk> produkItems;
ImageLoader imageLoader = AppController.getInstance().getImageLoader();
public CustomListAdapter(Activity activity, List<Produk> produkItems) {
this.activity = activity;
this.produkItems = produkItems;
}
#Override
public int getCount() {
return produkItems.size();
}
#Override
public Object getItem(int location) {
return produkItems.get(location);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (inflater == null)
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null)
convertView = inflater.inflate(R.layout.list_row, null);
if (imageLoader == null)
imageLoader = AppController.getInstance().getImageLoader();
NetworkImageView thumbNail = (NetworkImageView) convertView
.findViewById(R.id.thumbnail);
TextView title = (TextView) convertView.findViewById(R.id.title);
TextView rating = (TextView) convertView.findViewById(R.id.rating);
// getting movie data for the row
Produk p = produkItems.get(position);
// thumbnail image
thumbNail.setImageUrl(p.getFotoProduk(), imageLoader);
// title
title.setText(p.getNamaProduk());
// rating
rating.setText("Harga: " + p.getHargaProduk());
return convertView;
}
Add this code to your onCreate or init method.
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
If this doesn't work please post logcat showing warning or error.
Im trying to make a lisView with two textView and an imageView (that come as a url Sting) on each item at the list but, the list is not scrolling as good as I want, because its taking too long to load the image url.
Im using an AsyncTask class for loading the the image but still it dosent look so good.
here is my code at int the ArrayAdapter class:
public class MySimpleArrayAdapter extends ArrayAdapter<Movie> {
final private Context context;
final private Movie[] movies;
ImageView movieIcon;
TextView name, description;
Bitmap bitmap;
public MySimpleArrayAdapter(Context context, Movie[] movies) {
super(context,R.layout.item_in_movielist, movies);
this.context = context;
this.movies = movies;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.item_in_movielist, parent, false);
name = (TextView) rowView.findViewById(R.id.tvMovieName);
description = (TextView) rowView.findViewById(R.id.tvMovieDescription);
movieIcon = (ImageView) rowView.findViewById(R.id.ivMovieIcon);
GetImageAsync getImageAsync = new GetImageAsync();
getImageAsync.imageView = movieIcon;
name.setText(movies[position].getMovieName());
description.setText(movies[position].getMovieDescription());
getImageAsync.execute(position);
return rowView;
}
public class GetImageAsync extends AsyncTask<Integer, Void, Bitmap> {
public ImageView imageView;
#Override
protected void onPostExecute(Bitmap bitmap1) {
imageView.setImageBitmap(bitmap1);
}
#Override
protected Bitmap doInBackground(Integer... params) {
URL url = null;
try {
url = new URL(movies[params[0]].getMovieImgURL());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
return BitmapFactory.decodeStream(input);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
}
I understood that this is not the way to do that, I`m looking for changing my code into "Paging" and I want to do it right.
any tips what can I do ?
P.S
If you can show me how to add Paging to this code it will be great.
Thanks!
Picasso.with(mContext)
.load(img.get(pos).replaceAll(" ", "%20"))
.placeholder(R.drawable.ic_launcher)
.error(R.drawable.ic_launcher)
.noFade().resize(70, 70)
.into(v.image);