1

i'm trying to figure out how to modify a certain ImageView or TextView widget in a CardView after i created it and passed it to the main RecyclerView.

So my code is as follows :

First the class that holds the items :

 public class MovieDetails {
    protected static String title;
    protected static Bitmap imageViewPoster;
    protected static Bitmap imageViewFanart;}

Now i have the RecyclerView Adapter that also holds the ViewHolder inner class like this :

public class MovieDetailsAdapter extends RecyclerView.Adapter<MovieDetailsAdapter.MovieViewHolder> {

       private List<MovieDetails> movieList;

public MovieDetailsAdapter(List<MovieDetails> movietList) {

    this.movieList = movietList;
}

@Override
public int getItemCount() {

    return movieList.size();
}

@Override
public MovieViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
    View itemView = LayoutInflater.
            from(viewGroup.getContext()).
            inflate(R.layout.card_layout_movies, viewGroup, false);

    return new MovieViewHolder(itemView);
}

@Override
public void onBindViewHolder(MovieViewHolder movieViewHolder, int i) {

    MovieDetails md = movieList.get(i);

    movieViewHolder.vTitle.setText(md.title);      
    movieViewHolder.vPoster.setImageBitmap(md.imageViewPoster);
    movieViewHolder.vFanart.setImageBitmap(md.imageViewFanart);

}


public static class MovieViewHolder extends RecyclerView.ViewHolder {

    protected TextView vTitle;
    protected ImageView vPoster;
    protected ImageView vFanart;

    public MovieViewHolder(View v)
    {
        super(v);
        vTitle = (TextView)v.findViewById(R.id.title);
        vPoster = (ImageView) v.findViewById(R.id.imageViewPoster);
        vFanart = (ImageView) v.findViewById(R.id.imageViewFanart);

    }
}} 

And now my main Activity class, where i am setting the layout and view and creating the MovieList array of objects.

public class MoviesListActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_movies_list);
    RecyclerView recList = (RecyclerView) findViewById(R.id.cardList);
    recList.setHasFixedSize(true);
    LinearLayoutManager llm = new LinearLayoutManager(this);
    llm.setOrientation(LinearLayoutManager.VERTICAL);
    recList.setLayoutManager(llm);

    MovieDetailsAdapter ca = new MovieDetailsAdapter(createList(5));
    recList.setAdapter(ca);

}

private List<MovieDetails> createList(int size){

    List<MovieDetails> moviedetailsList =  new ArrayList<MovieDetails>();

    for (int i=1; i <= size; i++) {
        MovieDetails moviedetails = new MovieDetails();
        moviedetails.title= "Name" +i;

        Bitmap bit = resizeBitmap(getResources(), R.drawable.poster_example, 640, 955);
        moviedetails.imageViewPoster = bit;

        Bitmap bit2 = resizeBitmap(getResources(), R.drawable.fanart_example, 800, 450);
        moviedetails.imageViewFanart = bit2;

        moviedetailsList.add(moviedetails);
    }
    return  moviedetailsList;
}

resizeBitmap is another function i use for resizing the images, irrelevant to the question, i edited it out.

Now, one i've created this CardView list, is there any way to modify the ImageView and TextViews inside it from the main Activity class ? I want to to this since i`m using AsyncTask to retrieve the new images i want to replace, so i first have to initialize the Bitmaps with some "default" images, and then later one modify them from the results of the AsyncTask used to retrieve them from the server.

Searched around google, found dozens of tutorials around CardViews but nothing regarding this, any help is appreciated, thanks.

Alex
  • 127
  • 3
  • 12

1 Answers1

1

The CardView does not care. It is just like a LinearLayout. It does not need any different treatment than a standard RecyclerView.

First, you need to have and modify only one List<MovieDetails> and only one MovieDetailsAdapter for this to work. (You can keep your work in a temporary one if you need).

So, you should make the List public static in the code of the adapter.

Then in the Activity, instead of

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    MovieDetailsAdapter ca = new MovieDetailsAdapter(createList(5));
    ...
}

do

private MovieDetailsAdapter ca;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    ca = new MovieDetailsAdapter(createList(5));
    ...
}

Move the createList's generic MovieDetails (using the size) to another method (I'll call it makeGenericMovieDetails()). (In the createList, just replace this in the for loop.)

private MovieDetails makeGenericMovieDetails() {
    MovieDetails moviedetails = new MovieDetails();
    // The size of the list, but add one.
    moviedetails.title= "Name" + (adapter.getSize() + 1);

    Bitmap bit = resizeBitmap(getResources(), R.drawable.poster_example, 640, 955);
    moviedetails.imageViewPoster = bit;

    Bitmap bit2 = resizeBitmap(getResources(), R.drawable.fanart_example, 800, 450);
   moviedetails.imageViewFanart = bit2; 
return moviedetails;
}

Now when you want to change it, do this (once you resized it):

private void setMovieDetails(@Nullable Bitmap poster, @Nullable Bitmap fanart, @Nullable String title, int id) { 
    MovieDetails details;
    boolean isAlreadyThere = true;
    try {
        details = ca.movieList.get(id);
    } catch (IndexOutOfBoundsException e) {
        isAlreadyThere = false;
        // If we come with a bad ID we crash. No fun.
        details = makeGenericMovieDetails();
    }
    if (poster != null)
        details.imageViewPoster = poster;
    if (fanart != null)
        details.imageViewFanart = fanart;
    if (title != null)
        details.Title = title;
    ca.movieList.set(id, details);

    if (isAlreadyThere) {
        ca.notifyItemChanged(id);
    } else {
        ca.notifyItemInserted(id);
    }
}

The key line here is

ca.notifyItemChanged(id);

which tells the adapter to update that item.

I hope this works, I am doing this from StackExchange's app off hand and it keeps losing the draft, so test first.

Now, when you want to change the pic, just call setMovieDetails(poster, fanart, title, id); and pass null for a value you do not want to change. (Except id).

BTW try to make variables start with a lowercase letter. So instead of Title do something like title or mTitle. It looks better and makes it easier to distinguish.

EasyasPi
  • 430
  • 8
  • 8
  • Thanks a lot for the reply. Well, it works and it kinda doesn't. Ok, so i changed the list to static and public and defined the Adapter (MovieDetailsAdapter) globally like you said, and renamed the old createList to makeGenericMovieDetails(). But then came an issue in your setMovieDetails method, namely the `details = makeGenericMovieDetails(); ` part. This can't be assigned since details is an object (MovieDetails class object) while 'makeGenericMovieDetails()' is a List (list of objects). Doesn't make sense, and why is it in the "catch" part of the code? – Alex Sep 11 '15 at 03:32
  • So i edited it out since i presume you wanted to write something else. And it kinda does work, in the sense that when i try to update an entry at a specific ID, the entire list of cards get updated with the same bitmaps, and the application starts to lag (hundreds of dropped frames in the logcat) – Alex Sep 11 '15 at 03:32
  • Also, looking at the documentation (http://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#notifyItemChanged(int)) i've found this method, notifyItemChanged (position) , which i tried instead of ca.notifyDataSetChanged();, and it behaves weird as well , if i set it to position number 2, it will update all cards after position number 2 (with the same lagging effect described in my earlier post) – Alex Sep 11 '15 at 03:47
  • @Alex Oh nice. I am too new with RecyclerView adapters and I am used to `notifyDataSetChanged()` See edit. – EasyasPi Sep 11 '15 at 21:02
  • I think you forgot to edit. I've found a nice piece tutorial in the meantime, i`m trying to implement it and i`ll be back with the results in 30 minutes. – Alex Sep 11 '15 at 21:31
  • Ok, so you're code makes sense theoretically (i now understand what you meant by `details = makeGenericMovieDetails();` , but it doesn't work properly. It updates the card at position X and then all of the cards after it, the application starts to lag with dropped frames. But i managed to get it working using another tutorial, need to move some code around since that tutorial was using encapsulated classes for the MovieDetails & List, in the Adapter class, since i can't access resources without passing Context from a non-Activity class, i can't update the ImageViews. – Alex Sep 12 '15 at 00:10
  • I'll add the final code tomorrow afternoon so you can see it, i marked you answer as correct, thanks a lot. – Alex Sep 12 '15 at 00:11
  • I found out the lag. It seems to be an issue with it creating a new ViewHolder. See [here](https://stackoverflow.com/questions/30667014/why-recyclerview-notifyitemchanged-will-create-a-new-viewholder-and-use-both-t) – EasyasPi Sep 12 '15 at 18:13
  • Well, i added a Log to my on bind and on create methods in the adapter, and it's not creating 1 additional ViewHolder, but 2 of them. I get 3 ViewHolders created when making the generic movie details, and then another 1 when updating a card, then when i scroll over the new modified card, it re-binds again, each time i scroll over it. It also seems to only lag when scrolling over the modified card, when going down in the list it works normally. Adding a new card also creates the duplicate ViewHolder and the lag. Worst part is that `setItemAnimator(null);` doesn`t seem to fix it, same behaviour. – Alex Sep 12 '15 at 20:50
  • Does disabling the animation fix it for you? – Alex Sep 12 '15 at 20:51
  • Interesting thing. If i use TextViews instead of ImageViews for the poster and fanart (just some random text in them), no lag. It seems that the issue is coming from the imageViews, i`ll try to experiment with this new library i found (http://square.github.io/picasso/) to re-size and load images, instead of the methods i was using. Maybe that is causing it. – Alex Sep 12 '15 at 23:04
  • Yeah, I heard Picasso is pretty good for image manipulation like that. I hope it works out and is the cause of the lag. – EasyasPi Sep 13 '15 at 22:13
  • Well good news, apparently that was the issue, using Picasso to load the images did solve the lag, even if it's still creating another ViewHolder, the lag is gone , so i'm good for the moment. Thanks a lot for the assistance. – Alex Sep 14 '15 at 23:22