I want to update the items inside RecyclerView using sending request to API and a timer.
This is my code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
get_items();
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
synchronized public void run() {
get_items();
}
}, TimeUnit.SECONDS.toMillis(3), TimeUnit.SECONDS.toMillis(3));
}
public void get_items(){
Client client = ServiceGenerator.createService(Client.class,getBaseContext());
final Call<List<Items>> call = Client.getItems("items");
call.enqueue(new Callback<List<Items>>() {
#Override
public void onResponse(Call<List<Items>> call, Response<List<Items>> response) {
List<Items> items = response.body();
ItemsAdapter adapter = new ItemsAdapter(items);
recyclerView = findViewById(R.id.chatrecycler);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(Activity.this, LinearLayoutManager.VERTICAL,true));
}
#Override
public void onFailure(Call<List<Items>> call, Throwable t) {
}
});
}
And every 3 seconds the list updates but it starts from beginning.
What should I do to keep the position of the list during update??
You should not create new RecyclerView object in every api call. Also you are assigning adapter and LayoutManager with yout RecycletView in each call.
You just need to setAdapter with your RecycletView once and then second time just add new data in your adapter list and notify your adapter to refresh.
Please check below code.
private ItemsAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
recyclerView = findViewById(R.id.chatrecycler);
recyclerView.setLayoutManager(new LinearLayoutManager(SingleChat.this, LinearLayoutManager.VERTICAL,true));
get_items();
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
synchronized public void run() {
get_items();
}
}, TimeUnit.SECONDS.toMillis(3), TimeUnit.SECONDS.toMillis(3));
}
public void get_items(){
Client client = ServiceGenerator.createService(Client.class,getBaseContext());
final Call<List<Items>> call = Client.getItems("items");
call.enqueue(new Callback<List<Items>>() {
#Override
public void onResponse(Call<List<Items>> call, Response<List<Items>> response) {
List<Items> items = response.body();
if(adapter == null) {
adapter = new ItemsAdapter(items);
recyclerView.setAdapter(adapter);
} else {
runOnUiThread(new Runnable() {
#Override
public void run() {
adapter.addMoreItems(items);
adapter.notifyDatasetChanged();
}
});
}
}
#Override
public void onFailure(Call<List<SingleChatItem>> call, Throwable t) {
}
});
}
Add one method in your ItemsAdapter.java like,
public void addMoreItems(List<Items> items) {
this.items = items;
}
I have not tested this code myself.But it must work.Let me know if it doesn't.
final ItemsAdapter adapter = null;
ArrayList<Items> items = null;
recyclerView = findViewById(R.id.chatrecycler);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(Activity.this, LinearLayoutManager.VERTICAL,true));
call.enqueue(new Callback<List<Items>>() {
#Override
public void onResponse(Call<List<Items>> call, Response<List<Items>> response) {
List<Items> newItems = response.body();
if(adapter == null){
adapter = new ItemsAdapter(items);
items.addAll(newItems);
}else{
int i = 0;
for(Item item : items){
int index = newItems.indexOf(item);
item.stuff = newItems.get(index).stuff;
adapter.notifyItemChanged(i);
i++;
}
}
}
#Override
public void onFailure(Call<List<Items>> call, Throwable t) {
}
});
Related
So I have a app where I am displaying the products list from the database using a Recycler View.
There is delete button beside each item which is working fine.
And there is a scan item button which opens a BARCODE SCANNER which on succesfull scanning adds the new product to the database and goes back to the Recycler View display is supposed to refresh the view and display the new Item.
but I am having problem with refreshing the recycler view on adding a new Product.
there is also a delete product function which works perfectly so I tried to do the add item method the same way, but the recycler view doesn't refresh.
UserPage activity
public class UserPage extends AppCompatActivity implements ProductAdaptar.clickedItem {
Toolbar toolbar;
RecyclerView recyclerView;
String rfidNo;
public static String barcode;
Button scanItem;
Button payBill;
TextView total;
ProductAdaptar productAdaptar;
Call<List<UserLoginResp>> productList;
List<UserLoginResp> productListsItems = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_page);
toolbar=findViewById(R.id.toolbar);
recyclerView=findViewById(R.id.recyclerview);
scanItem = findViewById(R.id.scanItem);
payBill = findViewById(R.id.payBill);
total = findViewById(R.id.total);
scanItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(getApplicationContext(),ScannerView.class).putExtra("rfid",rfidNo));
}
});
payBill.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
checkout();
}
});
LinearLayoutManager manager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(manager);
recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL));
productAdaptar = new ProductAdaptar(this::clickedItem, this);
Intent intent =getIntent();
if(intent.getExtras()!=null){
rfidNo= intent.getStringExtra("rfid");
}
getAllProducts(rfidNo);
}
public void getAllProducts(String rfidno){
LinearLayoutManager manager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(manager);
productAdaptar = new ProductAdaptar(this::clickedItem,this);
productList= ApiClient.getUserPageService().getCartItems(rfidno);
productList.enqueue(new Callback<List<UserLoginResp>>() {
#Override
public void onResponse(Call<List<UserLoginResp>> call, Response<List<UserLoginResp>> response) {
if (response.isSuccessful()) {
productListsItems = response.body();
productAdaptar.setData(productListsItems);
RecyclerView recyclerView = findViewById(R.id.recyclerview);
recyclerView.setAdapter(productAdaptar);
getTotal();
}
}
#Override
public void onFailure(Call<List<UserLoginResp>> call, Throwable t) {
Log.e("listfailed",t.getLocalizedMessage());
}
});
}
public void getTotal(){
Call<getBill> bill = ApiClient.getUserPageService().getBill(rfidNo);
bill.enqueue(new Callback<getBill>() {
#Override
public void onResponse(Call<getBill> call, Response<getBill> response) {
if(response.isSuccessful()){
getBill getBill = response.body();
String bill = String.valueOf(getBill.getBill());
total.setText(bill);
}
}
#Override
public void onFailure(Call<getBill> call, Throwable t) {
Log.e("bill error",""+t);
}
});
}
public void checkout(){
Call<String> payment= APIClientString.getUserPageService().checkout(rfidNo);
payment.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if(response.isSuccessful()){
getAllProducts(rfidNo);
Toast.makeText(UserPage.this, "Payment Successful", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
Log.e("paymentfail",""+t);
}
});
}
#Override
public void clickedItem(UserLoginResp userLoginResp) {
Log.e("clicked prodcut", userLoginResp.toString());
}
}
ScannerView Class
enter #Override
public void handleResult(Result rawResult) {
barcode = rawResult.getText();
if(addItem(barcode,rfidNo)) {
userPage.getAllProducts(rfidNo);
}
onBackPressed();
}
public boolean addItem(String barcode,String rfidNo){
final boolean[] res = {false};
Call<String> resp = APIClientString.getUserPageService().addProduct(barcode,rfidNo);
resp.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if(response.isSuccessful()){
Toast.makeText(ScannerView.this, response.body().toString(), Toast.LENGTH_SHORT).show();
res[0] =true;
}
}
This is the scanner class which is suppose to call the call the getAllproducts function from the UserPage Activity to refresh the view. It shows no error but the recycler view doesn't get updated.
This is the Adapter Class
public class ProductAdaptar extends RecyclerView.Adapter<ProductAdaptar.ProductAdaptarVH> {
private List<UserLoginResp> productListItems;
private UserPage context;
private clickedItem clickedItem;
public ProductAdaptar(clickedItem clickedItem, UserPage activity) {
this.clickedItem = clickedItem;
this.context= activity;
}
public void setData(List<UserLoginResp> productListItems) {
this.productListItems = productListItems;
notifyDataSetChanged();
}
#NonNull
#Override
public ProductAdaptarVH onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new ProductAdaptar.ProductAdaptarVH(LayoutInflater.
from(context).inflate(R.layout.row_products,parent,false));
}
#Override
public void onBindViewHolder(#NonNull ProductAdaptarVH holder, int position) {
UserLoginResp userLoginResp = productListItems.get(position);
String pName = userLoginResp.getProductName();
String pQuan = userLoginResp.getQuantity();
String pPrice = userLoginResp.getProductPrice();
String pBarcode = userLoginResp.getProductID();
String userID = userLoginResp.getUserID();
holder.pName.setText(pName);
holder.pQuan.setText(pQuan);
holder.pPrice.setText(pPrice);
holder.delProdcut.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
delProduct(userID,pBarcode);
}
});
holder.moreDetails.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
public interface clickedItem{
public void clickedItem(UserLoginResp userLoginResp);
}
public void delProduct(String userID, String pBarcode){
Call<String> res = APIClientString.getUserPageService().deleteProduct(pBarcode,userID);
res.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if(response.isSuccessful()){
Toast.makeText(context, response.body().toString(), Toast.LENGTH_SHORT).show();
context.getAllProducts(userID);
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
Log.e("deletefailed",""+t);
}
});
}
#Override
public int getItemCount() {
return productListItems.size();
}
public static class ProductAdaptarVH extends RecyclerView.ViewHolder {
TextView pName;
TextView pQuan;
TextView pPrice;
Button delProdcut;
Button moreDetails;
public ProductAdaptarVH(#NonNull View itemView) {
super(itemView);
pName=itemView.findViewById(R.id.pName);
pQuan=itemView.findViewById(R.id.pQuantity);
pPrice=itemView.findViewById(R.id.pPrice);
delProdcut=itemView.findViewById(R.id.delProduct);
moreDetails=itemView.findViewById(R.id.moreDetails);
}
}
}
In this Product Apdapter there is a delete product item function
holder.delProdcut.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
delProduct(userID,pBarcode);
}
});
public void delProduct(String userID, String pBarcode){
Call<String> res = APIClientString.getUserPageService().deleteProduct(pBarcode,userID);
res.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if(response.isSuccessful()){
Toast.makeText(context, response.body().toString(), Toast.LENGTH_SHORT).show();
context.getAllProducts(userID);
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
Log.e("deletefailed",""+t);
}
});
}
Which also calls the getProduts function from UserPage activity and it works perfectly fine but the AddItem function doesn't refresh the view.
The Retrofit APIs are working completly fine too, the problem is only with refreshing the recycler view display on Item Add.
I am new to android coding so I can't seem to understand how to do it.
As you are refreshing your adapter using getAllProducts() method but it is being called in onCreate(). Now whenever you start a ScannerView activity, UserPage activity gets paused and then started (not created) when ScannerView activity finishes. So, you should call getAllProducts() in onStart() method like this:
#Override
protected void onStart() {
super.onStart();
Intent intent = getIntent();
if (intent.getExtras() != null) {
rfidNo = intent.getStringExtra("rfid");
}
getAllProducts(rfidNo);
}
This is the code of my fragment, when i created a recyclerview, when i can see a list if I click on I can see detail in antoher page, but when I come back I can't see the list.
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
SharedPreferences pref = getActivity().getSharedPreferences("pref", Context.MODE_PRIVATE);
adapter = new PlatformAdapter(data);
recyclerView = view.findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
//recyclerView.setAdapter(adapter);
if (pref.getBoolean("firstTime", true)) {
RequestVolley.getInstance(getContext())
.doGetRequest("http://www....json", new RequestVolley.OnCompleteCallback() {
#Override
public void onCompleted(String response) {
try {
JSONArray jsonArray = new JSONArray(response);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject object = jsonArray.optJSONObject(i);
if (object != null) {
data.add(Platform.parseJSON(object));
}
}
adapter.notifyDataSetChanged();
} catch (JSONException e) {
e.printStackTrace();
}
}
});
new Thread(new Runnable() {
#Override
public void run() {
DB.getInstance(getContext()).getPlatformDao().insert(data);
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
adapter.notifyDataSetChanged();
}
});
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean("firstTime", false);
editor.apply();
final PlatformAdapter adapter = new PlatformAdapter(data);
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
recyclerView.setAdapter(adapter);
}
});
}
}).start();
}
if(pref.getBoolean("firstTime",false)){
new Thread(new Runnable() {
#Override
public void run() {
data.addAll(DB.getInstance(getContext()).getPlatformDao().findAll());
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
adapter.notifyDataSetChanged();
}
});
final PlatformAdapter adapter = new PlatformAdapter(data);
recyclerView.post(new Runnable() {
#Override
public void run() {
recyclerView.setAdapter(adapter);
}
});
}
}).start();
}
}
}
this is the code of my app in the point of inserto or reload from roomDatabase, the first time I oper the page it's ok, but the second time I can see only a white page.Why? I dont'have any error.
Why it isn't on the DB, or it isn't reload?
This is my Dao
#Dao
public interface PlatformDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertOne(Platform platform);
#Insert(onConflict=OnConflictStrategy.REPLACE)
public void insert(List<Platform> platforms);
#Insert(onConflict=OnConflictStrategy.REPLACE)
public void insertVarArgs(Platform...platforms);
#Update
public void update(Platform platform);
#Delete
public void delete(Platform platform);
#Query("SELECT * FROM platforms")
public List<Platform> findAll();
#Query("SELECT * FROM platforms WHERE cdenominazione__=:cdenominazione__")
public Platform findById(String cdenominazione__);
}
When you add the fragment are you doing an add or a replace? Aso are you adding the fragment to the backstack?
I always use the approach to show the data in recyclerview using Volley is to create Adapter in Oncreate method and then notify after receiving response from volley using notifyDataSetChanged.
I tried to use the same approach in Retrofit but data is not showing in Recyclerview,but if instead of setting adapter in Oncreate method if I set Adapter in retrofit after receiving response from Retrofit then it works perfectly
My question is that
(1) why the data is not showing in Retrofit if i create adapter in Oncreate am i doing something wrong?
(2)Is it a good approach to create adapter in Oncreate and then notifying it after receiving response or shoud I set adapter after getting response?
If I set adapter after getting response like this
#Override
public void onResponse(Call<SubCatModelList> call, Response<SubCatModelList> response) {
Log.d("subcategoryJsonResponse", String.valueOf(response));
Gson gson = new Gson();
String successResponse = gson.toJson(response.body());
Log.d("aaaaaa", String.valueOf(successResponse));
if (response.isSuccessful()) {
subCatModel =response.body().getSubCategories();
Log.d("asaaaaaaa", String.valueOf(subCatModel));
subCategoryAdapter = new SubCategoryAdapter(BinaryCommissionActivity.this, subCatModel);
FixedGridLayoutManager manager = new FixedGridLayoutManager();
manager.setTotalColumnCount(1);
rvBinaryCommission.setLayoutManager(manager);
rvBinaryCommission.setAdapter(subCategoryAdapter);
rvBinaryCommission.addItemDecoration(new DividerItemDecoration(BinaryCommissionActivity.this, DividerItemDecoration.VERTICAL));
}
then every time this line initializes adapter and creates object whenver api hits and i think its a bad approach
subCategoryAdapter = new
SubCategoryAdapter(BinaryCommissionActivity.this, subCatModel);
public class BinaryCommissionActivity extends AppCompatActivity {
int scrollX = 0;
List<SubCatModel> subCatModel = new ArrayList<>();
SubCategoryAdapter subCategoryAdapter;
VolleyCustomClass volleyCustomClass;
#BindView(R.id.toolbarcustom) Toolbar toolbarcustom;
#BindView(R.id.spinner_binary_commission) Spinner spinnerBinaryCommission;
#BindView(R.id.searchViewBinary) SearchView searchViewBinary;
#BindView(R.id.headerScroll) HorizontalScrollView headerScroll;
#BindView(R.id.rvBinaryCommission) RecyclerView rvBinaryCommission;
#BindView(R.id.swipeRefreshBinaryList) SwipeRefreshLayout swipeRefreshBinaryList;
private AlertDialog dialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_binary_commission);
ButterKnife.bind(this);
toolbarcustom = findViewById(R.id.toolbarcustom);
setSupportActionBar(toolbarcustom);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle("Binary Commission");
volleyCustomClass = new VolleyCustomClass(getApplicationContext());
dialog = new SpotsDialog.Builder().setContext(this)
.setMessage("Loading...")
.setCancelable(false).build();
spinnerData();
setUpRecyclerView();
prepareClubData();
recyclerviewScrollListener();
searchFilter();
swipeRefreshBinaryList.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
if (!InternetUtils.checkForInternet(getApplicationContext())) {
swipeRefreshBinaryList.setRefreshing(false);
FancyToast.makeText(getApplicationContext(), "No Internet Connection", FancyToast.LENGTH_LONG, FancyToast.ERROR, false).show();
} else {
prepareClubData();
}
}
});
}
#Override
public boolean onSupportNavigateUp() {
onBackPressed();
return super.onSupportNavigateUp();
}
/**
* Recyclerview ScrollListener
*/
private void recyclerviewScrollListener() {
rvBinaryCommission.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
scrollX += dx;
headerScroll.scrollTo(scrollX, 0);
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
});
}
/**
* Search Filter
*/
private void searchFilter() {
searchViewBinary.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
subCategoryAdapter.getFilter().filter(query);
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
subCategoryAdapter.getFilter().filter(newText);
return false;
}
});
}
/**
* Adding data to spinner
*/
private void spinnerData() {
List<String> show_entries = new ArrayList<String>();
show_entries.add("5");
show_entries.add("10");
show_entries.add("15");
show_entries.add("20");
show_entries.add("All");
ArrayAdapter<String> dataAdapterShowEntries = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, show_entries);
dataAdapterShowEntries.setDropDownViewResource(R.layout.layout_spinner_item);
spinnerBinaryCommission.setAdapter(dataAdapterShowEntries);
spinnerBinaryCommission.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
Object item = adapterView.getItemAtPosition(position);
if (item != null) {
// Toast.makeText(getContext(), item.toString(), Toast.LENGTH_SHORT).show();
}
// Toast.makeText(getContext(), "Selected", Toast.LENGTH_SHORT).show();
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {
// TODO Auto-generated method stub
}
});
}
private void prepareClubData() {
if (!InternetUtils.checkForInternet(getApplicationContext())) {
FancyToast.makeText(getApplicationContext(), "No Internet Connection", FancyToast.LENGTH_LONG, FancyToast.ERROR, false).show();
return;
} else {
dialog.show();
if (subCatModel != null) {
subCatModel.clear();
}
/**
* Volley
*/
/* volleyCustomClass.callGetServer(URLs.subcategoryURL, new VolleyCallback() {
#Override
public void onSuccess(String response) {
Log.d("subcategoryJsonResponse", response);
try {
JSONObject obj = new JSONObject(response);
JSONArray productArray = obj.getJSONArray("sub-categories");
for (int i = 0; i < productArray.length(); i++) {
JSONObject productObject = productArray.getJSONObject(i);
SubCatModel subCatModelClass = new SubCatModel();
subCatModelClass.setSubcategoriesId(productObject.getString("Subcategories-Id"));
subCatModelClass.setCategoriesId(productObject.getString("categories-Id"));
subCatModelClass.setSubcategoriesName(productObject.getString("Subcategories-Name"));
subCatModel.add(subCatModelClass);
Log.d("subCategoryArraylist", String.valueOf(subCatModel));
}
dialog.dismiss();
subCategoryAdapter.notifyDataSetChanged();
swipeRefreshBinaryList.setRefreshing(false);
} catch (JSONException e) {
e.printStackTrace();
dialog.dismiss();
}
}
#Override
public void onError(VolleyError error) {
VolleyLog.e("Error: ", error.getMessage());
swipeRefreshBinaryList.setRefreshing(false);
}
});*/
/**
* Retrofit
*/
ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
Call<SubCatModelList> call = apiService.getList();
call.enqueue(new Callback<SubCatModelList>() {
#Override
public void onResponse(Call<SubCatModelList> call, Response<SubCatModelList> response) {
Log.d("subcategoryJsonResponse", String.valueOf(response));
Gson gson = new Gson();
String successResponse = gson.toJson(response.body());
Log.d("aaaaaa", String.valueOf(successResponse));
if (response.isSuccessful()) {
subCatModel =response.body().getSubCategories();
Log.d("asaaaaaaa", String.valueOf(subCatModel));
}
dialog.dismiss();
subCategoryAdapter.notifyDataSetChanged();
swipeRefreshBinaryList.setRefreshing(false);
}
#Override
public void onFailure(Call<SubCatModelList> call, Throwable t) {
swipeRefreshBinaryList.setRefreshing(false);
}
});
}
}
/**
* Handles RecyclerView for the action
*/
private void setUpRecyclerView() {
subCategoryAdapter = new SubCategoryAdapter(BinaryCommissionActivity.this, subCatModel);
FixedGridLayoutManager manager = new FixedGridLayoutManager();
manager.setTotalColumnCount(1);
rvBinaryCommission.setLayoutManager(manager);
rvBinaryCommission.setAdapter(subCategoryAdapter);
rvBinaryCommission.addItemDecoration(new DividerItemDecoration(BinaryCommissionActivity.this, DividerItemDecoration.VERTICAL));
}
}
Instead of initializing adapter everytime, you can use: (inside adapter class)
public void setItems(List<String> myList) { // Let's say it's List of Strings
this.myList = myList;
notifyDataSetChanged();
}
And everytime you change List with your data to display (in your case inside of onResponse()) you just call 'setItems()' on your adapter.
I have a problem with RecyclerView. I need to refresh it after clicking a long click. I tried to play with notifyDataSetChanged() method. But nothing works. Here is my code. In current situation item removes from database right away. But I have to move to another activity and come back to see any UI changes. Thanks in advance for any help.
public class AlarmsActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private List<Alarm> alarmsList = new ArrayList<>();
private AlarmRecyclerAdapter mAdapter;
private AppDataBase db;
private AlarmsDao alarmDao;
private OnClickedRecyclerAdapter<Alarm> recyclerListener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.alarms_activity);
db = App.getInstance().getDataBase();
alarmDao = db.AlarmDao();
recyclerView = findViewById(R.id.recycler);
recyclerListener = new OnClickedRecyclerAdapter<Alarm>() {
#Override
public void onLongClicked(final Alarm data) {
removeAlarm(data);
}
#Override
public void onClicked(Alarm data) {
}
};
mAdapter = new AlarmRecyclerAdapter(alarmsList, recyclerListener);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setAdapter(mAdapter);
mAdapter.notifyDataSetChanged();
getData();
}
public void getData(){
new Thread(new Runnable() {
#Override
public void run() {
for (Alarm alarm : alarmDao.getAll()){
alarmsList.add(alarm);
}
}
}).start();
}
public void removeAlarm(final Alarm alarm){
new Thread(new Runnable() {
#Override
public void run() {
Log.e("xxx", "Long clicked");
alarmDao.deleteAlarm(alarm);
}
}).start();
}
}
OnClickListener
public interface OnClickedRecyclerAdapter<Alarm> {
void onLongClicked(Alarm data);
void onClicked(Alarm data);
}
Adapter.
public class AlarmRecyclerAdapter extends RecyclerView.Adapter<AlarmViewHolder> {
List<Alarm> alarms;
OnClickedRecyclerAdapter listener;
private AppDataBase db;
private AlarmsDao alarmsDao;
public AlarmRecyclerAdapter(List<Alarm> alarms, OnClickedRecyclerAdapter listener) {
this.alarms = alarms;
this.listener = listener;
db = App.getInstance().getDataBase();
alarmsDao = db.AlarmDao();
}
#NonNull
#Override
public AlarmViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycler, parent, false);
return new AlarmViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull AlarmViewHolder holder, final int position) {
final Alarm alarm = alarms.get(position);
holder.time.setText(alarm.getHour()+" : " + alarm.getMinute());
holder.days.setText(alarm.getDays());
holder.isOn.setChecked(alarm.isIs_on());
holder.isOn.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
Log.e("xxx", b + " ");
}
});
holder.cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
listener.onClicked(alarm);
}
});
holder.cardView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
listener.onLongClicked(alarm);
return true;
}
});
}
#Override
public int getItemCount() {
return alarms.size();
}
}
Change this:
holder.cardView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
listener.onLongClicked(alarm);
return true;
}
});
To this:
holder.cardView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
listener.onLongClicked(alarm);
alarms.remove(holder.getAdapterPosition()); //or alarms.remove(alarm);
notifyItemRemoved(holder.getAdapterPosition()); //or notifyDataSetChanged();
return true;
}
});
The problem is that you're removing the alarm from your database when you call removeAlarm() in your Activity, but you're not actually removing it from the alarms list in your Adapter. The change isn't being reflected because the data set never changes in your code. When you recreate your Activity, you pass the new database list into the Adapter, so it updates then.
I also recommend not doing this:
public void getData(){
new Thread(new Runnable() {
#Override
public void run() {
for (Alarm alarm : alarmDao.getAll()){
alarmsList.add(alarm);
}
}
}).start();
}
While alarmsList in your Activity and alarms are the same instance, and thus reflect the same additions and removals, there's no guarantee this logic will complete before the RecyclerView is inflated and the adapter attached. That means you could end up with missing data, especially on slower devices.
Unless you have an absolute ton of Alarms, there's no need to run that in a new Thread. You could just move that for-loop right above where you initialize the Adapter, and use the addAll() method:
alarmsList.addAll(alarmDuo.getAll());
mAdapter = //your assignment from your code
If you do need it to be async, attach the Adapter later, inside your Thread:
public void getData(){
new Thread(new Runnable() {
#Override
public void run() {
alarmsList.addAll(alarmDuo.getAll());
runOnUiThread(new Runnable() {
mAdapter = new AlarmRecyclerAdapter(alarmsList, recyclerListener);
recyclerView.setAdapter(mAdapter);
}
}
}).start();
}
Remove the assignment, adapter setting and notifyDataSetChanged() calls from your onCreate() method:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.alarms_activity);
db = App.getInstance().getDataBase();
alarmDao = db.AlarmDao();
recyclerView = findViewById(R.id.recycler);
recyclerListener = new OnClickedRecyclerAdapter<Alarm>() {
#Override
public void onLongClicked(final Alarm data) {
removeAlarm(data);
}
#Override
public void onClicked(Alarm data) {
}
};
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(mLayoutManager);
getData();
}
I have this recyclerview that loads data from a server through a json api. the Data on the server will be updated frequently so i have a "swipe to refresh" in my application. I'm using volley to get the data from the server and i want to make it in such a way that, when the user swipe to refresh, volley will reload the json data from the server so that recyclerview can display the newly added items.
This is my onSwipeListener
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
adapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
}
}, 3000);
}
});
This is how volley is requesting the data
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
public ArrayList<Deal> userlist;
public DealAdapter adapter;
#Override
public void onResponse(String response) {
MainActivity.userList = this.userlist;
userList = new JsonConverter<Deal>().
toArrayList(response, Deal.class);
Collections.reverse(userList);
adapter = new DealAdapter(getApplicationContext(), userList);
MainActivity.adapter= this.adapter;
recyclerView.setAdapter(adapter);
}
But i can't figure out how to refresh the volley response data when onSwipeListener is called.
this is how i add the request to Queue
MySingleton.getInstance(getApplicationContext()).addToRequestQueue(stringRequest);
I think the problem is that you are re-assigning the Activity adapter, but never setting that new adapter back onto the Activity's RecyclerView.
Sure, you have recyclerView.setAdapter(adapter), but it is not clear where that reference came from.
Similarly, the Activity's userList is empty.
MainActivity.userList = this.userlist; // This is null
userList = new JsonConverter<Deal>(). // This should have been first
toArrayList(response, Deal.class);
So, anyways, as I was saying in the comments, static variables are really not a good way to "toss data over the Java class wall".
Let's say you have this class to handle all the Volley calls. Notice that the Response.Listener interface is passed as the parameter.
public class JsonApi {
private Context mContext;
private static JsonApi instance;
private JsonApi(Context c) { this.mContext = c; }
public static JsonApi getInstance(Context context) {
if (instance == null) instance = new JsonApi(context);
return instance;
}
public void getSomeData(Response.Listener<String> success) {
StringRequest stringRequest = new StringRequest(Request.Method.GET, url, success);
MySingleton.getInstance(this.mContext).addToRequestQueue(stringRequest);
}
}
Now, over in the Activity or Adapter, you can get this class and re-do Volley requests whenever.
(Apologies for any typos. This is untested, but it just gives a sample of how I would layout the code).
public class MainActivity implements
SwipeRefreshLayout.OnRefreshListener,
Response.Listener<String> {
private List<Deal> userList;
private DealAdapter adapter;
private RecyclerView recyclerView;
private SwipeRefreshLayout swipeRefreshLayout;
private JsonApi api;
#Override
public void onResponse(String response) {
userList.clear();
userList.addAll(new JsonConverter<Deal>().
toArrayList(response, Deal.class));
Collections.reverse(userList);
// Handle adapter updates here
if (swipeRefreshLayout.isRefreshing()) {
swipeRefreshLayout.setRefreshing(false);
}
recyclerView.notifyDataSetChanged();
}
#Override
public void onRefresh() {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// Start an API call to refresh data
api.getSomeData(MainActivity.this);
}
}, 3000);
}
#Override
protected void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.activity_main);
api = JsonApi.getInstance(MainActivity.this);
userList = new ArrayList<Deal>();
adapter = new DealAdapter(MainActivity.this, userList);
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
// set layout manager ... etc.
recyclerView.setAdapter(adapter);
api.getSomeData(MainActivity.this); // start the first call when the activity starts
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefresh);
swipeRefreshLayout.setOnRefreshListener(MainActivity.this);
}
}
Okay So this is what worked for me. I had to call StringRequest again in the OnRefreshListener with new variable so that when the user swipes, volley requests the json data again and pass it to the adapter as shown below.
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
StringRequest mrequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
public ArrayList<Deal> userlist;
public DealAdapter adapter;
#Override
public void onResponse(String response) {
MainActivity.userList = this.userlist;
userList = new JsonConverter<Deal>().
toArrayList(response, Deal.class);
Collections.reverse(userList);
adapter = new DealAdapter(getApplicationContext(), userList);
MainActivity.adapter= this.adapter;
recyclerView.setAdapter(adapter);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, error.toString());
Toast.makeText(getApplicationContext(), "Something Went wrong: " + "'" + error + "'", Toast.LENGTH_LONG).show();
}
});
MySingleton.getInstance(getApplicationContext()).addToRequestQueue(mrequest);
adapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
}
}, 3000);
}
});