I have a RecyclerView in a Fragment where I would like to populate data using MutableLiveData. I have a mock DAO that returns a list of objects of my Model class. The RecyclerView is blank when executed.
This is my mock DAO class:
public class DaoMockFriends implements DaoFriendsInterface {
private ArrayList<FriendsModel> friendsModelsArrayList;
public DaoMockFriends() {
friendsModelsArrayList = new ArrayList<>();
}
#SuppressLint("NewApi")
#Override
public List<FriendsModel> getFriendsList() {
for(int i = 0 ; i < 10 ; i++){
FriendsModel friendsModel = (new FriendsModel("adasdsada"+i,"sdadsadsa"+(i*2),"adsdsadssad"+(i*3),""+(i++), LocalDateTime.now()));
friendsModelsArrayList.add(friendsModel);
}
return friendsModelsArrayList;
}
}
This is my ViewModel class:
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.ViewModel;
import java.util.List;
public class FriendsListLiveDataProvider extends ViewModel {
private MutableLiveData<List<FriendsModel>> friendsModelLiveData = new MutableLiveData<>();
public LiveData<List<FriendsModel>> getFriendsModelLiveData() {
if(this.friendsModelLiveData == null) friendsModelLiveData.postValue(new DaoMockFriends().getFriendsList());
return this.friendsModelLiveData;
}
}
This is my RecyclerViewAdapter class:
public class AdapterFriendsListRecyclerView extends RecyclerView.Adapter<AdapterFriendsListRecyclerView.ViewHolder> {
private List<FriendsModel> listOfFriends;
public AdapterFriendsListRecyclerView(List<FriendsModel> listOfFriends) {
this.listOfFriends = listOfFriends;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.friend_entiry_block,parent,false));
}
#SuppressLint("SetTextI18n")
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
FriendsModel friendsInterface = this.listOfFriends.get(position);
String firstName = friendsInterface.getFirstName();
String lastName = friendsInterface.getLastName();
holder.getTextView().setText(firstName+" "+lastName);
}
#Override
public int getItemCount() {
return listOfFriends.size();
}
public void addItems(List<FriendsModel> friendsInterface){
this.listOfFriends = friendsInterface;
notifyDataSetChanged();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private TextView textView;
public ViewHolder(View itemView) {
super(itemView);
this.textView = itemView.findViewById(R.id.friendEntityNameTV);
}
public TextView getTextView() {
return textView;
}
}
}
This is the implementation in the Fragment :
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_friends_list, container, false);
friendsListRV = v.findViewById(R.id.FriendsListRecyclerView);
adapterFriendsListRecyclerView = new AdapterFriendsListRecyclerView(new ArrayList<>());
friendsListRV.setLayoutManager(new LinearLayoutManager(getActivity()));
friendsListRV.setAdapter(adapterFriendsListRecyclerView);
friendsListLiveDataProvider = ViewModelProviders.of(this).get(FriendsListLiveDataProvider.class);
friendsListLiveDataProvider
.getFriendsModelLiveData()
.observe(this, (friendsModels) -> adapterFriendsListRecyclerView
.addItems(friendsModels));
return v;
}
I think that somehow the data from the DAO isn't getting across to the RecyclerViewAdapter. I followed this blog.
How to make the data from the DAO visible in the RecyclerView using LiveData
EDIT:
I changed my ViewModel class as per Yogesh's answer. But my app terminates abruptly without any error or warning messages. When I debug, I found that my app crashes on execution of this line: friendsModelLiveData.postValue(new DaoMockFriends().getFriendsList());
EDIT2:
I tried with the code below in my ViewModel implementation class
public LiveData<List<FriendsModel>> getFriendsModelLiveData() {
if(friendsModelLiveData == null){
friendsModelLiveData = new MutableLiveData<>();
}
loadData();
return this.friendsModelLiveData;
}
private void loadData(){
DaoMockFriends anInterface = new DaoMockFriends();
ArrayList<FriendsModel> fm = anInterface.getFriendsList();
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(new Runnable() {
#Override
public void run() {
friendsModelLiveData.postValue(fm);
}
});
}
The app abruptly terminates at this ArrayList<FriendsModel> fm = anInterface.getFriendsList(); line. I followed this thread to come up with this type of approach.
Method new DaoMockFriends().getFriendsList() never gets executed, because you have initialized friendsModelLiveData at declaration.
Update:
public class FriendsListLiveDataProvider extends ViewModel {
private MutableLiveData<List<FriendsModel>> friendsModelLiveData;
public LiveData<List<FriendsModel>> getFriendsModelLiveData() {
if(this.friendsModelLiveData == null) {
friendsModelLiveData = new MutableLiveData<>();
friendsModelLiveData.postValue(new DaoMockFriends().getFriendsList());
}
return this.friendsModelLiveData;
}
}
Related
Recently I have created RecyclerView with Multiple ViewTypes followed this video. In the video the lecturer followed the traditional method of sending data using ArrayList In MainAvtivity. But when I used ViewModel and Retrofit it is not passing any data and nothing show in the screen.
And here an expiation of what I have done.
First:
I have 3 diffrents types of views so I created a 3 model classes for each view called (FirstImageModel,LiveModel,DailyImageModel).
FirstImageModel class -- All other 2 classes have same stretcher.
public class FirstImageModel {
#SerializedName("firstImageUrl")
private String firstImageUrl;
public FirstImageModel(String firstImageUrl) {
this.firstImageUrl = firstImageUrl;
}
public String getFirstImageUrl() {
return firstImageUrl;
}
public void setFirstImageUrl(String firstImageUrl) {
this.firstImageUrl = firstImageUrl;
}
and I have created a fourth class called ItemModel that have type int and Object object.
public class ItemModel {
private int type;
private Object object;
public ItemModel(int type) {
this.type = type;
this.object = object;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
}
Second:
I have created Adapter class called HomeAdapter that extends from RecyclerView and I made 3 classes for eatch view inside the same class HomeAdapter.
public class HomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<ItemModel> items;
public HomeAdapter(List<ItemModel> items) {
this.items = items;
}
public void setHomeList(List<ItemModel> items) {
this.items = items;
notifyDataSetChanged();
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
// Here is types are: 0-FirstImage, 1-Live, 2-DailyImage
if (viewType == 0) {
return new FirstImageViewHolder(
LayoutInflater.from(parent.getContext()).inflate(
R.layout.item_home_container_firstimage,
parent,
false
)
);
} else if (viewType == 1) {
return new LiveViewHolder(
LayoutInflater.from(parent.getContext()).inflate(
R.layout.item_home_container_live,
parent,
false
)
);
} else {
return new DailyImageViewHolder(
LayoutInflater.from(parent.getContext()).inflate(
R.layout.item_home_container_dailyimage,
parent,
false
)
);
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
if (getItemViewType(position) == 0) {
FirstImageModel firstImageModel = (FirstImageModel) items.get(position).getObject();
((FirstImageViewHolder) holder).setFirstImageData(firstImageModel);
} else if (getItemViewType(position) == 1) {
LiveModel liveModel = (LiveModel) items.get(position).getObject();
((LiveViewHolder) holder).setLiveImageData(liveModel);
} else {
DailyImageModel dailyImageModel = (DailyImageModel) items.get(position).getObject();
((DailyImageViewHolder) holder).setDailyImageData(dailyImageModel);
}
}
#Override
public int getItemCount() {
if(this.items != null) {
return this.items.size();
}
return 0;
}
#Override
public int getItemViewType(int position) {
return items.get(position).getType();
}
/* firstImage Adapter */
static class FirstImageViewHolder extends RecyclerView.ViewHolder {
private ImageView firstImage;
FirstImageViewHolder(#NonNull View itemView) {
super(itemView);
firstImage = itemView.findViewById(R.id.image_home_firstImage);
}
void setFirstImageData(FirstImageModel firstImageModel) {
Glide.with(itemView.getContext())
.load(firstImageModel.getFirstImageUrl())
.into(firstImage);
}
}
/* Live Adapter */
static class LiveViewHolder extends RecyclerView.ViewHolder {
private ImageView liveImage;
LiveViewHolder(#NonNull View itemView) {
super(itemView);
liveImage = itemView.findViewById(R.id.image_home_liveImage);
}
void setLiveImageData(LiveModel liveModel) {
Glide.with(itemView.getContext())
.load(liveModel.getImageLiveInfoUrl())
.into(liveImage);
}
}
/* DailyImage Adapter */
static class DailyImageViewHolder extends RecyclerView.ViewHolder {
private ImageView dailyImage;
DailyImageViewHolder(#NonNull View itemView) {
super(itemView);
dailyImage = itemView.findViewById(R.id.image_home_dailyImage);
}
void setDailyImageData(DailyImageModel dailyImageModel) {
Glide.with(itemView.getContext())
.load(dailyImageModel.getDailyImageUrl())
.into(dailyImage);
}
}
My Problem:
When I use Retrofit to load the data, I don't Know how to pass the 3 views types data to the RecyclerView using ViewModel. My ViewModel class.
public class HomeViewModel extends ViewModel {
private MutableLiveData<List<ItemModel>> homeObjectsList;
private Call<List<ItemModel>> call;
public HomeViewModel(){
homeObjectsList = new MutableLiveData<>();
}
public MutableLiveData<List<ItemModel>> getHomeItemsListObserver() {
return homeObjectsList;
}
public void makeApiCallHome() {
APIServiceHome apiServiceHome = RetroInstanceHome.getRetroClientHome().create(APIServiceHome.class);
call = apiServiceHome.getHomeObjectsList();
call.enqueue(new Callback<List<ItemModel>>() {
#Override
public void onResponse(Call<List<ItemModel>> call, Response<List<ItemModel>> response) {
homeObjectsList.postValue(response.body());
}
#Override
public void onFailure(Call<List<ItemModel>> call, Throwable t) {
homeObjectsList.postValue(null);
}
});
}
Also here's my fragment that should dislay the data in RecyclerView.
public class HomeFragment extends Fragment {
View rootView;
RecyclerView recyclerView;
private List<ItemModel> itemModelList;
private HomeViewModel viewModel;
private HomeAdapter adapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_home, container, false);
initializeContent();
initializeViewModel();
return rootView;
}
private void initializeContent() {
recyclerView = rootView.findViewById(R.id.recyclerView_home);
recyclerView.setAdapter(new HomeAdapter(itemModelList));
}
private void initializeViewModel() {
ProgressBar progressBar = rootView.findViewById(R.id.progress_bar_home);
viewModel = ViewModelProviders.of(this).get(HomeViewModel.class);
viewModel.getHomeItemsListObserver().observe(getActivity(), new Observer<List<ItemModel>>() {
#Override
public void onChanged(List<ItemModel> itemModels) {
if(itemModels != null) {
progressBar.setVisibility(View.GONE);
itemModelList = itemModels;
adapter.setHomeList(itemModels);
}
}
});
viewModel.makeApiCallHome();
}
I hope this explanation was clear to show the problem. Waiting for your answer
I have been struggling with one bug for a week now. I am a beginner android developer and I am trying to build MVVM app. The idea is to be gaming news but that does not really matter. Here are my 3 classes that are key to this bug.
My NewsFragment class:
FragmentNewsBinding binding;
NewsFragmentAdapter adapter;
Context context;
public NewsFragmentViewModel newsFragmentViewModel;
public NewsFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
newsFragmentViewModel = new ViewModelProvider(this).get(NewsFragmentViewModel.class);
// Inflating the layout for this fragment
binding = FragmentNewsBinding.inflate(inflater, container, false);
newsFragmentViewModel.init();
setAdapter(container);
observeChanges();
return binding.getRoot();
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
private void setAdapter(ViewGroup container) {
context = container.getContext();
adapter = new NewsFragmentAdapter(newsFragmentViewModel.getItems().getValue(), context);
RecyclerView.LayoutManager linearLayoutManager = new LinearLayoutManager(context);
binding.recyclerViewNews.setLayoutManager(linearLayoutManager);
binding.recyclerViewNews.setAdapter(adapter);
}
public void observeChanges() {
newsFragmentViewModel.getItems().observe(getViewLifecycleOwner(), new Observer<List<Item>>() {
#Override
public void onChanged(List<Item> items) {
adapter.notifyDataSetChanged();
}
});
}
}
NewsFragmentViewModel class:
public class NewsFragmentViewModel extends ViewModel {
private MutableLiveData<List<Item>> mItems;
private NewsRepository mRepo;
// Empty constructor
public NewsFragmentViewModel() {
}
public void init() {
if (mItems != null) {
return;
}
mRepo = NewsRepository.getInstance();
mItems = mRepo.getItems();
}
public LiveData<List<Item>> getItems() {
return mItems;
}
}
And the repository class:
public class NewsRepository {
MutableLiveData<List<Item>> mItems;
private static NewsRepository instance;
public static NewsRepository getInstance() {
if (instance == null) {
instance = new NewsRepository();
}
return instance;
}
//Empty constructor
private NewsRepository() {
}
public MutableLiveData<List<Item>> getItems() {
mItems = new MutableLiveData<>();
ServiceApi.getInstance().getGamezoneMethods().getGamezoneNews().enqueue(new Callback<Rss>() {
#Override
public void onResponse(Call<Rss> call, Response<Rss> response) {
if (response.code() == 200) {
List<Item> items = response.body().getChannel().getItems();
mItems.postValue(items);
}
}
#Override
public void onFailure(Call<Rss> call, Throwable t) {
Log.d("Failed", "Error");
mItems.postValue(null);
}
});
return mItems;
}
}
Now, the main problem with this code is that in the repository when I do my request I successfully get the data and I think, I successfully push the data to the viewmodel. But when I try to get my data from the LiveData in the viewmodel class or in the fragment class I always get null, and because of the null problem I cannot set up an adapter properly and I cannot set up my onChanged() method. I am trying to get the data in the fragment with the classic getValue() method. Please somebody help me because I am losing my mind.
If you are confused with this code, here is link to my github repo.
get API link using retrofit and live data call the first time after some time not updating itself
Call the function on resume in fragment
LiveDataReport used in call retrofit and set the value mutable live
data
Model Fragment used in RecyclerView and adapter
I want get change on anything or updating in get API
immediate change updating RecyclerView list
here my code
Java
public class LiveDataReport {
private static LiveDataReport liveDataReport;
private MutableLiveData<ArrayList<Model>> modelLiveData = new MutableLiveData<>();
private ApiService apiService;
private LiveDataReport() {
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new BasicAuth("user", "pass")).build();
Retrofit retrofit = new Retrofit.Builder()
.client(client)
.baseUrl("http://someipaddress")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create()).build();
apiService = retrofit.create(ApiService.class);
getModelLiveData();
}
public synchronized static LiveDataReport getInstance() {
if (liveDataReport == null)
liveDataReport = new LiveDataReport();
return liveDataReport;
}
public LiveData<ArrayList<Model>> getModelLiveData() {
Call<ArrayList<Model>> arrayListCall = apiService.getLiveDataJson();
arrayListCall.enqueue(new Callback<ArrayList<Model>>() {
#Override
public void onResponse(#NotNull Call<ArrayList<Model>> call, #NotNullResponse<ArrayList<Model>> response) {
if (response.isSuccessful() && response.body() != null) {
System.out.println("//report change success Model");
modelLiveData.postValue(response.body());
}
}
#Override
public void onFailure(#NotNull Call<ArrayList<Model>> call, #NotNull Throwable t) {
System.out.println("//report change failure " + t.toString());
}
});
return modelLiveData;
}
}
public class MainViewModel extends AndroidViewModel {
private LiveData<ArrayList<Model>> liveData ;
public MainViewModel(#NonNull Application application) {
super(application);
liveData=LiveDataReport.getInstance().getModelLiveData();
}
public LiveData<ArrayList<Model>> getLiveData() {
System.out.println("//report call last update : getModelLiveData");
liveData=LiveDataReport.getInstance().getModelLiveData();
return liveData;
}
}
public class ModelFragment extends Fragment implements LifecycleOwner {
private MainViewModel mainViewModel;
private RecyclerView recyclerView;
private FragmentBinding binding;
private LiveDataAdapter adapter;
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment, container, false);
mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
recyclerView = binding.myRecycleView;
adapter = new LiveDataAdapter();
recyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
recyclerView.setHasFixedSize(false);
return binding.getRoot();
}
#Override
public void onResume() {
super.onResume();
if (Utils.isNetWorkConnected(requireContext())) {
mainViewModel.getLiveData().observe(getViewLifecycleOwner(),new Observer<ArrayList<Model>>() {
#Override
public void onChanged(ArrayList<Model> models) {
recyclerView.setAdapter(adapter);
adapter.setModel(models,requireContext());
}
});
} else
Utils.alertCheckNetwork(requireContext());
}
}
public class LiveDataAdapter extends AdapterSkeleton<Model, LiveDataAdapter.CustomViewHolder> {
public LiveDataAdapter(Context context, ArrayList<Model> model, RecyclerView recyclerView, IsCanSetAdapterListener canSetAdapterListener){
this.context=context;
this.items=model;
this.isCanSetAdapterListener=canSetAdapterListener;
measureHeightRecyclerViewAndItem(recyclerView,R.layout.list_model);
}
#NonNull
#Override
public CustomViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
ListModelBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.list_model, parent, false);
return new CustomViewHolder(binding);
}
#Override
public void onBindViewHolder(#NonNull CustomViewHolder holder, int position) {
if(skeletonConfig.isSkeletonIsOn()){
return;
}
else{
holder.binding.appSkeleton.setShowSkeleton(false);
holder.binding.appSkeleton.finishAnimation();
}
Model model = items.get(position);
holder.binding.setModel(reportStatus);
setAnimation(holder.itemView, position);
}
private void setAnimation(View viewToAnimate, int position) {
Animation animation = AnimationUtils.loadAnimation(context, R.anim.item_animation_fall_down);
viewToAnimate.startAnimation(animation);
}
class CustomViewHolder extends RecyclerView.ViewHolder {
ListModelBinding binding;
public CustomViewHolder(#NonNull ListModelBinding itemView) {
super(itemView.getRoot());
binding = itemView;
}
}
}
updating list does not means it will shown into RecyclerView immediately . you have to manually update RecyclerView as well .
to implement this concept , create a method into your RecyclerView Adapter something like this one below
void updateList(List<POJO> newList) {
oldList = newList;
notifyDataChange();
}
so you will see that your RecyclerView will update , i know that we use livedata to do it automatically but in order to update recyclerView you have to approach this concept .
So I've been searching the WHOLE internet for a solution but NONE worked for me sadly.
My goal is to retrieve the data from the cardView which is stored on Firebase and use that data when the cardView is clicked to display it on a new Activity.
I've created "Posts" as a collection, so all of the data such as header, date, image etc are stored inside the "Posts" collection.
From my understanding, what I need to do is to retrieve the key from the cardView data that has been clicked and use it to implement the same data on the new activity using the same mutual key.
I'm stuck here for ages, I would LOVE to get some help.. thanks alot!
My code -
RecyclerAdapter -
public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.ViewHolder> {
public List<ListForPost> list_post;
public Context context;
public PostsAdapter(List<ListForPost> list_post) {
this.list_post = list_post;
}
#NonNull
#Override
public PostsAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.posts_intro, parent, false);
context = parent.getContext();
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final PostsAdapter.ViewHolder holder, int position) {
String header_data = list_post.get(position).getHeader();
holder.setHeaderText(header_data);
String date_data = list_post.get(position).getDate1();
holder.setDateText(date_data);
String image_data = list_post.get(position).getImage_url();
holder.setIntroIMG(image_data);
}
#Override
public int getItemCount() {
return list_post.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private View mView;
private ImageView introIMG;
private TextView headerText;
private TextView dateText;
public ViewHolder(View itemView) {
super(itemView);
mView = itemView;
}
public void setHeaderText(String headText) {
headerText = mView.findViewById(R.id.introHeader);
headerText.setText(headText);
}
public void setDateText(String tarihText) {
dateText = mView.findViewById(R.id.introDate);
dateText.setText(tarihText);
}
public void setIntroIMG (String downloadUri) {
introIMG = (ImageView) mView.findViewById(R.id.introImage);
Glide.with(context).load(downloadUri).into(introIMG);
}
}
}
MainActivity -
firebaseFirestore = FirebaseFirestore.getInstance();
firebaseFirestore.collection("Posts").limit(10).addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(QuerySnapshot queryDocumentSnapshots, FirebaseFirestoreException e) {
for (DocumentChange doc: queryDocumentSnapshots.getDocumentChanges()) {
if (doc.getType() == DocumentChange.Type.ADDED) {
ListForPost listForPost = doc.getDocument().toObject(ListForPost.class);
list_post.add(listForPost);
postsAdapter.notifyDataSetChanged();
}
}
}
});
One solution would be set an OnClickListener for mView inside of your ViewHolder. So for your code that would be something like this:
public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.ViewHolder> {
//...
public class ViewHolder extends RecyclerView.ViewHolder {
private View mView;
private ListForPost mListForPost;
public ViewHolder(View itemView) {
super(itemView);
mView = itemView;
mView.setOnClickListener(new OnClickListener() {
#Override
public void onClick() {
//TODO: launch my activity here with mListForPost
}
});
}
private void setListForPost(ListForPost listForPost) {
mListForPost = listForPost;
}
//...
}
}
If you want to make this even nicer (so you don't have to deal with how to call startActivity() or pass in Context as a parameter to your ViewHolder) you can abstract out the details with another listener, maybe a "ListForPostClickedListener":
public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.ViewHolder> {
//...
private ListForPostClickedListener mListForPostClickedListener;
public PostsAdapter(ListForPostClickedListener listForPostClickedListener) {
mListForPostClickedListener = listForPostClickedListener;
}
public class ViewHolder extends RecyclerView.ViewHolder {
//...
public ViewHolder(View itemView) {
//...
mView.setOnClickListener(new OnClickListener() {
#Override
public void onClick() {
mListForPostClickedListener.onClick(mListForPost);
}
});
}
//...
}
public interface ListForPostClickedListener {
void onClick(ListForPost listForPost);
}
}
And in the Activity, Fragment or whatever other context-aware object you use your PostsAdapter from, you implement the interface and launch the necessary Activity:
public class MyActivityWhichManagesTheAdapter extends Activity implements PostsAdapter.ListForPostClickedListener {
//...
public onClick(ListForPost listForPost) {
//TODO: launch my activity here with mListForPost
}
}
Guys please dont make this question as a duplicate as I have not found any simple or easy implementation of realm in my app. I am in the process of creating a chat app and what I want to do is make it possible for the user to be able to access and read earlier messages that he received even without internet connection.
In short I want my app to be still accessible without an internet connection but am finding it difficult doing that as am new to local data storage.
Below are my codes for your perusal:
Fragment
public class ChatFragment extends Fragment {
public RecyclerView mChatsList;
public View mView;
public List<ChatsModel> mChatsModel;
public ChatsAdapter mChatsAdapter;
private Realm realm;
public ChatFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
mView = inflater.inflate(R.layout.fragment_chat, container, false);
((AppCompatActivity) getActivity()).getSupportActionBar().setShowHideAnimationEnabled(true);
initUI();
realm = Realm.getDefaultInstance();
return mView;
}
//Method and views initializer
public void initUI() {
mChatsList = (RecyclerView) mView.findViewById(R.id.chatsList);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
mChatsModel = new ArrayList<ChatsModel>();
mChatsAdapter = new ChatsAdapter(getActivity(), mChatsModel);
mChatsList.setLayoutManager(layoutManager);
mChatsList.setHasFixedSize(true);
mChatsAdapter.notifyDataSetChanged();
mChatsList.setAdapter(mChatsAdapter);
RecyclerView.ItemAnimator itemAnimator = new DefaultItemAnimator();
itemAnimator.setAddDuration(1000);
itemAnimator.setRemoveDuration(1000);
mChatsList.setItemAnimator(itemAnimator);
prepareItems();
Realm realm = Realm.getInstance(getActivity());
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
ChatsModel chat = realm.createObject(ChatsModel.class);
chat.setUsername("username");
chat.setDate("date");
chat.setPicture("Picture");
}
}, new Realm.Transaction.Callback() {
#Override
public void onSuccess() {
Main.Mess(getString(R.string.real_sucess));
}
#Override
public void onError(Exception e) {
Main.Mess(getString(R.string.real_error));
}
});
}
// This is a simple method to add items to our recyclerview
private void prepareItems() {
Rests mRests = RestService.createService(Rests.class, Session.getToken(getActivity()));
mRests.suggest(new Callback<List<ChatsModel>>() {
#Override
public void success(List<ChatsModel> mChatsModel, Response response) {
RealmList<ChatsModel> mChatsModel2 = new RealmList<ChatsModel>();
mChatsAdapter.setUsers(mChatsModel);
}
#Override
public void failure(RetrofitError error) {
Main.Mess(getString(R.string.server_error));
}
});
}
}
My Adapter
public class ChatsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private RealmList<ChatsModel> mChatsModel;
private Realm realm;
public Activity mActivity;
public ChatsAdapter(#NonNull Activity mActivity) {
super();
this.mChatsModel = new RealmList<>();
this.realm = Realm.getDefaultInstance();
this.mActivity = mActivity;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder (ViewGroup parent, int viewType) {
if(viewType == TYPE_HEADER) {
View v = LayoutInflater.from (parent.getContext ()).inflate (R.layout.ad_view, parent, false);
return new HeaderViewHolder (v);
} else if(viewType == TYPE_ITEM) {
View v = LayoutInflater.from (parent.getContext ()).inflate (R.layout.chats_item, parent, false);
return new ContentViewHolder (v);
}
return null;
}
private ChatsModel getItem (int position) {
return mChatsModel.get (position);
}
#Override
public void onBindViewHolder (RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof HeaderViewHolder) {
HeaderViewHolder headerHolder = (HeaderViewHolder) holder;
if (Constant.SHOW_ADS) {
headerHolder.mAdView.setVisibility(View.VISIBLE);
AdRequest adRequest = new AdRequest.Builder().build();
headerHolder.mAdView.loadAd(adRequest);
} else {
headerHolder.mAdView.setVisibility(View.GONE);
}
}else if (holder instanceof ContentViewHolder) {
ContentViewHolder contentHolder = (ContentViewHolder) holder;
ChatsModel item = getItem (position - 1);
contentHolder.username.setText(item.getUsername());
contentHolder.date.setText(item.getDate());
contentHolder.message.setText(item.getMessage());
Picasso.with(mActivity.getApplicationContext())
.load(Constant.IMAGE_SMALL + item.getPicture())
.error(R.drawable.user)
.into(contentHolder.picture);
}
}
#Override
public int getItemViewType (int position) {
if(isPositionHeader (position)) {
return TYPE_HEADER;
}
return TYPE_ITEM;
}
public void setUsers(RealmList<ChatsModel> friendsItems) {
this.mChatsModel = friendsItems;
notifyDataSetChanged();
}
public List<ChatsModel> getSuggestionsModel() {
return this.mChatsModel;
}
private boolean isPositionHeader (int position) {
return position == 0;
}
#Override
public int getItemCount () {
return mChatsModel.size ();
}
public class HeaderViewHolder extends RecyclerView.ViewHolder {
public AdView mAdView;
public HeaderViewHolder(View itemView) {
super(itemView);
mAdView = (AdView) itemView.findViewById(R.id.ad_view);
}
}
public class ContentViewHolder extends RecyclerView.ViewHolder {
public ImageView picture;
public TextView username, date, message;
public LinearLayout chat;
public ContentViewHolder(View v) {
super(v);
picture = (ImageView) v.findViewById(R.id.picture);
username = (TextView) v.findViewById(R.id.username);
date = (TextView) v.findViewById(R.id.date);
message = (TextView) v.findViewById(R.id.message);
}
}
}
Okay so thats my code.
You can use a SQLite database for storing the chats offline, watch this YouTube tutorial, it is extensive but you can skip through some parts.
When downloading/sending chat messages, mirror them into your database and use the database and ONLY THE DATABASE as the data source for your RecyclerView (never let any online data directly go into the list, always store it in the database first and read it from the database when putting it into your layout).
To improve performance, you can store relevant chat messages in memory (in a separate ArrayList for example) instead of always reading data from the DB that you just wrote into it.
As #geisshirt says in comment, use RealmRecyclerViewAdapter instead of RecyclerView.Adapter. Couple words about android realm adapters you can found in official doc.
Also, you can look at RealmRecyclerViewAdapter example