list is not updating after first call from get API - java

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 .

Related

Android RecyclerView does not work properly with data from retrofit request

I have a screen with CardViews inside of a RecyclerView. The data for each card is retrieved from an ArrayList. As soon as I manually add data to the ArrayList, everything works fine. However, when data is added by an API using retrofit, cards are displayed only if at least one element is manually added to the ArrayList. In addition, if I switch to another menu and then return to a list of cards, only the manually added cards are displayed.
List with cards
Only manually added card is visible
Full code
public class CurrencyRates extends Fragment {
ArrayList<CurrencyModel> currencyModelArrayList;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_currency_rates, container, false);
currencyModelArrayList = new ArrayList<>();
getRates("EUR", "USD");
getRates("USD", "AUD");
getRates("EUR", "GBP");
getRates("EUR", "CNY");
currencyModelArrayList.add(new CurrencyModel("EUR", "CZK", 24.8821));
RecyclerView currencyRV;
currencyRV = view.findViewById(R.id.RVCurrency);
CurrencyAdapter currencyAdapter = new CurrencyAdapter(getActivity().getBaseContext(), currencyModelArrayList);
//cardview two columns
RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(getActivity().getBaseContext(), 2);
currencyRV.setLayoutManager(mLayoutManager);
currencyRV.setAdapter(currencyAdapter);
return view;
}
public void getRates(String baseCurrency, String exchangeCurrency) {
RetrofitInterface retrofitInterface = RetrofitClient.getRetrofitInstance().create(RetrofitInterface.class);
Call<JsonObject> call = retrofitInterface.getData(baseCurrency, exchangeCurrency);
call.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
JsonObject res = response.body();
JsonPrimitive rate = res.getAsJsonObject().getAsJsonPrimitive("conversion_rate");
currencyModelArrayList.add(new CurrencyModel(baseCurrency, exchangeCurrency, Double.parseDouble(rate.toString())));
}
public void onFailure(Call<JsonObject> call, Throwable t) {
}
});
}}
Adapter class:
public class CurrencyAdapter extends RecyclerView.Adapter<CurrencyAdapter.ViewHolder> {
private Context context;
private ArrayList<CurrencyModel> currencyModelArrayList;
public CurrencyAdapter(Context context, ArrayList<CurrencyModel> currencyModelArrayList) {
this.context = context;
this.currencyModelArrayList = currencyModelArrayList;
}
#NonNull
#Override
public CurrencyAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull CurrencyAdapter.ViewHolder holder, int position) {
CurrencyModel model = currencyModelArrayList.get(position);
holder.baseCurrencyTV.setText(model.getBaseCurrency());
holder.exchangeCurrencyTV.setText("/ " + model.getExchangeCurrency());
holder.rateTV.setText(String.valueOf(model.getRate()));
}
#Override
public int getItemCount() {
return currencyModelArrayList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView baseCurrencyTV, exchangeCurrencyTV, rateTV;
public ViewHolder(#NonNull View itemView) {
super(itemView);
baseCurrencyTV = itemView.findViewById(R.id.TVBaseCurrency);
exchangeCurrencyTV = itemView.findViewById(R.id.TVExchangeCurrency);
rateTV = itemView.findViewById(R.id.TVRate);
}
}}
MainActivity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Navigation
BottomNavigationView bottomNavigationView = findViewById(R.id.bottomNavigationView);
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(R.id.currencyRates, R.id.converter).build();
NavController navController = Navigation.findNavController(this, R.id.fragment);
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(bottomNavigationView, navController);
}}
Retrofit:
public class RetrofitClient {
private static Retrofit retrofit;
private static RetrofitClient instance;
public static RetrofitInterface myRetrofitInterface;
public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(RetrofitInterface.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
public interface RetrofitInterface {
String BASE_URL = "https://v6.exchangerate-api.com/";
#GET("v6/API_KEY/pair/{baseCurrency}/{exchangeCurrency}")
Call<JsonObject> getData(#Path("baseCurrency") String baseCurrency,
#Path("exchangeCurrency") String exchangeCurrency);
}
When you add an item to the list you should notify the adapter
public void getRates(String baseCurrency, String exchangeCurrency) {
RetrofitInterface retrofitInterface = RetrofitClient.getRetrofitInstance().create(RetrofitInterface.class);
Call<JsonObject> call = retrofitInterface.getData(baseCurrency, exchangeCurrency);
call.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
JsonObject res = response.body();
JsonPrimitive rate = res.getAsJsonObject().getAsJsonPrimitive("conversion_rate");
currencyModelArrayList.add(new CurrencyModel(baseCurrency, exchangeCurrency, Double.parseDouble(rate.toString())));
CurrencyAdapter.notifyDataSetChanged();// add this line
}
});
}}

getValue() returning null from ViewModel (Java Android)

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.

Using LiveData to manipulate data in RecyclerView from inside a Fragment

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;
}
}

RecyclerView isn't attached to Adapter

I have this strage error. I'm downloading data from API and I want to print it in RecyclerView nested in Fragment called ListFragment. This fragment is one of three which I use in ViewPager. ViewPager works fine. For now, I want to display only two textViews. Layout for them has name single_data_layout. List<Feature> features is my DataResponse. Project builds normaly without issues and I receive in Log that "Data is successfully downloaded". When I run app in DebugMode I get message that my RecyclerView isn't attached to adapter. Any ideas?
ApiClient:
public interface ApiClient {
#GET("/query?format=geojson&starttime&minsig=700")
Call<DataResponse> getData();
}
ApiClientFactory:
public class ApiClientFactory {
public static final String baseURL = "https://earthquake.usgs.gov/fdsnws/event/1/";
public ApiClient createApiClient(){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit.create(ApiClient.class);
}
}
DataAdapter:
public class DataAdapter extends RecyclerView.Adapter<DataAdapter.DataViewHolder> {
private List<Feature> features;
private ListFragment listFragment;
private Context context;
private LayoutInflater inflater;
public DataAdapter(List<Feature> features) {
this.features = features;
}
#Override
public DataViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
inflater = LayoutInflater.from(context);
View contractView = inflater.inflate(R.layout.single_data_layout, parent, false);
return new DataViewHolder(contractView);
}
#Override
public void onBindViewHolder(DataViewHolder holder, int position) {
holder.place.setText(features.get(position).getProperties().getPlace());
holder.alert.setText(features.get(position).getProperties().getAlert());
}
#Override
public int getItemCount() {
return features.size();
}
public static class DataViewHolder extends RecyclerView.ViewHolder {
private final TextView place;
private final TextView alert;
public DataViewHolder(View view) {
super(view);
place = (TextView) view.findViewById(R.id.place_text_view);
alert = (TextView) view.findViewById(R.id.alert_text_view);
}
}
}
ListFragment:
public class ListFragment extends Fragment {
private RecyclerView recyclerView;
private static final String TAG = ListFragment.class.getSimpleName();
private ApiClient apiClient;
private List<Feature> features;
private RecyclerView.LayoutManager layoutManager;
private DataAdapter adapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_list, container, false);
recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
getData();
return view;
}
private void getData() {
apiClient = new ApiClientFactory().createApiClient();
apiClient.getData().enqueue(new Callback<DataResponse>() {
#Override
public void onResponse(Call<DataResponse> call, Response<DataResponse> response) {
if (response.isSuccessful()) {
features = response.body().getFeatures();
adapter = new DataAdapter(features);
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(layoutManager);
}
Log.d(TAG, "Data successfully downloaded");
}
#Override
public void onFailure(Call<DataResponse> call, Throwable t) {
Log.e(TAG, t.toString());
}
});
}
}
You should change the way you inflate the layout. This should do the trick.
public class DataAdapter extends RecyclerView.Adapter<DataAdapter.DataViewHolder> {
private List<Feature> features;
public DataAdapter(List<Feature> features) {
this.features = features;
}
#Override
public DataAdapter.DataViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.single_data_layout, parent, false);
return new DataViewHolder(view);
}
#Override
public void onBindViewHolder(DataViewHolder holder, int position) {
holder.place.setText(features.get(position).getProperties().getPlace());
holder.alert.setText(features.get(position).getProperties().getAlert());
}
#Override
public int getItemCount() {
return features.size();
}
public static class DataViewHolder extends RecyclerView.ViewHolder {
private final TextView place;
private final TextView alert;
public DataViewHolder(View view) {
super(view);
place = (TextView) view.findViewById(R.id.place_text_view);
alert = (TextView) view.findViewById(R.id.alert_text_view);
}
}
}
I see that the adapter class don't have a context , I guess if you add to the adapter constructor Context c as a second parameter your problem will be solved
try to replase
public DataAdapter(List<Feature> features) {
this.features = features;
}
by this
public DataAdapter(List<Feature> features, Context c) {
this.features = features;
this.context = c;
}
and replace this line in the fragment class
adapter = new DataAdapter(features);
by this
adapter = new DataAdapter(features,getActivity());
hope this will help you :)

How to store and retrive RecyclerView Items for offline use

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

Categories

Resources