I'm building on a tutorial I did in which I created a RecyclerView screen with cards with selectable options. I want one of the selectable options to bring the user to a new activity that has more information & options about the card they selected. My problem is when I try to transfer traits of that specific card to the next SlideViewActivity.java activity I am unable to successfully do so. I tried transforming my list into an array then sending that, but I keep obtaining a null value (which could be due to my syntax for all I know).
Any clarification & guidance would be appreciated, let me know if you would want any of the other code as well.
public class Adapter extends RecyclerView.Adapter<Adapter.MyViewHolder> {
private Context mContext;
private List<Properties> dogList;
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView title, count;
public ImageView thumbnail, overflow;
public MyViewHolder(View view) {
super(view);
title = (TextView) view.findViewById(R.id.title);
count = (TextView) view.findViewById(R.id.count);
thumbnail = (ImageView) view.findViewById(R.id.thumbnail);
overflow = (ImageView) view.findViewById(R.id.overflow);
}
}
public Adapter(Context mContext, List<Properties> dogList) {
this.mContext = mContext;
this.dogList = dogList;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.card, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
Properties dog = dogList.get(position);
holder.title.setText(dog.getName());
// loading dog cover using Glide library
Glide.with(mContext).load(dog.getThumbnail()).into(holder.thumbnail);
holder.overflow.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
showPopupMenu(holder.overflow);
}
});
}
/**
* Showing popup menu when tapping on icon
*/
private void showPopupMenu(View view) {
// inflate menu
PopupMenu popup = new PopupMenu(mContext, view);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.menu, popup.getMenu());
popup.setOnMenuItemClickListener(new MyMenuItemClickListener());
popup.show();
}
/**
* Click listener for popup menu items
*/
class MyMenuItemClickListener implements PopupMenu.OnMenuItemClickListener {
public MyMenuItemClickListener() {
}
#Override
public boolean onMenuItemClick(MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.action_add_favourite:
Toast.makeText(mContext, "Add to favourite", Toast.LENGTH_SHORT).show();
return true;
case R.id.action_more_info:
Intent slideStart = new Intent(mContext, SlideViewActivity.class);
String[] dogArray = new String[dogList.size()];
slideStart.putExtra("List", dogArray);
Log.e("putting extra", String.valueOf(dogArray[0]));
//TODO:MAKE NAME TRANSFERS WORK
mContext.startActivity(slideStart);
return true;
default:
}
return false;
}
}
Adding Properties.java:
public class Properties {
private String name;
private String info;
private int thumbnail;
public Properties() {
}
public Properties(String name, String info, int thumbnail) {
this.name = name;
this.info = info;
this.thumbnail = thumbnail;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getInfo() {
return info;
}
public void getInfo(String info) {
this.info = info;
}
public int getThumbnail() {
return thumbnail;
}
public void setThumbnail(int thumbnail) {
this.thumbnail = thumbnail;
}
}
You can pass ArrayList<T>, if T is Serializable.
for example:
ArrayList<String> list = new ArrayList<String>();
intent.putExtra("list", list);
use getSerializableExtra to extract data
Your array is dogList.size()-d null values.
String[] dogArray = new String[dogList.size()];
slideStart.putExtra("List", dogArray);
You should see null logged here.
Log.e("putting extra", String.valueOf(dogArray[0]));
It's not clear what type of class you have for Properties, but if it is your class, and not java.util.Properties, you should implement Parcelable on that class, then you'd have
intent.putParcelableArrayList("List", dogList)
(Tip: You should rename that class to Dog to avoid confusion for yourself and others)
But if you are just worried about getting null, case matters. You put a key="List", so make sure you aren't getting key="list" or anything else but "List"
Two ways possible for that::
First
While Sending the list using intent::
Intent slideStart = new Intent(mContext, SlideViewActivity.class);
slideStart.putExtra("List", dogList);
mContext.startActivity(slideStart);
In This just pass the list as is it is ::
But your class Properties should be implements "Serializable"
Now while receiving that intent ::
ArrayList<Properties> dogList =(ArrayList<Properties>) getIntent().getExtras().getSerializable("List");
Second way :: using Library
One library is there for converting string to arraylist & vice versa ,
compile 'com.google.code.gson:gson:2.4'
So, for this while sending list convert that list to string like ::
Type listType = new TypeToken<List<Properties>>() {
}.getType();
String listString=new Gson().toJson(dogList,listType );
pass this simple as string with intent:::
Intent slideStart = new Intent(mContext, SlideViewActivity.class);
slideStart.putExtra("List", listString);
mContext.startActivity(slideStart);
And while getting it back in other activity::
String listString =getIntent().getExtras().getString("List");
Type listType = new TypeToken<List<Properties>>() {}.getType();
List<Properties> list=new Gson().fromJson(listString, listType);
Tell me if need more help..
I have done a similar project I will share my code. Please take a look and if have doubts ping me
DataAdapter.java
package com.example.vishnum.indiavideo;
import android.content.Context;
import android.content.Intent;
import android.provider.MediaStore;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import java.util.ArrayList;
import java.util.List;
public class DataAdapter extends RecyclerView.Adapter<DataAdapter.ViewHolder> {
private Context context;
List<Video_Details> video;
public DataAdapter(List<Video_Details> video, Context context) {
super();
this.context = context;
this.video = video;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.card_row, parent, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final Video_Details videoDetails = video.get(position);
String url;
final String VideoID;
holder.title.setText(video.get(position).getTitle());
VideoID= video.get(position).getV_id();
url = video.get(position).getThumb();
Glide.with(context)
.load(url)
.override(150,70)
.into(holder.thumb);
//viewHolder.thumb.setText(android.get(i).getVer());
// viewHolder.tv_api_level.setText(android.get(i).getApi());
holder.vm.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "You Clicked"+video.get(position).getV_id(), Toast.LENGTH_SHORT).show();
Intent intent = new Intent(v.getContext(),Play_Video.class);
intent.putExtra("VideoId",(video.get(position).getV_id()));
intent.putExtra("Title",(video.get(position).getTitle()));
v.getContext().startActivity(intent);
}
}
);
}
#Override
public int getItemCount() {
return video.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
public TextView title;
public ImageView thumb;
public String videoid;
public View vm;
public ViewHolder(View view) {
super(view);
vm = view;
title = (TextView)view.findViewById(R.id.title);
thumb = (ImageView) view.findViewById(R.id.thumb);
//tv_version = (TextView)view.findViewById(R.id.tv_version);
//tv_api_level = (TextView)view.findViewById(R.id.tv_api_level);
}
}
}
Video_Details.java
package com.example.vishnum.indiavideo;
public class Video_Details {
private String id;
private String v_id;
private String title;
private String thumb;
public String getId() {
return id;
}
public String getV_id() {
return v_id;
}
public String getTitle() {
return title;
}
public String getThumb() {
return (Constants.urlvideo+v_id+"/0.jpg");
}
public void setId(String id) {
this.id = id;
}
public void setV_id(String v_id) {
this.v_id = v_id;
}
public void setTitle(String title) {
this.title = title;
}
public void setThumb(String v_id) {
this.thumb =Constants.urlvideo+v_id+"/0.jpg";
}
}
Related
I bought a movie app from Codecanyon. The support team screwed up, they can't fix the problem. I'm having trouble with letters like ç, ş, ğ, ü (Space) while searching for movies in the app. When I add a space between a two-word movie, all movies come off the screen.
https://youtube.com/shorts/Z9SOkbIa5kk
Home.java
Searchlistaddepter.java
Searchlist.java
Below
----------------------------------------------------------------------------
HOME.java
---------------------------------------------------------------------------
void searchContent(String text) {
RequestQueue queue = Volley.newRequestQueue(this);
StringRequest sr = new StringRequest(Request.Method.GET, AppConfig.url +"searchContent/"+text+"/"+onlyPremium, response -> {
if(!response.equals("No Data Avaliable") ) {
JsonArray jsonArray = new Gson().fromJson(response, JsonArray.class);
List<SearchList> searchList = new ArrayList<>();
for (JsonElement r : jsonArray) {
JsonObject rootObject = r.getAsJsonObject();
int id = rootObject.get("id").getAsInt();
String name = rootObject.get("name").getAsString();
String year = "";
if(!rootObject.get("release_date").getAsString().equals("")) {
year = getYearFromDate(rootObject.get("release_date").getAsString());
}
String poster = rootObject.get("poster").getAsString();
int type = rootObject.get("type").getAsInt();
int status = rootObject.get("status").getAsInt();
int contentType = rootObject.get("content_type").getAsInt();
if (status == 1) {
searchList.add(new SearchList(id, type, name, year, poster, contentType));
}
}
RecyclerView searchLayoutRecyclerView = findViewById(R.id.Search_Layout_RecyclerView);
SearchListAdepter myadepter = new SearchListAdepter(context, searchList);
searchLayoutRecyclerView.setLayoutManager(new GridLayoutManager(context, 3));
searchLayoutRecyclerView.setAdapter(myadepter);
} else {
View bigSearchLottieAnimation = findViewById(R.id.big_search_Lottie_animation);
RecyclerView searchLayoutRecyclerView = findViewById(R.id.Search_Layout_RecyclerView);
bigSearchLottieAnimation.setVisibility(View.VISIBLE);
searchLayoutRecyclerView.setVisibility(View.GONE);
}
}, error -> {
// Do nothing because There is No Error if error It will return 0
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String,String> params = new HashMap<>();
params.put("x-api-key", AppConfig.apiKey);
return params;
}
#Override
protected Map<String,String> getParams(){
Map<String,String> params = new HashMap<>();
params.put("search",text);
params.put("onlypremium", String.valueOf(onlyPremium));
return params;
}
};
queue.add(sr);
}
---------------------------------------------------------
SEARCHLİSTADDEPTER.Java
----------------------------------------------------------
package com.xxxx.android.adepter;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.xxxx.android.AppConfig;
import com.xxxx.android.R;
import com.xxxx.android.MovieDetails;
import com.xxxx.android.list.SearchList;
import com.xxxx.android.WebSeriesDetails;
import java.util.List;
public class SearchListAdepter extends RecyclerView.Adapter<SearchListAdepter.MyViewHolder> {
private Context mContext;
private List<SearchList> mData;
Context context;
public SearchListAdepter(Context mContext, List<SearchList> mData) {
this.mContext = mContext;
this.mData = mData;
}
#NonNull
#Override
public SearchListAdepter.MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
View view;
LayoutInflater mInflater = LayoutInflater.from(mContext);
view = mInflater.inflate(AppConfig.contentItem,parent,false);
return new SearchListAdepter.MyViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull SearchListAdepter.MyViewHolder holder, #SuppressLint("RecyclerView") int position) {
holder.setTitle(mData.get(position));
holder.setYear(mData.get(position));
holder.setImage(mData.get(position));
holder.IsPremium(mData.get(position));
holder.Movie_Item.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(mData.get(position).getContent_Type() == 1) {
Intent intent = new Intent(mContext, MovieDetails.class);
intent.putExtra("ID", mData.get(position).getID());
mContext.startActivity(intent);
} else if(mData.get(position).getContent_Type() == 2) {
Intent intent = new Intent(mContext, WebSeriesDetails.class);
intent.putExtra("ID", mData.get(position).getID());
mContext.startActivity(intent);
}
}
});
}
#Override
public int getItemCount() {
return mData.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView Title;
TextView Year;
ImageView Thumbnail;
View Premium_Tag;
CardView Movie_Item;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
Title = (TextView) itemView.findViewById(R.id.Movie_list_Title);
Year = (TextView) itemView.findViewById(R.id.Movie_list_Year);
Thumbnail = (ImageView) itemView.findViewById(R.id.Movie_Item_thumbnail);
Premium_Tag = (View) itemView.findViewById(R.id.Premium_Tag);
Movie_Item = itemView.findViewById(R.id.Movie_Item);
}
void IsPremium(SearchList type) {
if(type.getType() == 1) {
Premium_Tag.setVisibility(View.VISIBLE);
} else {
Premium_Tag.setVisibility(View.GONE);
}
}
void setTitle(SearchList title_text) {
Title.setText(title_text.getTitle());
}
void setYear(SearchList year_text) {
Year.setText(year_text.getYear());
}
void setImage(SearchList Thumbnail_Image) {
Glide.with(context)
.load(Thumbnail_Image.getThumbnail())
.placeholder(R.drawable.thumbnail_placeholder)
.into(Thumbnail);
}
}
}
------------------------------------------
SEARCHLİST.Java
---------------------------------------
package com.xxxx.android.list;
public class SearchList {
private int ID;
private int Type;
private String Title;
private String Year;
private String Thumbnail;
private int Content_Type;
public SearchList(int ID, int type, String title, String year, String thumbnail, int content_Type) {
this.ID = ID;
Type = type;
Title = title;
Year = year;
Thumbnail = thumbnail;
Content_Type = content_Type;
}
public int getID() {
return ID;
}
public void setID(int ID) {
this.ID = ID;
}
public int getType() {
return Type;
}
public void setType(int type) {
Type = type;
}
public String getTitle() {
return Title;
}
public void setTitle(String title) {
Title = title;
}
public String getYear() {
return Year;
}
public void setYear(String year) {
Year = year;
}
public String getThumbnail() {
return Thumbnail;
}
public void setThumbnail(String thumbnail) {
Thumbnail = thumbnail;
}
public int getContent_Type() {
return Content_Type;
}
public void setContent_Type(int content_Type) {
Content_Type = content_Type;
}
public void onResponse(String response) {
try{ byte[] u = response.toString().getBytes("ISO-8859-1");
response = new String(u, "UTF-8");
}
catch (Exception e){
e.printStackTrace();
}
}
}
Update: One of the problems is solved: Now updateList is resolved, the problem was that I defined mAdapter as RecyclerView.Adapter instead of MyAdapter. But now even though I am getting data, nothing shows up on the list, it's empty
--------------------ORIGINAL POST--------------------
I want to update my RecyclerView using DiffUtil to prevent duplicates.
I have 4 classes: The User class, the Activity class where I set data, the Adapter class and the DiffUtil class. I am not sure I combine all these 4 correctly.
This is the User class:
public class User {
private String mUserId;
private Uri mImageUrl;
public User(String userId, String imageUrl) {
mUserId = userId;
mImageUrl = Uri.parse(imageUrl);
}
public String getUserId() {
return mUserId;
}
public Uri getImageUrl() {
return mImageUrl;
}
}
This is how I set data dynamically (I keep getting new Json arrays from the server containing user id's to be displayed, then I set the user image from Firebase storage): (It's a function invoked by an onClick listener:)
This is the method call from the fragment:
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
updateUsersList();
}
});
This is the function:
private void updateUsersList() {
#Override
public void onResponse(JSONArray response) { // the JSON ARRAY response of user ids ["uid1", "uid334", "uid1123"]
myDataset.clear(); // clear dataset to prevent duplicates
for (int i = 0; i < response.length(); i++) {
try {
String userKey = response.get(i).toString(); // the currently iterated user id
final DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference userKeyRef = rootRef.child("users").child(userKey); // reference to currently iterated user
ValueEventListener listener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
myDataset.add(new User(dataSnapshot.getKey(), dataSnapshot.child("imageUrl").getValue().toString())); //add new user: id and image url
mAdapter.updateList(myDataset); // cannot resolve this method, why?
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage());
}
};
userKeyRef.addListenerForSingleValueEvent(listener);
}
catch (JSONException e) { Log.d(TAG, "message " + e); }
}
}
This is how my DiffUtil class looks like:
public class MyDiffUtilCallBack extends DiffUtil.Callback{
ArrayList<User> oldUsers;
ArrayList<User> newUsers;
public MyDiffUtilCallBack(ArrayList<User> newUsers, ArrayList<User> oldUsers) {
this.newUsers = newUsers;
this.oldUsers = oldUsers;
}
#Override
public int getOldListSize() {
return oldUsers.size();
}
#Override
public int getNewListSize() {
return newUsers.size();
}
#Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return oldUsers.get(oldItemPosition).getUserId().equals( newUsers.get(newItemPosition).getUserId());
}
#Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return oldUsers.get(oldItemPosition).equals(newUsers.get(newItemPosition));
}
#Nullable
#Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
//you can return particular field for changed item.
return super.getChangePayload(oldItemPosition, newItemPosition);
}
}
And this is my adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private ArrayList<User> mDataset;
private MyViewHolder myHolder;
private User user;
public static class MyViewHolder extends RecyclerView.ViewHolder {
public TextView singleItemTextView;
public ImageView singleItemImage;
public View layout;
public ConstraintLayout constraintLayout;
public MyViewHolder(View v) {
super(v);
layout = v;
singleItemImage = (ImageView) v.findViewById(R.id.icon);
singleItemTextView = (TextView) v.findViewById(R.id.singleitemtv);
constraintLayout = (ConstraintLayout) v.findViewById(R.id.nbConstraintLayout);
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(ArrayList<User> myDataset) {
mDataset = myDataset;
}
// Create new views (invoked by the layout manager)
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.nb_image_view, parent, false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
myHolder = holder;
user = mDataset.get(position);
Uri userImage = user.getImageUrl();
myHolder.singleItemTextView.setText(user.getUserId());
Glide.with(myHolder.itemView.getContext() /* context */)
.load(userImage)
.into(myHolder.singleItemImage);
myHolder.constraintLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(v.getContext(), DisplayUserActivity.class);
context.startActivity(intent);
}
});
}
public void updateList(ArrayList<User> newList) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new MyDiffUtilCallBack(this.mDataset, newList));
diffResult.dispatchUpdatesTo(this);
}
}
I am not sure I combine all the classes correctly (my first time using DiffUtil), and I also get cannot resolve method updateList(?)
What am I doing wrong?
This is how I define mAdapter in my Fragment:
public class MyFragment extends Fragment {
private ArrayList<User> myDataset;
private RecyclerView.Adapter mAdapter;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
rootView = inflater.inflate(R.layout.fragment_lks, container, false);
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.my_recycler_view);
myDataset = new ArrayList<User>();
mAdapter = new MyAdapter(myDataset);
The problem comes from definition of mAdapter. You defined it as RecyclerView.Adapter which is super class of your MyAdapter and it does not contain updateList(). You should change it as following:
private MyAdapter mAdapter;
Updated 1/13/2019:
I've revised your adapter with AsyncListDiffer which calculates the diffrence asynchronously then applies it to the adapter.
MyAdapter.java
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.constraint.ConstraintLayout;
import android.support.v7.recyclerview.extensions.AsyncListDiffer;
import android.support.v7.util.DiffUtil;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import java.util.List;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private AsyncListDiffer<User> mAsyncListDiffer;
public static class MyViewHolder extends RecyclerView.ViewHolder {
public TextView singleItemTextView;
public ImageView singleItemImage;
public View layout;
public ConstraintLayout constraintLayout;
public MyViewHolder(View v) {
super(v);
layout = v;
singleItemImage = (ImageView) v.findViewById(R.id.icon);
singleItemTextView = (TextView) v.findViewById(R.id.singleitemtv);
constraintLayout = (ConstraintLayout) v.findViewById(R.id.nbConstraintLayout);
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter() {
DiffUtil.ItemCallback<User> diffUtilCallback = new DiffUtil.ItemCallback<User>() {
#Override
public boolean areItemsTheSame(#NonNull User newUser, #NonNull User oldUser) {
return newUser.getUserId().equals(oldUser.getUserId());
}
#Override
public boolean areContentsTheSame(#NonNull User newUser, #NonNull User oldUser) {
return newUser.equals(oldUser);
}
};
mAsyncListDiffer = new AsyncListDiffer<>(this, diffUtilCallback);
}
// Create new views (invoked by the layout manager)
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.nb_image_view, parent, false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
User user = mAsyncListDiffer.getCurrentList().get(position);
Uri userImage = user.getImageUrl();
holder.singleItemTextView.setText(user.getUserId());
Glide.with(holder.itemView.getContext() /* context */)
.load(userImage)
.into(holder.singleItemImage);
holder.constraintLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(v.getContext(), DisplayUserActivity.class);
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return mAsyncListDiffer.getCurrentList().size();
}
public void updateList(List<User> newList) {
mAsyncListDiffer.submitList(newList);
}
}
User.java
public class User {
private String mUserId;
private Uri mImageUrl;
public User(String userId, String imageUrl) {
mUserId = userId;
mImageUrl = Uri.parse(imageUrl);
}
public String getUserId() {
return mUserId;
}
public Uri getImageUrl() {
return mImageUrl;
}
#Override
public boolean equals(Object other) {
if (other instanceof User) {
User user = (User) other;
return mUserId.equals(user.getUserId()) && mImageUrl.equals(user.getImageUrl());
} else {
return false;
}
}
}
In addition to #aminography's answer, I suggest you to use ListAdapter, a RecyclerView.Adapter implementation that makes it easier to update you RecyclerView with the correct animations. This class is included in the recyclerview support library.
Below is an example of usage based on your use case:
public class MyAdapter extends ListAdapter<User, UserViewHolder> {
public MyAdapter() {
super(new UserDiffCallback());
}
public UserViewHolder onCreateViewHolder(int position, int viewType) { ... }
public void onBindViewHolder(UserViewModel holder, int position) {
User userAtPosition = getItem(position); // getItem is a protected method from ListAdapter
// Bind user data to your holder...
}
}
public class UserDiffCallback extends DiffUtil.ItemCallback<User> {
#Override
public boolean areItemsTheSame(#NonNull User oldUser, #NonNull User newUser) {
return oldUser.getUserId().equals(newUser.getUserId());
}
#Override
public boolean areContentsTheSame(#NonNull User oldUser, #NonNull User newUser) {
// No need to check the equality for all User fields ; just check the equality for fields that change the display of your item.
// In your case, both impact the display.
return oldUser.getUserId().equals(newUser.getUserId())
&& (oldUser.getImageUrl() == null) ? newUser.getImageUrl() == null : oldUser.getImageUrl().equals(newUser.getImageUrl());
}
}
Then, when you need to update the list with new users, call myAdapter.submitList(newList). Juste like with AsyncListDiffer, the diff between the two list is calculated on a background Thread.
Modify your method:
public void updateList(ArrayList<User> newList) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new MyDiffUtilCallBack(this.mDataset, newList));
this.mDataSet.clear()
this.mDataSet.addAll(newList)
diffResult.dispatchUpdatesTo(this);
}
Looks like
public void onBindViewHolder(#NonNull ViewHolder holder, int position, #NonNull List<Object> payloads) not implemented
Implement this to use DiffUtils properly, as this method will be called for the changes, and based on the payload you can update your recyclerview items instead of calling notifyDataSetChanged()
I am making a news app using Firebase. I have a problem when I try to transfer an object to another activity.
An exception:
"java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.fx.fibi/com.example.fx.fibi.DetailActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String com.example.fx.fibi.News.getTitle()' on a null object reference.
Here is my code:
MainActivity:
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.firebase.ui.database.FirebaseRecyclerAdapter;
import com.firebase.ui.database.FirebaseRecyclerOptions;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.Query;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private Context mContext;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.recycle_view);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.setAdapter(firebaseRecyclerAdapter);
}
Query query = FirebaseDatabase.getInstance().getReference().child("Global");
FirebaseRecyclerOptions<News> options = new FirebaseRecyclerOptions.Builder<News>()
.setQuery(query, News.class).build();
final FirebaseRecyclerAdapter<News,NewsViewHolder> firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<News,
NewsViewHolder>(options) {
#NonNull
#Override
public NewsViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.news_row, parent, false);
return new NewsViewHolder(view);
}
#Override
protected void onBindViewHolder(#NonNull NewsViewHolder holder, int position, #NonNull News model) {
holder.setTitle(model.getTitle());
holder.setDesc(model.getDesc());
holder.setImage(getApplicationContext(), model.getImage());
holder.setOnClickListener(new NewsViewHolder.ClickListener() {
#Override
public void onItemClick(View view, int position) {
Toast.makeText(MainActivity.this, "Item clicked at " + position, Toast.LENGTH_SHORT).show();
Intent intent = new Intent(MainActivity.this, DetailActivity.class);
intent.putExtra("news", position);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
#Override
public void onItemLongClick(View view, int position) {
}
});
}
};
#Override
protected void onStart() {
super.onStart();
firebaseRecyclerAdapter.startListening();
}
}
DetailActivity (this class gets NullPointerException at "title.setText(news.getTitle());"
public class DetailActivity extends AppCompatActivity {
TextView title, desc;
ImageView imageView;
News news;
String titleString, descString, image;
Context mContext;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
initCollapsingToolbar();
imageView = findViewById(R.id.thumbnail_image_header);
title = findViewById(R.id.detail_title);
Intent intentThatStartedThisActivity = getIntent();
if (intentThatStartedThisActivity.hasExtra("news")) {
news = getIntent().getParcelableExtra("news");
title.setText(news.getTitle());
desc.setText(news.getDesc());
Picasso.with(mContext).load(image).into(imageView);
}
}
private void initCollapsingToolbar() {
final CollapsingToolbarLayout collapsingToolbarLayout =
(CollapsingToolbarLayout)findViewById(R.id.collapsing_toolbar);
collapsingToolbarLayout.setTitle(" ");
AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appbar);
appBarLayout.setExpanded(true);
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
boolean isShow = false;
int scrollRange = -1;
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (scrollRange == -1) {
scrollRange = appBarLayout.getTotalScrollRange();
}
if (scrollRange + verticalOffset == 0) {
collapsingToolbarLayout.setTitle(getString(R.string.app_name));
isShow = true;
} else if (isShow) {
collapsingToolbarLayout.setTitle(" ");
isShow = false;
}
}
});
}
}
Model:
public class News implements Parcelable {
private String title;
private String desc;
private String image;
public News(String title, String desc, String image) {
this.title = title;
this.desc = desc;
this.image = image;
}
public News (Parcel in) {
String[] data = new String[3];
in.readStringArray(data);
title = data[0];
desc = data[1];
image = data[2];
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public News() {
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStringArray(new String[] {title, desc, image});
}
public static final Parcelable.Creator<News> CREATOR = new Parcelable.Creator<News>() {
#Override
public News createFromParcel(Parcel source) {
return new News(source);
}
#Override
public News[] newArray(int size) {
return new News[size];
}
};
}
Sorry if my question seems to be foolish, this is my first full app.
Thanks!
I added an object list List <News> newsList and changed method onItem click to:
if (position != RecyclerView.NO_POSITION) {
News clickedDataItem = newsList.get(position);
Toast.makeText(MainActivity.this, "Item clicked at " + position, Toast.LENGTH_SHORT).show();
Intent intent = new Intent(MainActivity.this, DetailActivity.class);
intent.putExtra("news", clickedDataItem);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
But now i get "java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object java.util.List.get(int)' on a null object reference"
You can implement java Serializable interface instead of implementing Parcelable in your model class as shown below
public class News implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private String title;
private String desc;
private String image;
public News(String title, String desc, String image) {
this.title = title;
this.desc = desc;
this.image = image;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public News() {
//empty contructor neeeded
}
#Overide
public String toString(){
return super.toString();
}
}
Then you can pass the object the intent like this
News news = new News("title", "desc", "image");
Intent intent = new Intent(StartActivity.this, TargetActivity.class);
intent.putExtra("news", news);
startActivity(intent);
In order to get the object from the intent, you need to cast New class into the Intent results as shown below.
News news = (News)getIntent().getSerializableExtra("news");
In your onBindViewHolder, you put wrong data.
intent.putExtra("news", position); // position is int
which position is an int, put the News object at the position instead.
You can set the value to the intent ....
Intent i = new Intent(FirstScreen.this, SecondScreen.class);
String strName = "value";
i.putExtra("STRING_I_NEED", strName);
startActivity(i);
You can retrive the value in second activity..........
String newString;
if (savedInstanceState == null) {
Bundle extras = getIntent().getExtras();
if(extras == null) {
newString= null;
} else {
newString= extras.getString("STRING_I_NEED");
}
} else {
newString= (String) savedInstanceState.getSerializable("STRING_I_NEED");
}
news = getIntent().getIntExtra("news");
i am working in PopularMovies Project that i used the movies DB
and i was successful fetch the data from the API but when i click the item in RecyclerView it doesn't work.
I have followed the tutorial for udacity
PopularMovies-issue
there is the link of the source code
and this is the adapter
What should I do?
package com.popmov.popmov.popmov
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import com.popmov.popmov.popmov.data.Movie;
import com.squareup.picasso.Picasso;
class MoviesAdapter extends RecyclerView.Adapter<MoviesAdapter.MoviesViewHolder> {
private final Context context;
private Movie[] moviesData;
public static final String BASE_URL_IMAGE = "http://image.tmdb.org/t/p/w342";
private final MoviesAdapterOnClickHandler moviesAdapterOnClickHandler;
public interface MoviesAdapterOnClickHandler {
void onItemClickListener(int id, String title, String imageUrl, String synopsis, double rating, String releaseDate);
}
public MoviesAdapter(MoviesAdapterOnClickHandler clickHandler,Context context) {
moviesAdapterOnClickHandler = clickHandler;
this.context=context;
}
public class MoviesViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public final ImageView imageView;
public MoviesViewHolder(View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.image_view_movie);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
int adapterPosition = getAdapterPosition();
int id = moviesData[adapterPosition].getId();
String title = moviesData[adapterPosition].getTitle();
String imageUrl = moviesData[adapterPosition].getImageUrl();
String synopsis = moviesData[adapterPosition].getSynopsis();
double rating = moviesData[adapterPosition].getRating();
String releaseDate = moviesData[adapterPosition].getReleaseDate();
moviesAdapterOnClickHandler.onItemClickListener(id, title, imageUrl, synopsis, rating, releaseDate);
}
}
#Override
public MoviesViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
Context context = viewGroup.getContext();
int layoutForItem = R.layout.movies_list_item;
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(layoutForItem, viewGroup, false);
return new MoviesViewHolder(view);
}
#Override
public void onBindViewHolder(MoviesViewHolder holder, int position) {
String path = BASE_URL_IMAGE + moviesData[position].getImageUrl();
Picasso.get()
.load(path)
.placeholder(R.mipmap.poster)
.into(holder.imageView);
}
#Override
public int getItemCount() {
if (null == moviesData) {
return 0;
} else {
return moviesData.length;
}
}
}
In your movies_list_item.xml make ImageView clickable to false
android:clickable="false"
As your ImageView clickable property was set to true so your ImageView are absorbing the ClickListener
I am trying to add some string resources to replace Content string but i cannot seem to access them because its a static class. How do i add items to DummyItem from a non static context?
I edited to add a custom context class i seen on another post.
It works now but the custom context class throws a warning - Do not place Android context classes in static fields; this is a memory leak.
Is this actually a memory leak? How? and can i resolve it?
// Custom Context Class
public class MyCustomContext extends Application {
private static Context context;
public void onCreate() {
super.onCreate();
MyCustomContext.context = getApplicationContext();
}
public static Context getAppContext() {
return MyCustomContext.context;
}
}
// Dummy Content Class
public class DummyContent {
public static final List<DummyItem> ITEMS = new ArrayList<>();
public static final Map<String, DummyItem> ITEM_MAP = new HashMap<>(5);
static {
addItem(new DummyItem("1", R.drawable.p1, "Item #1", "Author A", res.MyCustomContext.getAppContext().getString(R.string.ContentA));
addItem(new DummyItem("2", R.drawable.p2, "Item #2", "Author B", res.MyCustomContext.getAppContext().getString(R.string.ContentB));
addItem(new DummyItem("3", R.drawable.p3, "Item #3", "Author C", res.MyCustomContext.getAppContext().getString(R.string.ContentC)));
}
private static void addItem(DummyItem item) {
ITEMS.add(item);
ITEM_MAP.put(item.id, item);
}
public static class DummyItem {
public final String id;
public final int photoId;
public final String title;
public final String author;
public final String content;
public DummyItem(String id, int photoId, String title, String author, String content) {
this.id = id;
this.photoId = photoId;
this.title = title;
this.author = author;
this.content = content;
}
}
}
// List Fragment
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ListFragment;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.BitmapImageViewTarget;
import com.app.test.R;
import com.app.test.DummyContent;
/**
* Shows a list of all available quotes.
*/
public class PortfolioListFragment extends ListFragment {
private Callback callback = dummyCallback;
/**
* A callback interface. Called whenever a item has been selected.
*/
public interface Callback {
void onItemSelected(String id);
}
/**
* A dummy no-op implementation of the Callback interface. Only used when no active Activity is present.
*/
private static final Callback dummyCallback = new Callback() {
#Override
public void onItemSelected(String id) {
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new MyListAdapter());
setHasOptionsMenu(true);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
// notify callback about the selected list item
callback.onItemSelected(DummyContent.ITEMS.get(position).id);
}
/**
* onAttach(Context) is not called on pre API 23 versions of Android.
* onAttach(Activity) is deprecated but still necessary on older devices.
*/
#TargetApi(23)
#Override
public void onAttach(Context context) {
super.onAttach(context);
onAttachToContext(context);
}
/**
* Deprecated on API 23 but still necessary for pre API 23 devices.
*/
#SuppressWarnings("deprecation")
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
onAttachToContext(activity);
}
}
/**
* Called when the fragment attaches to the context
*/
protected void onAttachToContext(Context context) {
if (!(context instanceof Callback)) {
throw new IllegalStateException("Activity must implement callback interface.");
}
callback = (Callback) context;
}
private class MyListAdapter extends BaseAdapter {
#Override
public int getCount() {
return DummyContent.ITEMS.size();
}
#Override
public Object getItem(int position) {
return DummyContent.ITEMS.get(position);
}
#Override
public long getItemId(int position) {
return DummyContent.ITEMS.get(position).id.hashCode();
}
#Override
public View getView(int position, View convertView, ViewGroup container) {
if (convertView == null) {
convertView = LayoutInflater.from(getActivity()).inflate(R.layout.list_item_article, container, false);
}
final DummyContent.DummyItem item = (DummyContent.DummyItem) getItem(position);
((TextView) convertView.findViewById(R.id.article_title)).setText(item.title);
((TextView) convertView.findViewById(R.id.article_subtitle)).setText(item.author);
final ImageView img = (ImageView) convertView.findViewById(R.id.thumbnail);
Glide.with(getActivity()).load(item.photoId).asBitmap().into(new BitmapImageViewTarget(img) {
#Override
protected void setResource(Bitmap resource) {
RoundedBitmapDrawable circularBitmapDrawable = RoundedBitmapDrawableFactory.create(getActivity().getResources(), resource);
circularBitmapDrawable.setCircular(true);
img.setImageDrawable(circularBitmapDrawable);
}
});
return convertView;
}
}
public PortfolioListFragment() {
}
}
Check below code . You can pass a Context when Calling this Class from the activity and than you can acess the String resources .
public class DummyContent {
/**
* An array of sample items.
*/
private Context context ;
public DummyContent(Context context){
this.context = context ;
addStaticItem();
}
public static List<DummyItem> ITEMS = new ArrayList<>();
/**
* A map of sample items. Key: sample ID; Value: Item.
*/
public static Map<String, DummyItem> ITEM_MAP = new HashMap<>(5);
public void addStaticItem(){
addItem(new DummyItem("1", R.drawable.ic_launcher, "Item #1", "Author A", "Content A"));
addItem(new DummyItem("2", R.drawable.ic_launcher, "Item #2", "Author B","Content B"));
addItem(new DummyItem("3", R.drawable.ic_launcher, "Item #3", "Author C", context.getResources().getString(R.string.text_ok) ));
}
private void addItem(DummyItem item) {
ITEMS.add(item);
ITEM_MAP.put(item.id, item);
}
public class DummyItem {
public String id;
public int photoId;
public String title;
public String author;
public String content;
public DummyItem(String id, int photoId, String title, String author, String content) {
this.id = id;
this.photoId = photoId;
this.title = title;
this.author = author;
this.content = content;
}
}
}
Below is your PortfolioListFragment class :
public class PortfolioListFragment extends ListFragment {
private Callback callback = dummyCallback;
/**
* A callback interface. Called whenever a item has been selected.
*/
public interface Callback {
void onItemSelected(String id);
}
/**
* A dummy no-op implementation of the Callback interface. Only used when no active Activity is present.
*/
private static final Callback dummyCallback = new Callback() {
#Override
public void onItemSelected(String id) {
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DummyContent dummyContent = new DummyContent(getContext());
setListAdapter(new MyListAdapter());
setHasOptionsMenu(true);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
// notify callback about the selected list item
callback.onItemSelected(DummyContent.ITEMS.get(position).id);
}
/**
* onAttach(Context) is not called on pre API 23 versions of Android.
* onAttach(Activity) is deprecated but still necessary on older devices.
*/
#TargetApi(23)
#Override
public void onAttach(Context context) {
super.onAttach(context);
onAttachToContext(context);
}
/**
* Deprecated on API 23 but still necessary for pre API 23 devices.
*/
#SuppressWarnings("deprecation")
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
onAttachToContext(activity);
}
}
/**
* Called when the fragment attaches to the context
*/
protected void onAttachToContext(Context context) {
if (!(context instanceof Callback)) {
throw new IllegalStateException("Activity must implement callback interface.");
}
callback = (Callback) context;
}
private class MyListAdapter extends BaseAdapter {
#Override
public int getCount() {
return DummyContent.ITEMS.size();
}
#Override
public Object getItem(int position) {
return DummyContent.ITEMS.get(position);
}
#Override
public long getItemId(int position) {
return DummyContent.ITEMS.get(position).id.hashCode();
}
#Override
public View getView(int position, View convertView, ViewGroup container) {
if (convertView == null) {
convertView = LayoutInflater.from(getActivity()).inflate(R.layout.list_item_article, container, false);
}
final DummyContent.DummyItem item = (DummyContent.DummyItem) getItem(position);
((TextView) convertView.findViewById(R.id.article_title)).setText(item.title);
((TextView) convertView.findViewById(R.id.article_subtitle)).setText(item.author);
final ImageView img = (ImageView) convertView.findViewById(R.id.thumbnail);
Glide.with(getActivity()).load(item.photoId).asBitmap().into(new BitmapImageViewTarget(img) {
#Override
protected void setResource(Bitmap resource) {
RoundedBitmapDrawable circularBitmapDrawable = RoundedBitmapDrawableFactory.create(getActivity().getResources(), resource);
circularBitmapDrawable.setCircular(true);
img.setImageDrawable(circularBitmapDrawable);
}
});
return convertView;
}
}
public PortfolioListFragment() {
}
}
So, you can't use a findViewById(R.string.myString) resource because it isn't final? Not really sure if that's what you are asking or not.