I'm fetching 3 String values from the database and then I'm converting it to Long and then I'm calculating a difference and then putting this calculated Long value in a method as parameter. I'm using FastAdapter.
The filterRequests(List <Long> l) is a method in MainActivity which do the logic of filtering requests/content based on the long l.
entire adapter:
public class GRModelClass extends AbstractItem<GRModelClass, GRClass.ViewHolder>{
private static final ViewHolderFactory<? extends ViewHolder> FACTORY = new ItemFactory();
String postedBy, postedTime, currentLat, currentLng, utcFormatDateTime, userEmail, userID;
String startDateTimeInEpoch, endDateTimeInEpoch;
DatabaseReference primaryDBKey;
long ms;
String itemID;
public GRModelClass(){}
public GRModelClass(String postedBy, String postedTime, String currentLat, String currentLng, String utcFormatDateTime, String userEmail, String userID, String startDateTimeInEpoch, String endDateTimeInEpoch, DatabaseReference primaryDBKey) {
this.postedBy = " " + postedBy;
this.postedTime = postedTime;
this.currentLat = currentLat;
this.currentLng = currentLng;
this.utcFormatDateTime = utcFormatDateTime;
this.userEmail = userEmail;
this.userID = userID;
this.startDateTimeInEpoch = startDateTimeInEpoch;
this.endDateTimeInEpoch = endDateTimeInEpoch;
this.primaryDBKey = primaryDBKey;
}
#Exclude
public Map<String, Object> toMap() {
HashMap<String, Object> result = new HashMap<>();
result.put("pBy", postedBy);
result.put("cLat", currentLat);
result.put("cLng", currentLng);
result.put("utcFormatDateTime", utcFormatDateTime);
result.put("userEmail", userEmail);
result.put("userID", userID);
result.put("startDateTime", startDateTimeInEpoch);
result.put("endDateTime", endDateTimeInEpoch);
return result;
}
#Override
public int getType() {
return R.id.recycler_view;
}
#Override
public int getLayoutRes() {
return R.layout.sr_layout;
}
#Override
public void bindView(final ViewHolder holder, List list) {
super.bindView(holder, list);
holder.postedBy.setText(postedBy);
holder.postedBy.setTypeface(null, Typeface.BOLD);
holder.startDateTimeInEpoch.setText(startDateTimeInEpoch);
holder.startDateTimeInEpoch.setVisibility(View.INVISIBLE);
holder.endDateTimeInEpoch.setText(endDateTimeInEpoch);
holder.endDateTimeInEpoch.setVisibility(View.INVISIBLE);
MainActivity.filterButton.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem menuItem) {
holder.geoQuery = holder.geoFireReference.queryAtLocation(new GeoLocation(holder.currentLatDouble, holder.currentLngDouble), 5);
holder.geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
#Override
public void onKeyEntered(String key, GeoLocation location) {
primaryDBKey.child(key).child("startDateTimeInEpoch").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() != null) {
holder.startTimeDateInEpochLong2 = Long.parseLong(dataSnapshot.getValue().toString());
holder.now = System.currentTimeMillis() / 1000;
holder.diffNowsdtel.add(holder.startTimeDateInEpochLong2 - holder.now);
Log.d("log1", String.valueOf(holder.diffNowsdtel));
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if(holder.mContext instanceof MainActivity){
((MainActivity)holder.mContext).filterRequests(holder.diffNowsdtel);
Log.d("log2", String.valueOf(holder.diffNowsdtel));
}
}
}, 1500);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public void onKeyExited(String key) {
}
#Override
public void onKeyMoved(String key, GeoLocation location) {
}
#Override
public void onGeoQueryReady() {
}
#Override
public void onGeoQueryError(DatabaseError error) {
}
});
return true;
}
});
}
/**
* our ItemFactory implementation which creates the ViewHolder for our adapter.
* It is highly recommended to implement a ViewHolderFactory as it is 0-1ms faster for ViewHolder creation,
* and it is also many many timesa more efficient if you define custom listeners on views within your item.
*/
protected static class ItemFactory implements ViewHolderFactory<ViewHolder> {
public ViewHolder create(View v) {
return new ViewHolder(v);
}
}
/**
* return our ViewHolderFactory implementation here
*
* #return
*/
#Override
public ViewHolderFactory<? extends ViewHolder> getFactory() {
return FACTORY;
}
// Manually create the ViewHolder class
protected static class ViewHolder extends RecyclerView.ViewHolder {
TextView postedBy, userID, currentLt, currentLn, requestID, postedFrom;
TextView startDateTimeInEpoch, endDateTimeInEpoch, diffNowsdtelTV;
LinearLayout linearLayout;
long difference, differenceCurrentStartTime, handlerGap;
long startTimeDateInEpochLong2;
public static long now;
List<Long> diffNowsdtel;
Context mContext;
DatabaseReference firebaseDatabase;
GeoFire geoFireReference;
GeoQuery geoQuery;
public ViewHolder(final View itemView) {
super(itemView);
postedBy = (TextView) itemView.findViewById(R.id.postedBy);
startDateTimeInEpoch = (TextView) itemView.findViewById(R.id.startTimeDateInEpoch);
endDateTimeInEpoch = (TextView) itemView.findViewById(R.id.endTimeDateInEpoch);
diffNowsdtelTV = (TextView) itemView.findViewById(R.id.diffNowsdtelTV);
this.mContext = itemView.getContext();
private boolean isNetworkAvailable() {
ConnectivityManager connectivityManager
= (ConnectivityManager) itemView.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
}
}
The problem is that when I'm logging log1, I'm getting all the 3 values shown in Logcat, but when I'm logging log2 only the last calculated value is getting shown and that is the value using which filterRequests(Long l) is getting called.
Update - after updating the adapter code, log1 and log2 now shows this:
D/log1: [2197]
D/log1: [2197, -1007]
D/log1: [2197, -1007, 4003]
D/log2: [2197, -1007, 4003]
filterRequests() method is the method in which the logic to filter content based on time is done. The parameter which goes in filterRequests() is holder.diffNowsdtel which has 3 long values for now and do it should do the logic based on it.. if the long value is <=900 the content which has the long value -1007 should be shown and when long value is >900, the content which has the long value 2197 and 4003 should be shown.
here's the code:
public void filterRequests(final List<Long> l) {
final int size = l.size();
Log.d("lng", String.valueOf(l));
if (isNetworkAvailable()) {
if (chkBoxLiveRqsts.isChecked()) {
firebaseDatabase.child(key).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() != null) {
for (int i = 0; i < size; i++){
if (l.get(i) <= 900) {
...
} else {
}
}
progressDialogAdding.dismiss();
} else {
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
...
});
} else if (chkBoxSFLRqsts.isChecked()) {
fastItemAdapter.clear();
firebaseDatabase.child(key).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() != null) {
for (int i = 0; i < size; i++) {
if (l.get(i) > 900) {
...
} else {
}
}
progressDialogAdding.dismiss();
} else {
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
...
}
} else {
Snackbar snackbar = Snackbar
.make(coordinatorLayout, "No internet connection", Snackbar.LENGTH_SHORT);
snackbar.show();
progressDialogAdding.dismiss();
}
}
});
dialog = builder.create();
dialog.show();
}
Log value of lng:
D/lng: [2197, -1007, 4003]
What I want is that the filterRequests(Long l) method should use all the values of holder.diffNowsdtelTV.getText().toString() and do the logic using them.
I'm sorry for ambiguous question. Please help me with this issue!
What you are doing
In your ViewHolder diffNowsdtel is declared as long (which saves latest value) whenever you are logging log1 Logger displays (latest value from diffNowsdtel so your log displays different values because bindView calls many times whenever you are updating your row or complete dataset)
D/log1: -22136
D/log1: -22403
D/log1: -25094
Inside onMenuItemClick you are fetching value directly from your TextView which is now -25094 that's why you have only one value in your TextView and log says
D/log2: -25094
What you should do
Set Tag using holder.diffNowsdtelTV.setTag(your database key or row_id) and inside your onMenuItemClick get your tag using
Object someTagName = (Object) holder.diffNowsdtelTV.getTag();
Now fetch your 3 String values from the database using value stored in someTagName and then do your calculations.
Edit
Actually you need 3 values to do your calculation, while in your current logic you only have the latest value stored in diffNowsdtel. So now you need a logic to store trice your values somewhere and inside onMenuItemClick use them but if you will go to save trice your values you have to change your diffNowsdtel to long[] or List<Long> and save your every value in it on every bindView call which needs some logic so simplest way is to pass your unique database column say primary key and save it in your GRModelClass
String primaryDbKey;
public GRModelClass(String primaryDbKey, ...) {
this.primaryDbKey = primaryDbKey;
....
}
Inside your onMenuItemClick use primaryDbKey to fetch your 3 String values from your database table(which you are doing somewhere else) and then do your calculations.
Edit
You made a list with Long, both List and Long are not primitive data types.
In your question you are comparing primitive and non primitive data types, for comparison both sides of a comparator should be of same data types.
What you are doing:
if (l.get(i) <= 900)
Here l.get(i) returns a Long value where as 900 is integer value.
What you should do:
Simply compare your l.get(i) with Long by doing 900L
if (l.get(i) <= 900L)
Or
if (l.get(i) > 900L)
Try to set initialize the value you want within the onBindView
Log.d("diffNowsdtel", holder.diffNowsdtelTV.getText().toString());
final String val = holder.diffNowsdtelTV.getText().toString();
MainActivity.filterButton.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem menuItem) {
if(holder.mContext instanceof MainActivity){
((MainActivity)holder.mContext).filterRequests(Long.parseLong(val));
Log.d("diffNowsdtel2",val);
}
return true;
}
});
You have a statically defined object called filterButton in MainActivity. Every time bindView is called for a new view, it is replacing the menu item listener for that button. So when you click that menu item, it is only calling the handler you set on for the last item that bindView was called for. That's why you only see one log entry when that filter button is pressed.
Related
I have an Android Fragment (in Java) that displays recyclerview. Now I would like to read the data for the items in the recyclerview from a firebase database. They should be stored into an array list orderList. For this purpose, I would like to use LiveData and a ViewModel because I read several times that this is the recommended way of implementing it. Further, I would like the Fragment to update the recyclerview automatically whenever new data is stored in the firebase database.
I tried to follow the steps that are described in the offical Firebase Blog (https://firebase.googleblog.com/2017/12/using-android-architecture-components.html) but unfortunately the result is always an empty list. No elements are being displayed altough there are some relevant entries in the database that should be returned and displayed. The implementation of the recyclerview itself is correct (I checked that by manually adding items into the recylcerview).
Here is my Fragment that holds the recyclerview:
public class FR_Orders extends Fragment {
private FragmentOrdersBinding binding;
//Define variables for the RecyclerView
private RecyclerView recyclerView_Order;
private RV_Adapter_Orders adapter_Order;
private RecyclerView.LayoutManager layoutManager_Order;
private ArrayList<RV_Item_Order> orderList;
public FR_Orders() {
// Required empty public constructor
}
public static FR_Orders newInstance(String param1, String param2) {
FR_Orders fragment = new FR_Orders();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
orderList = new ArrayList<RV_Item_Order>();
// Obtain a new or prior instance of ViewModel_FR_Orders from the ViewModelProviders utility class.
ViewModel_FR_Orders viewModel = new ViewModelProvider(this).get(ViewModel_FR_Orders.class);
LiveData<DataSnapshot> liveData = viewModel.getDataSnapshotLiveData();
liveData.observe(this, new Observer<DataSnapshot>() {
#Override
public void onChanged(#Nullable DataSnapshot dataSnapshot) {
for(DataSnapshot ds: dataSnapshot.getChildren()) {
if (dataSnapshot != null) {
String drinkName = "";
String drinkSize = "";
String orderDate = "";
String orderStatus = "";
int orderDateInMilliseconds = 0;
int orderID = 0;
int quantity = 0;
int tableNumber = 0;
// update the UI here with values in the snapshot
if( dataSnapshot.child("drinkName").getValue(String.class)!=null) {
drinkName = dataSnapshot.child("drinkName").getValue(String.class);
}
if(dataSnapshot.child("drinkSize").getValue(String.class)!=null) {
drinkSize = dataSnapshot.child("drinkSize").getValue(String.class);
}
if(dataSnapshot.child("orderDate").getValue(String.class)!=null) {
orderDate = dataSnapshot.child("orderDate").getValue(String.class);
}
if(dataSnapshot.child("orderStatus").getValue(String.class)!=null) {
orderStatus = dataSnapshot.child("orderStatus").getValue(String.class);
}
if(dataSnapshot.child("orderDateInMilliseconds").getValue(Integer.class)!=null) {
orderDateInMilliseconds = dataSnapshot.child("orderDateInMilliseconds").getValue(Integer.class);
}
if(dataSnapshot.child("quantity").getValue(Integer.class)!=null) {
quantity = dataSnapshot.child("quantity").getValue(Integer.class);
}
if(dataSnapshot.child("orderID").getValue(Integer.class)!=null) {
orderID = dataSnapshot.child("orderID").getValue(Integer.class);
}
if(dataSnapshot.child("tableNumber").getValue(Integer.class)!=null) {
tableNumber = dataSnapshot.child("tableNumber").getValue(Integer.class);
}
orderList.add(new RV_Item_Order(drinkName, drinkSize, orderDateInMilliseconds, orderDate, tableNumber, orderStatus, quantity, orderID));
;
}
}
}
});
}// end onCreate
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentOrdersBinding.inflate(inflater, container, false);
buildRecyclerView();
return binding.getRoot();
}
public void buildRecyclerView() {
recyclerView_Order = binding.rvDrinksToBeDisplayed;
recyclerView_Order.setHasFixedSize(true);
layoutManager_Order = new LinearLayoutManager(this.getContext());
adapter_Order = new RV_Adapter_Orders(orderList);
recyclerView_Order.setLayoutManager(layoutManager_Order);
recyclerView_Order.setAdapter(adapter_Order);
adapter_Order.setOnItemClickListener(new RV_Adapter_Orders.OnItemClickListener() {
/*
Define what happens when clicking on an item in the RecyclerView
*/
#Override
public void onItemClick(int position) {
}
});
}//end build recyclerView
}//End class
Here is the LiveData class
public class LiveData_FirebaseOrder extends LiveData<DataSnapshot> {
private static final String LOG_TAG = "LiveData_FirebaseOrder";
private final Query query;
private final MyValueEventListener listener = new MyValueEventListener();
public LiveData_FirebaseOrder(Query query) {
this.query = query;
}
public LiveData_FirebaseOrder(DatabaseReference ref) {
this.query = ref;
}
#Override
protected void onActive() {
Log.d(LOG_TAG, "onActive");
query.addValueEventListener(listener);
}
#Override
protected void onInactive() {
Log.d(LOG_TAG, "onInactive");
query.removeEventListener(listener);
}
private class MyValueEventListener implements ValueEventListener {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
setValue(dataSnapshot);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(LOG_TAG, "Can't listen to query " + query, databaseError.toException());
}
}
}
Here is the ViewModel class (the full name of the database is not given because of privacy issues).
public class ViewModel_FR_Orders extends ViewModel {
private static final DatabaseReference ORDERS_REF =
FirebaseDatabase.getInstance("https://....firebasedatabase.app").getReference("/Orders");
private final LiveData_FirebaseOrder liveData = new LiveData_FirebaseOrder(ORDERS_REF);
#NonNull
public LiveData<DataSnapshot> getDataSnapshotLiveData() {
return liveData;
}
}
And here is a screenshot of the Firebase Database that shows that there are some entries in the node Orders that should be returned
Does anyone have an idea what I am making wrong? I tried to stick exactly to the instructions of the offical Firebase Blog.
Update: I found out that the query itself returns the correct datasnapshots but the recyclerview is not built and updated.
I tried to make a covid-19 tracking app by watching Youtube tutorials.
My app shows the country list with flags and when you click on any country it opens an activity and shows the details of that country i.e total cases, deaths, recovered, etc. The video on Youtube uses ListView and I am using recyclerView
I fetched the country list successfully and set onClickListener on the view and it opens second activity which shows the case in detail. But I don't know how to show the data.
my adapter class:
class Countries_adapter extends RecyclerView.Adapter<Countries_adapter.MyViewHolder> {
Context ct;
List<Countries_data> list;
public Countries_adapter(Context context,List<Countries_data> country)
{
ct=context;
list=country;
}
#Override
public MyViewHolder onCreateViewHolder( ViewGroup parent, int viewType) {
View v = LayoutInflater.from(ct).inflate(R.layout.countries_row,parent,false);
return new MyViewHolder(v);
}
#Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
holder.tvCountryName.setText(list.get(position).getCountry());
holder.tvtotal.setText(list.get(position).getActive());
Glide.with(ct).load(list.get(position).getFlag()).into(holder.imageView);
holder.linearLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent i = new Intent(ct,CountriesDetails.class);
i.putExtra("Country",list.get(position).getCountry());
ct.startActivity(i);
}
});
}
#Override
public int getItemCount() {
return list.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView tvCountryName,tvtotal;
ImageView imageView;
LinearLayout linearLayout;
public MyViewHolder( View itemView) {
super(itemView);
tvCountryName = itemView.findViewById(R.id.tvCountryName);
tvtotal=itemView.findViewById(R.id.tvCountrytotalcaese);
imageView = itemView.findViewById(R.id.imageFlag);
linearLayout=itemView.findViewById(R.id.linear_layout);
}
}
my AffectedCoutries class activity is as follows:
public class AffectedCountries extends AppCompatActivity {
EditText edtSearch;
RecyclerView recyclerView;
SimpleArcLoader simpleArcLoader;
public static ArrayList countryList = new ArrayList<>();
Countries_data countryData;
Countries_adapter CountriesAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_affected_countries);
edtSearch = findViewById(R.id.edtSearch);
recyclerView=findViewById(R.id.recyclerAffectedCountries);
simpleArcLoader = findViewById(R.id.loader);
fetchData();
}
private void fetchData() {
String url = "https://corona.lmao.ninja/v2/countries/";
simpleArcLoader.start();
StringRequest request = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONArray jsonArray = new JSONArray(response);
for(int i=0;i<jsonArray.length();i++){
JSONObject jsonObject = jsonArray.getJSONObject(i);
String countryName = jsonObject.getString("country");
String cases = jsonObject.getString("cases");
String todayCases = jsonObject.getString("todayCases");
String deaths = jsonObject.getString("deaths");
String todayDeaths = jsonObject.getString("todayDeaths");
String recovered = jsonObject.getString("recovered");
String active = jsonObject.getString("active");
String critical = jsonObject.getString("critical");
JSONObject object = jsonObject.getJSONObject("countryInfo");
String flagUrl = object.getString("flag");
countryData = new Countries_data(flagUrl,countryName,cases,todayCases,deaths,todayDeaths,recovered,active,critical);
countryList.add(countryData);
}
CountriesAdapter = new Countries_adapter(AffectedCountries.this,countryList);
recyclerView.setLayoutManager(new LinearLayoutManager(AffectedCountries.this));
recyclerView.setAdapter(CountriesAdapter);
simpleArcLoader.stop();
simpleArcLoader.setVisibility(View.GONE);
} catch (JSONException e) {
e.printStackTrace();
simpleArcLoader.start();
simpleArcLoader.setVisibility(View.GONE);
Toast.makeText(AffectedCountries.this,"catch response", Toast.LENGTH_SHORT).show();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
simpleArcLoader.stop();
simpleArcLoader.setVisibility(View.GONE);
Toast.makeText(AffectedCountries.this,"error response", Toast.LENGTH_SHORT).show();
}
});
RequestQueue requestQueue = Volley.newRequestQueue(this);
requestQueue.add(request);
}
}
my Countries_data class(model class)
public class Countries_data {
public String country;
public String cases;
public String todayCases;
public String deaths;
public String todayDeaths;
public String recovered;
public String active;
public String critical;
public String flag;
public Countries_data(String flag, String country, String cases, String
todayCases, String deaths, String todayDeaths, String recovered,
String active, String critical) {
this.country = country;
this.cases = cases;
this.todayCases = todayCases;
this.deaths = deaths;
this.todayDeaths = todayDeaths;
this.recovered = recovered;
this.active = active;
this.critical = critical;
this.flag = flag;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getCases() {
return cases;
}
public void setCases(String cases) {
this.cases = cases;
}
public String getTodayCases() {
return todayCases;
}
public void setTodayCases(String todayCases) {
this.todayCases = todayCases;
}
public String getDeaths() {
return deaths;
}
public void setDeaths(String deaths) {
this.deaths = deaths;
}
public String getTodayDeaths() {
return todayDeaths;
}
public void setTodayDeaths(String todayDeaths) {
this.todayDeaths = todayDeaths;
}
public String getRecovered() {
return recovered;
}
public void setRecovered(String recovered) {
this.recovered = recovered;
}
public String getActive() {
return active;
}
public void setActive(String active) {
this.active = active;
}
public String getCritical() {
return critical;
}
public void setCritical(String critical) {
this.critical = critical;
}
public String getFlag() {
return flag;
}
public void setFlag(String flag) {
this.flag = flag;
}
}
my Country_details class
public class CountriesDetails extends AppCompatActivity {
TextView tvCountry, tvCases, tvRecovered,tvdeaths;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_countries_details2);
tvCountry = findViewById(R.id.tvCountry);
tvCases = findViewById(R.id.tvCases);
tvRecovered = findViewById(R.id.tvRecovered);
tvdeaths=findViewById(R.id.tvTotalDeaths);
String positionCountry = getIntent().getStringExtra("Country");
Log.i("country name", positionCountry);
}
}
How to set the data in tvcases?
How can I show the data? Do I have to create a separate recycler view and then fetch the data from the API or can I use my main activity to show the data?
I have some sad information for you: Google Play policy restricts such apps from being published in store... especially when you are showing deaths count. Been there, done that, my app was blocked...
besides above:
you are passing country name in intent:
i.putExtra("Country",list.get(position).getCountry());
but trying to read "position" in details Activity - it will be always 0 (default)
edit due to comments:
pass position of clicked View
holder.linearLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent i = new Intent(ct,CountriesDetails.class);
i.putExtra("position", position);
ct.startActivity(i);
}
});
in CountriesDetailss onCreate method receive this position and restore proper Countries_data object from your static countryList array in AffectedCountries
int position = getIntent().getIntExtra("position", 0);
Countries_data country = AffectedCountries.countryList.get(position);
tvCountry.setText(country.getCountry());
that should work, but this isn't best way to store data, in fact its very weak... after downloading data (list of countries) you should store it somehow, e.g. SQLite/Room lib or at least SharedPreferences... then in details activity you should restore int position and using it take proper object from database or preferences, instead of stright from static array
it may be also useful to implement Parcelable inteface to your Countries_data - this will allow to put whole Countries_data object into Intents extras, not only primitive int with position in array. in this case details activity won't even need access to whole array or sql database/preferences, it will get whole object straight from Intents extras
I'm trying to make a replica chat app and I have a list that I need populated into the recyclerview. I'm getting data from firebase realtime database and every time I receive or actually send a message, All the previous item(messages) plus the new one are repopulated/duplicated into the recycler view.
What I have tried
I have tried using .cleaar() method on my list before adding a new item to the list but now all other items in the recycler view disappear
here's my adapter
public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public static final int MSG_TYPE_LEFT = 0;
public static final int MSG_TYPE_RIGHT = 1;
FirebaseUser firebaseUser;
private Context ctx;
private List<Messages> msgsR, msgsS;//ignore unused
private ArrayList<Messages> dataSet;
public MessageAdapter(Context context) {
this.ctx = context;
this.dataSet = new ArrayList<>();
//this.msgsR = messagesReceived;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if (viewType == Messages.SENT_TYPE) {
View view = LayoutInflater.from(ctx).inflate(R.layout.message_item_right, parent, false);
return new ViewHolderS(view);
}
if (viewType == Messages.RECEIVED_TYPE) {
View view = LayoutInflater.from(ctx).inflate(R.layout.message_item_left, parent, false);
return new ViewHolderR(view);
}
return null;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
Messages object = dataSet.get(position);
if (object != null) {
switch (object.type) {
case Messages.RECEIVED_TYPE:
((ViewHolderR) holder).populate(object, position);
break;
case Messages.SENT_TYPE:
((ViewHolderS) holder).populate(object, position);
break;
}
}
}
//recceives messages object to populate into list
//it does not matter where i put the .clear() method, after or below dataset.add() its still undesireable
public void addMessageSent(Messages messages){
dataSet.clear();
dataSet.add(messages);
// notifyItemInserted(dataSet.size()-1);
//notifyItemRangeChanged(dataSet.size()-1, dataSet.size());
}
#Override
public int getItemCount() {
return dataSet.size();
}
// sent messages are handled here
public static class ViewHolderS extends RecyclerView.ViewHolder {
public TextView msg, time;
public LinearLayout layout;
public ViewHolderS(#NonNull View itemView) {
super(itemView);
layout = itemView.findViewById(R.id.cont);
msg = itemView.findViewById(R.id.send_msg);
time = itemView.findViewById(R.id.time);
}
private void populate(Messages messages, int position) {
msg.setText(messages.getMessage());
msg.setPadding(6, 4, 18, 4);
msg.setMinWidth(100);
msg.setMaxWidth(400);
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) msg.getLayoutParams();
layoutParams.gravity = Gravity.START;
layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT;
layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT;
layoutParams.topMargin = layoutParams.bottomMargin = layoutParams.rightMargin = 10;
layoutParams.leftMargin = 20;
msg.setLayoutParams(layoutParams);
time.setText(messages.getTime());
}
}
#Override
public int getItemViewType(int position) {
switch (dataSet.get(position).type) {
case 0:
return Messages.SENT_TYPE;
case 1:
return Messages.RECEIVED_TYPE;
default:
return -1;
}
}
// received messages are handled here
private class ViewHolderR extends ViewHolderS {
public TextView msg, time;
public LinearLayout layout;
public ViewHolderR(#NonNull View itemView) {
super(itemView);
layout = itemView.findViewById(R.id.cont);
msg = itemView.findViewById(R.id.send_msg);
time = itemView.findViewById(R.id.time);
}
private void populate(Messages messages, int position) {
msg.setText(messages.getMessage());
msg.setPadding(6, 4, 18, 4);
msg.setMinWidth(100);
msg.setMaxWidth(400);
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) msg.getLayoutParams();
layoutParams.gravity = Gravity.START;
layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT;
layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT;
layoutParams.topMargin = layoutParams.bottomMargin = layoutParams.rightMargin = 10;
layoutParams.leftMargin = 20;
msg.setLayoutParams(layoutParams);
time.setText(messages.getTime());
}
}
}
and here's my data model
public class Messages {
private String message;
public static final int SENT_TYPE=0;
public static final int RECEIVED_TYPE=1;
public static final int AUDIO_TYPE=2;
private long time;
public int type;
private String id;
private String receiver;
public Messages(String message,long time, String sender,String receiver, int type) {
this.message = message;
this.time = time;
this.id = sender;
this.type = type;
this.receiver = receiver;
}
public Messages() {
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getTime() {
SimpleDateFormat output = new SimpleDateFormat("HH:mm", Locale.getDefault());
return output.format(new Date(time));
}
public void setTime(long time) {
this.time = time;
}
public String getSender() {
return id;
}
public void setSender(String sender) {
this.id = sender;
}
public String getReceiver() {
return receiver;
}
public void setReceiver(String receiver) {
this.receiver = receiver;
}
}
and below is activity class where I set the adapter and fill the list
recyclerView.setHasFixedSize(true);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext());
linearLayoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(linearLayoutManager);
list= new ArrayList();
messageAdapter = new MessageAdapter(PersonChatActivity.this);
recyclerView.setAdapter(messageAdapter);
recyclerView.setItemAnimator(new DefaultItemAnimator());
if (Objects.requireNonNull(recyclerView.getAdapter()).getItemCount() > 0) {
recyclerView.smoothScrollToPosition(recyclerView.getAdapter().getItemCount());
}
//get sent messages from firebase
private void getmessages() {
DatabaseReference reference = database.getReference("Chats");
reference.keepSynced(true);
reference.child(senderId).child(receiver).push();
reference.child(senderId).child(receiver).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
messageSent.clear();
for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
if (dataSnapshot.getValue() != null) {
String message = (String) dataSnapshot.child("message").getValue();
long time = (Long) dataSnapshot.child("time").getValue();
String senderId = (String) dataSnapshot.child("id").getValue();
String receiverId = (String) dataSnapshot.child("receiver").getValue();
assert firebaseUser != null;
String user = firebaseUser.getUid();
Messages msg = new Messages(message, time, senderId, receiverId,Messages.SENT_TYPE);
String Uid = msg.getSender();
if (!Uid.isEmpty() && Uid.equals(user)) {
//pass the new message object to messages adapter to fill the list
messageAdapter.addMessageSent(msg);
}
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
EDIT UPDATE
As suggested I have made the necessary changes and code works like magic
I wont update the right changes into the question in case someone makes the same mistake as I have plus the comment marked as answer basically highlights the correct changes,,
NEW PROBLEM
The new problem is, on adding object message to the addMessagesent() previously populated recyclerview items get replaced by the new data.
to make it easy, on receiving a new message, all the previous visible sent messagees disappear and are replaced by the new received messages
here is my getmessageReceived() method
DatabaseReference reference = database.getReference("Chats");
reference.keepSynced(true);
reference.child(receiver).child(senderId).push();
reference.child(receiver).child(senderId).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
messageReceived.clear();
messageAdapter.clearAllMessage();
for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
if (dataSnapshot.getValue() != null) {
String message = (String) dataSnapshot.child("message").getValue();
long time = (Long) dataSnapshot.child("time").getValue();
String senderId = (String) dataSnapshot.child("id").getValue();
String receiverId = (String) dataSnapshot.child("receiver").getValue();
assert firebaseUser != null;
String user = firebaseUser.getUid();
Messages msg = new Messages(message, time, senderId, receiverId,Messages.RECEIVED_TYPE);
String Uid = msg.getReceiver();
if (!Uid.isEmpty() && Uid.equals(user)) {
messageAdapter.addMessageSent(msg);
messageAdapter.notifyDataSetChanged();
}
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
You should not clear the data set in the addMessageSent() just add new item to data set as shown below
public void addMessageSent(Messages messages){
dataSet.add(messages);
}
and create a new method to clear the dataset in your adapter
public void clearAllMessage(){
dataSet.clear();
}
And in getmessages() call clearAllMessage() like this
private void getmessages() {
DatabaseReference reference = database.getReference("Chats");
reference.keepSynced(true);
reference.child(senderId).child(receiver).push();
reference.child(senderId).child(receiver).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
messageSent.clear();
clearAllMessage();
for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
if (dataSnapshot.getValue() != null) {
String message = (String) dataSnapshot.child("message").getValue();
long time = (Long) dataSnapshot.child("time").getValue();
String senderId = (String) dataSnapshot.child("id").getValue();
String receiverId = (String) dataSnapshot.child("receiver").getValue();
assert firebaseUser != null;
String user = firebaseUser.getUid();
Messages msg = new Messages(message, time, senderId, receiverId,Messages.SENT_TYPE);
String Uid = msg.getSender();
if (!Uid.isEmpty() && Uid.equals(user)) {
//pass the new message object to messages adapter to fill the list
messageAdapter.addMessageSent(msg);
messageAdapter.notifyDataSetChanged(); // Call this also
}
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
There is Scroll by loading RecyclerView(Lazy loading). When an item quantity is incremented by adding the plus button to say 10. after loading the next set of items while scrolling, The item quantity(10) is reset to 1 and 10 will be displayed for another item.
In the same sequence, if the unit is changed(by the spinner) to another unit the corresponding image will be loaded but after scrolling the unit and image will change back to initial unit.
When the image of an item is clicked, a dialog will show the details of the selected item . Sometimes the image in the dialog is correct but image of the RecyclerView is incorrect, will take time to load or will not load.
public class ArticleRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
implements Filterable {
private static final String TAG = "ArticleAdapter";
private final int VIEW_TYPE_ITEM = 0;
private final int VIEW_TYPE_LOADING = 1;
private Context context;
private Dialog articleDescriptionDialog;
private UOMOffersLIistViewAdapter uomOffersLIistViewAdapter;
private List<ArticleDetails> articleDetailsList;
private HomeActivityViewModel homeActivityViewModel;
private ItemFilter itemFilter = new ItemFilter();
private GetViewListener getViewListener;
private int lastPosition = -1;
private OnLoadMoreListener loadMoreListener;
private boolean isLoading = false, isMoreDataAvailable = true;
/*
* isLoading - to set the remote loading and complete status to fix back to back load more call
* isMoreDataAvailable - to set whether more data from server available or not.
* It will prevent useless load more request even after all the server data loaded
* */
public ArticleRecyclerViewAdapter(Context context, List<ArticleDetails> articleDetailsList
, HomeActivityViewModel homeActivityViewModel, Fragment fragment) {
this.context = context;
this.articleDetailsList = articleDetailsList;
this.homeActivityViewModel = homeActivityViewModel;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
if (viewType == VIEW_TYPE_ITEM) {
return new ItemViewHolder(inflater.inflate(R.layout.recycler_view_article_layout, parent, false));
} else {
return new LoadHolder(inflater.inflate(R.layout.item_loading_layout, parent, false));
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
if (position >= getItemCount() - 1 && isMoreDataAvailable && !isLoading && loadMoreListener != null) {
isLoading = true;
loadMoreListener.onLoadMore();
}
if (getItemViewType(position) == VIEW_TYPE_ITEM) {
populateItemRows((ItemViewHolder) viewHolder, position);
}
//No else part needed as load holder doesn't bind any data
}
#Override
public int getItemViewType(int position) {
if (articleDetailsList.get(position).isLoading()) {
return VIEW_TYPE_LOADING;
} else {
return VIEW_TYPE_ITEM;
}
}
#Override
public int getItemCount() {
return articleDetailsList.size();
}
#Override
public long getItemId(int position) {
return position;
}
/* VIEW HOLDERS */
static class ItemViewHolder extends RecyclerView.ViewHolder {
private ImageView stockImageView, copyStockImageView;
private TextView articleNameTextView, articleStockAmountTextView, articleOfferTextView, addCartTextView;
private NumberPicker articleStockQuantityNumberPicker;
private Spinner articleStockSpinner;
private ImageButton offerInfoImageButton;
private int selectedStockItemPosition, stockQuantity;
private String articleStockQuantityString = "";
private Integer stockId;
private double stockAmount;
public ItemViewHolder(View itemView) {
super(itemView);
stockImageView = itemView.findViewById(R.id.stockImgv);
copyStockImageView = itemView.findViewById(R.id.copyStockImgv);//for fly to cart animation
articleNameTextView = itemView.findViewById(R.id.articleNameTxt);
articleStockAmountTextView = itemView.findViewById(R.id.stock_amount_txt);
articleOfferTextView = itemView.findViewById(R.id.offer_txt);
offerInfoImageButton = itemView.findViewById(R.id.offerInfo_imgbtn);
addCartTextView = itemView.findViewById(R.id.addCart_txt);
articleStockSpinner = itemView.findViewById(R.id.stock_spnr);
articleStockQuantityNumberPicker = itemView.findViewById(R.id.qty_numberPicker);
articleStockQuantityNumberPicker.setMin(1);
articleStockQuantityNumberPicker.setUnit(1);
// no limit.
// viewHolder.quantityNumberPicker.setMax(15);
}
}
static class LoadHolder extends RecyclerView.ViewHolder {
public LoadHolder(View itemView) {
super(itemView);
}
}
public void setMoreDataAvailable(boolean moreDataAvailable) {
isMoreDataAvailable = moreDataAvailable;
}
/**
* notifyDataSetChanged is final method so we can't override it
* call articleRecyclerViewAdapter.notifyDataChanged(); after update the list
*/
public void notifyDataChanged() {
notifyDataSetChanged();
isLoading = false;
}
public interface OnLoadMoreListener {
void onLoadMore();
}
public void setLoadMoreListener(OnLoadMoreListener loadMoreListener) {
this.loadMoreListener = loadMoreListener;
}
private void populateItemRows(ItemViewHolder itemViewHolder, int itemPos) {
int itemPosition = itemViewHolder.getAdapterPosition();
try {
itemViewHolder.copyStockImageView.setImageResource(R.drawable.error_logo);//initialize with error logo.
itemViewHolder.stockImageView.setImageResource(R.drawable.error_logo);//initialize with error logo.
} catch (Exception e) {
e.printStackTrace();
}
itemViewHolder.articleNameTextView.setText(articleDetailsList.get(itemPosition).getArticleName());
itemViewHolder.articleNameTextView.setSelected(true);//for marquee
itemViewHolder.articleOfferTextView.setSelected(true);//for marquee
itemViewHolder.articleStockAmountTextView.setSelected(true);//for marquee
itemViewHolder.articleOfferTextView.setVisibility(View.GONE);//for demo (now no offer and schemes).
itemViewHolder.offerInfoImageButton.setVisibility(View.GONE);//for demo
setStockSpinner(itemViewHolder, itemPosition);
itemViewHolder.offerInfoImageButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//setUOMOffersDIalog(articleDetailsList.get(itemPosition));
}
});
itemViewHolder.articleStockQuantityNumberPicker.setValueChangedListener(new ValueChangedListener() {
#Override
public void valueChanged(int quantity, ActionEnum action) {
itemViewHolder.articleStockQuantityString = Integer.valueOf(quantity).toString();
}
});
itemViewHolder.addCartTextView.setOnClickListener(new SingleClickListener() {
#Override
public void onSingleClick(View v) {
itemViewHolder.articleStockQuantityString = Integer.valueOf(itemViewHolder.articleStockQuantityNumberPicker.getValue()).toString();
itemViewHolder.stockQuantity = Integer.valueOf(itemViewHolder.articleStockQuantityString);
itemViewHolder.stockAmount = articleDetailsList.get(itemPosition).getStockDetailsList()
.get(itemViewHolder.articleStockSpinner.getSelectedItemPosition()).getmRP();
itemViewHolder.stockId = articleDetailsList.get(itemPosition).getStockDetailsList()
.get(itemViewHolder.articleStockSpinner.getSelectedItemPosition()).getStockId();
Double stockDiscountAmount = 0.0;
//TODO: 26-Oct-19 discount hardcoded.
if (itemViewHolder.stockQuantity > 0) {
addItemToCart(itemViewHolder, itemViewHolder.stockId, itemViewHolder.stockQuantity, itemViewHolder.stockAmount, stockDiscountAmount);
} else {
//not using (used for when user enter invalid quantity)
MDToast.makeText(context, FinalVariables.ToastMessages.VALID_QUANTITY
, MDToast.LENGTH_SHORT, MDToast.TYPE_WARNING).show();
}
}
});
itemViewHolder.stockImageView.setOnClickListener(new SingleClickListener() {
#Override
public void onSingleClick(View v) {
itemViewHolder.articleOfferTextView.setVisibility(View.VISIBLE);
itemViewHolder.articleOfferTextView.setText( ""+ itemViewHolder.stockQuantity);
DialogUtils.showArticleDescription(context, articleDetailsList.get(itemPosition)
, itemViewHolder.selectedStockItemPosition, new DialogButtonClickListener() {
#Override
public void positiveButtonClick() {
//do nothing.
}
});
}
});
// Here you apply the animation when the view is bound
setAnimation(itemViewHolder.itemView, itemPosition);
}
private void setStockSpinner(ItemViewHolder itemViewHolder, int itemPosition) {
UOMSpinnerAdapter uomSpinnerAdapter = new UOMSpinnerAdapter(context, R.layout.spinner_uom_layout
, StringUtils.makeUomStringArrayForSpinner(articleDetailsList.get(itemPosition).getStockDetailsList()));
itemViewHolder.articleStockSpinner.setAdapter(uomSpinnerAdapter);
// Setting OnItemClickListener to the Spinner
itemViewHolder.articleStockSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
itemViewHolder.selectedStockItemPosition = position;
itemViewHolder.articleStockAmountTextView.setText(StringUtils.addAED(StringUtils.round3(
+articleDetailsList.get(itemPosition)
.getStockDetailsList().get(position).getmRP())));
//show image from link
try {
if (!articleDetailsList.get(itemPosition).getStockDetailsList().
get(position).getStockImageDetailsList().isEmpty()) {
ImageUtils.setGlide(context, itemViewHolder.stockImageView
, articleDetailsList.get(itemPosition).getStockDetailsList().
get(position).getStockImageDetailsList().get(0).getStockImage());
//same as stockImageView but used to animate fly to cart
ImageUtils.setGlide(context, itemViewHolder.copyStockImageView
, articleDetailsList.get(itemPosition).getStockDetailsList().
get(position).getStockImageDetailsList().get(0).getStockImage());
} else {
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
if (articleDetailsList.get(itemPosition).getStockDetailsList().size() > 0)
itemViewHolder.articleStockAmountTextView.setText(StringUtils.addAED(
StringUtils.round3(+articleDetailsList.get(itemPosition).getStockDetailsList().get(0).getmRP())));
itemViewHolder.selectedStockItemPosition = itemViewHolder.articleStockSpinner.getSelectedItemPosition();
}
});
}
private void addItemToCart(ItemViewHolder itemViewHolder, Integer stockId, Integer stockQuantity
, Double stockAmount, Double stockDiscountAmount) {
homeActivityViewModel.isItemAlreadyCarted(stockId
, new RetrieveDataListner() {
#Override
public void onDone(Object object) {
boolean isItemAlreadyCarted = (boolean) object;
if (!isItemAlreadyCarted) {//item not in cart,add to cart.
CartedItems cartedItem = new CartedItems();
cartedItem.setStockId(stockId);
cartedItem.setStockQuantity(stockQuantity);
cartedItem.setStockAmount(stockAmount);
cartedItem.setTotalAmount(CalcUtils.calculateTotalAmount(stockQuantity, stockAmount));
cartedItem.setNetAmount(CalcUtils.calculateNetAmount(stockQuantity
, stockAmount, stockDiscountAmount));
homeActivityViewModel.insertCartedItem(cartedItem
, new InsertDbSuccessListner() {
#Override
public void onSuccess() {
//set view to call back method for fly to cart animation
if (getViewListener != null){
getViewListener.onGetView(itemViewHolder.copyStockImageView);
}
itemViewHolder.articleStockQuantityNumberPicker.setValue(1);//reset item quantity in ui.
// TODO: 21-Mar-19 check fly to cart only in home activity or not
if (context instanceof MainActivity) {
MDToast.makeText(context, TextMessages.ITEM_ADDED
, MDToast.LENGTH_SHORT, MDToast.TYPE_SUCCESS).show();
} else if (context instanceof HomeActivity) {
try {
((HomeActivity) context).setCartDetails();
} catch (Exception e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure() {
}
});
} else {//item already carted,asking to add new quantity to carted quantity.
DialogUtils.showConformAlertDialog1(context, TextMessages.ITEM_ALREADY_CARTED
, TextMessages.ITEM_ALREADY_CARTED_WARNING
, StringValues.NO, StringValues.YES, new DialogButtonClickListener() {
#Override
public void positiveButtonClick() {//adding new quantity to existing quantity.
//set view to call back method for fly to cart animation
if (getViewListener != null)
getViewListener.onGetView(itemViewHolder.copyStockImageView);
homeActivityViewModel.updateCartedItemWithExistingValues(stockId, stockQuantity
, CalcUtils.calculateTotalAmount(stockQuantity, stockAmount),
CalcUtils.calculateNetAmount(stockQuantity, stockAmount, stockDiscountAmount)
, null);
itemViewHolder.articleStockQuantityNumberPicker.setValue(1);//reset item quantity in ui.
}
#Override
public void negativeButtonClick() {
}
});
}
}
});
}
/**
* Here is the key method to apply the animation
*/
private void setAnimation(View viewToAnimate, int position) {
// If the bound view wasn't previously displayed on screen, it's animated
if (position > lastPosition) {
Animation animation = AnimationUtils.loadAnimation(context, R.anim.fall_down);
viewToAnimate.startAnimation(animation);
lastPosition = position;
}
}
/**
* register listener for get article image(copyStockImageView)
* for fly to cart animation.
* the view get back to the call back(categorizedArticleFragment)).
*
* #param getViewListener
*/
public void setGetViewListener(GetViewListener getViewListener) {
this.getViewListener = getViewListener;
}
#Override
public Filter getFilter() {
return itemFilter;
}
private class ItemFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
String filterString = constraint.toString().toLowerCase();
FilterResults results = new FilterResults();
final List<ArticleDetails> list = articleDetailsList;
int count = list.size();
final List<ArticleDetails> nlist = new ArrayList<ArticleDetails>(count);
ArticleDetails filterableString;
for (int i = 0; i < count; i++) {
filterableString = list.get(i);
if (filterableString.getArticleName().toLowerCase().contains(filterString)) {
nlist.add(filterableString);
}
}
results.values = nlist;
results.count = nlist.size();
return results;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
articleDetailsList = (ArrayList<ArticleDetails>) results.values;
notifyDataSetChanged();
}
}
}
Fragments onCreate
#Override
public View onCreateView(#NonNull LayoutInflater layoutInflater, ViewGroup viewGroup,
Bundle savedInstanceState) {
context = viewGroup.getContext();
parentView = layoutInflater.inflate(R.layout.fragment_article, viewGroup, false);
homeActivityViewModel = getViewModel();
articleRecyclerView = parentView.findViewById(R.id.subcategory_rcylv);
Bundle bundle = getArguments();
Integer offset = bundle.getInt(TagNames.OFFSET, 0);
Integer numberOfRows = bundle.getInt(TagNames.NUMBER_OF_ROWS, FinalVariables.GETTING_DATA_COUNT_FROM_API);
filterTypeId = bundle.getInt(TagNames.FILTER_TYPE_ID, FilterTypes.SUGGESTED);
String filterText = bundle.getString(TagNames.FILTER_TEXT, "");
Integer categoryId = bundle.getInt(TagNames.CATEGORY_ID, 0);
initArticleRecyclerViewAdapter(articleDetailsList, filterText, categoryId, offset, numberOfRows);
return parentView;
}
Initializing Adapter
private void initArticleRecyclerViewAdapter(List<ArticleDetails> articleDetailsList
, String filterText, Integer categoryId, int offset, int numberOfRows) {
articleRecyclerViewAdapter = new ArticleRecyclerViewAdapter(context, this.articleDetailsList, homeActivityViewModel, this);
articleRecyclerViewAdapter.setLoadMoreListener(new ArticleRecyclerViewAdapter.OnLoadMoreListener() {
#Override
public void onLoadMore() {
articleRecyclerView.post(new Runnable() {
#Override
public void run() {
int index = ArticleFragment.this.articleDetailsList.size();
loadMore(filterText, categoryId, index, numberOfRows);
Log.e(TAG, "scrolling,index: " + index);
}
});
//Calling loadMore function in Runnable to fix the
// java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling error
}
});
articleRecyclerView.setHasFixedSize(true);
articleRecyclerView.setLayoutManager(new GridLayoutManager(context, 3));
articleRecyclerView.setAdapter(articleRecyclerViewAdapter);
load(filterText, categoryId, offset, numberOfRows);
}
Loading data
private void loadMore(String filterText, Integer categoryId, int offset, int numberOfRows) {
//add loading progress view
articleDetailsList.add(new ArticleDetails(true));
articleRecyclerViewAdapter.notifyItemInserted(articleDetailsList.size() - 1);
Integer storeId = FileUtils.getPreferenceInt(context
, Preferences.CUSTOMER_PREFERENCE, TagNames.STORE_ID);
Integer distributionChannelId = FileUtils.getPreferenceInt(context
, Preferences.CUSTOMER_PREFERENCE, TagNames.DISTRIBUTION_CHANNEL_ID);
homeActivityViewModel.getAllStockItemByCustomer(
DeviceUtils.getCredential(context, ServiceNames.GET_ALL_STOCK_ITEM_BY_CUSTOMER)
, storeId, distributionChannelId, filterTypeId, filterText, categoryId, offset, numberOfRows
, new ApiCallbackListener() {
#Override
public void onSuccess(Object object) {
try {
if (object != null) {
if (object instanceof GetAllStockItemByCustomerStatusReturn) {
//otp generation failed,
GetAllStockItemByCustomerStatusReturn getAllStockItemByCustomerStatusReturn
= (GetAllStockItemByCustomerStatusReturn) object;
showGetAllStockItemByCustomerErrorDialog(getAllStockItemByCustomerStatusReturn);
} else if (object instanceof List) {
List<GetAllStockItemByCustomerResponseData> getAllStockItemByCustomerResponseDataList
= (List<GetAllStockItemByCustomerResponseData>) object;
//remove loading view
articleDetailsList.remove(articleDetailsList.size() - 1);
List<ArticleDetails> result = new ArrayList<>();
result = homeActivityViewModel.getArticles(getAllStockItemByCustomerResponseDataList);
homeActivityViewModel.insertAllStockItemByCustomer(context,filterTypeId
, getAllStockItemByCustomerResponseDataList, null);
if (result.size() > 0) {
//add loaded data
articleDetailsList.addAll(result);
} else {//result size 0 means there is no more data available at server
articleRecyclerViewAdapter.setMoreDataAvailable(false);
//telling articleRecyclerViewAdapter to stop calling load more as no more server data available
//Toast.makeText(context, "No More Data Available", Toast.LENGTH_LONG).show();
}
articleRecyclerViewAdapter.notifyDataChanged();
//should call the custom method articleRecyclerViewAdapter.notifyDataChanged here to get the correct loading status
} else {
MDToast.makeText(context, FinalVariables.ToastMessages.SOMETHING_WENT_WRONG
, MDToast.LENGTH_SHORT, MDToast.TYPE_WARNING).show();
}
} else {
MDToast.makeText(context, FinalVariables.ToastMessages.SOMETHING_WENT_WRONG
, MDToast.LENGTH_SHORT, MDToast.TYPE_WARNING).show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
private void load(String filterText, Integer categoryId, int offset, int numberOfRows) {
Integer storeId = FileUtils.getPreferenceInt(context
, Preferences.CUSTOMER_PREFERENCE, TagNames.STORE_ID);
Integer distributionChannelId = FileUtils.getPreferenceInt(context
, Preferences.CUSTOMER_PREFERENCE, TagNames.DISTRIBUTION_CHANNEL_ID);
homeActivityViewModel.getAllStockItemByCustomer(
DeviceUtils.getCredential(context, ServiceNames.GET_ALL_STOCK_ITEM_BY_CUSTOMER)
, storeId, distributionChannelId, filterTypeId, filterText, categoryId, offset, numberOfRows
, new ApiCallbackListener() {
#Override
public void onSuccess(Object object) {
try {
if (object != null) {
if (object instanceof GetAllStockItemByCustomerStatusReturn) {
GetAllStockItemByCustomerStatusReturn getAllStockItemByCustomerStatusReturn
= (GetAllStockItemByCustomerStatusReturn) object;
showGetAllStockItemByCustomerErrorDialog(getAllStockItemByCustomerStatusReturn);
} else if (object instanceof List) {
List<GetAllStockItemByCustomerResponseData> getAllStockItemByCustomerResponseDataList
= (List<GetAllStockItemByCustomerResponseData>) object;
if (!getAllStockItemByCustomerResponseDataList.isEmpty()) {
if (offset == 0 && getAllStockItemByCustomerResponseDataList.get(0)
.getCategoryID() == StatusCodes.INVALID_CODE
&& filterTypeId == FilterTypes.SUGGESTED) {
//shows all article from store (filter type 0) if no article is mapped in suggested.
filterTypeId = FilterTypes.ALL;
load(filterText, categoryId, offset, numberOfRows);
}
}
articleDetailsList.addAll(homeActivityViewModel.getArticles(getAllStockItemByCustomerResponseDataList));
homeActivityViewModel.insertAllStockItemByCustomer(context,filterTypeId
, getAllStockItemByCustomerResponseDataList, null);
articleRecyclerViewAdapter.notifyDataChanged();
} else {
}
} else {
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
Newbie here - As practice, I am using google places to search for nearby restaurants and get their details . I am also using google place details to get more info about the specific restaurant. I am using Retrofit to parse the data.
The problem:
I have three ArrayList, restaurantName, restaurantAddress, and restaurantPhoneNum. I cannot pass all three ArrayLists into my recyclerview adapter.
Get Nearby restaurants method:
private void getNearbySearchUrl(final double latitude, final double longitude, final String nearbyPlace){
MapInterface retrofit = new Retrofit.Builder()
.baseUrl("https://maps.googleapis.com/maps/api/place/nearbysearch/")
.addConverterFactory(GsonConverterFactory.create())
.build().create(MapInterface.class);
Call<Result> call = retrofit.getResults("65.9667,-18.5333", PROXIMITY_RADIUS, "restaurant", placesKey);
call.enqueue(new Callback<Result>() {
#Override
public void onResponse(Call<Result> call, Response<Result> response) {
ArrayList<String> restaurantName = new ArrayList<>();
ArrayList<String> restaurantAddress = new ArrayList<>();
GetNearbyPlaces b = new GetNearbyPlaces();
for(int i = 0; i <= response.body().getResults().size() - 1 ; i++) {
restaurantName.add(response.body().getResults().get(i).getName());
restaurantAddress.add(response.body().getResults().get(i).getVicinity());
//get telephone number through this method
getPlaceDetailsUrl(response.body().getResults().get(i).getPlaceId());
}
Collections.reverse(restaurantName);
Collections.reverse(restaurantAddress);
adapter = new RestaurantListAdapter(restaurantName, restaurantAddress,
response.body().getResults().size());
mRecyclerView.setAdapter(adapter);
}
#Override
public void onFailure(Call<Result> call, Throwable t) {
Log.d("Matt", "fail");
}
});
}
Get more info about the restaurant method:
public String getPlaceDetailsUrl(final String placeId) {
final String mPlace = placeId;
MapInterface retrofit = new Retrofit.Builder()
.baseUrl("https://maps.googleapis.com/maps/api/place/details/")
.addConverterFactory(GsonConverterFactory.create())
.build().create(MapInterface.class);
Call<Result> call = retrofit.getPlaceDetails(placeId, placesKey);
call.enqueue(new Callback<Result>() {
#Override
public void onResponse(Call<Result> response) {
ArrayList<String> restaurantPhoneNum = new ArrayList<>();
if (response.body().getResult().getFormattedPhoneNumber() == null ){
return;
}
restaurantPhoneNum.add(response.body().getResult().getFormattedPhoneNumber());
}
#Override
public void onFailure(Call<Result> call, Throwable t) {
return;
}
});
}
Adapter:
public class RestaurantListAdapter extends RecyclerView.Adapter<RestaurantListAdapter.RestaurantListHolder> {
ArrayList<String> restaurantName = new ArrayList<>();
ArrayList<String> restaurantAddress = new ArrayList<>();
ArrayList<String> restaurantPhoneNum = new ArrayList<>();
private int restaurantCount;
public RestaurantListAdapter(ArrayList<String> resName, ArrayList<String> resAddress,
int resCount){
restaurantName = resName;
restaurantAddress = resAddress;
restaurantCount = resCount;
}
public RestaurantListAdapter(ArrayList<String> resPhoneNum){
restaurantPhoneNum = resPhoneNum;
}
#Override
public RestaurantListHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.restaurant_list_item, parent, false);
RestaurantListHolder viewHolder = new RestaurantListHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(RestaurantListHolder holder, int position) {
Log.d("Matt1", String.valueOf(position) + " size:" + String.valueOf(restaurantName.size()) + restaurantName.toString());
holder.bindView(position);
}
#Override
public int getItemCount() {
return restaurantCount;
}
public class RestaurantListHolder extends RecyclerView.ViewHolder{
public TextView mRestaurantName;
public TextView mAddress;
public TextView mPhone;
public TextView mDistance;
public ImageView mRestaurantPic;
public RestaurantListHolder(View itemView) {
super(itemView);
mRestaurantName = (TextView) itemView.findViewById(R.id.tvRestaurantName);
mAddress = (TextView) itemView.findViewById(R.id.tvAddress);
mPhone = (TextView) itemView.findViewById(R.id.tvPhone);
}
public void bindView(int arrayNum){
mRestaurantName.setText(restaurantName.get(arrayNum));
mAddress.setText(restaurantAddress.get(arrayNum));
mPhone.setText("123 123 123 123 ");
mRestaurantPic.setImageResource(R.drawable.rzkibble_chinese);
}
}
}
Ok, first advice, try to think about it a little bit on your own. I know that you know how to solve this :).
Your question is left unclear:
you want to update adapter after both requests are executed
you will update adapter after each request
I suppose that you want to implement this in a first way.
You need to have the logic which executes requestOne, executes requestTwo and when both requests are successful, update adapter. You can do this with some global flags arePlacesLoaded and areDetailsLoaded and to have third method which receives request result and inspects these flags.
A manual way (pseudo)
boolean arePlacesLoaded;
boolean areDetailsLoaded;
void loadPlaces(){
if(success){
arePlacesLoaded = true;
savePlaces(places)
updateAdapter()
}
}
void loadDetails(){
if(success){
areDetailsLoaded = true;
saveDetails(details)
updateAdapter()
}
}
void updateAdaper(){
if(arePlacesLoaded && areDetailsLoaded){
adaper.onDataChange(places, details);
}
}
You can use some queues where you put requests and once the requests are executed and queue is empty -> update adapter.
Another, better way would be with usage of RxJava and its zipWith operator, where implementation takes few lines only (having in mind that retrofit is moved somewhere to core of the app)
Here is a marble diagram of this operator:
So, request one executes, and then request two. You will provide a function which is going to merge data into some better object called Hotel, and you will feed the adapter with the array of Hotel objects.