I have an app that has 10 tabs (categorys) Each tab requires a different set of data to be called from a PHP/MySQL WebService on a remote server.
i'm passing category id (catid) to doinBackground function in parameter then i retrive the json and build the Array iList:
#Override
protected List doInBackground(String... params) {
int catid = Integer.parseInt(params[0]);
ProductApi prd = new ProductApi();
try {
List<ProductDetails> arrayproduct = prd.ProductApi(catid);
ArrayList<Data> al = new ArrayList<Data>();
for (ProductDetails prditem : arrayproduct) {
al.add(new Data(new String[] {
"Product",
prditem.getProductName(),
prditem.getProductPrice()},
new int[] { R.drawable.popularity_img5 }));
}
iList.addAll(al);
return arrayproduct;
} catch (IOException e) {
e.printStackTrace();
return null;
}
the problem i'm facing
is that i have the same product list in all tabs , because i don't know where i have to execute my AsyncTask on every horizontal Scroll , retrieve the current CatId (Category Id) and load new data on the current tab the user is viewing .
do i have to create many adapter as categories tabs i have ?
i Can get the CatId ( Category Id ) on instantiateItem but it don't let me execute the loaddata method getItemCount() in this case return Array List 0
#Override
public Object instantiateItem(ViewGroup container, int pos) {
int CatId = tinydb.getInt("CategoryId"+pos);
Log.w("PageAdapter", "-------------------------------------instantiateItem pos:"+pos+" CatId: "+CatId);
final View v = getLayoutInflater(null).inflate(R.layout.pager_card_view, null);
RecyclerView recList = (RecyclerView) v.findViewById(R.id.cardList);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
recList.setHasFixedSize(true);
//LinearLayoutManager llm = new LinearLayoutManager(getActivity());
StaggeredGridLayoutManager llm = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
llm.setOrientation(StaggeredGridLayoutManager.VERTICAL);
// use a linear layout manager
recList.setLayoutManager(llm);
// create an Object for Adapter
CardAdapter ca = new CardAdapter();
// set the adapter object to the Recyclerview
recList.setAdapter(ca);
((MainActivity) getActivity()).enableActionBarAutoHide(recList);
container.addView(v,
android.view.ViewGroup.LayoutParams.MATCH_PARENT,
android.view.ViewGroup.LayoutParams.MATCH_PARENT);
return v;
}
Related
In the parent activity, I have an edit text in a toolbar, and user can make a search through the data displayed by the recyclerview.
When the user push enter key down, the string in the edittext is sent to the fragment by :
Bundle bundle = new Bundle();
bundle.putString(predResult, placeid);
MapFragment mapFragment = new MapFragment();
ListRestFragment listRestFragment = new ListRestFragment();
mapFragment.setArguments(bundle);
listRestFragment.setArguments(bundle);
getSupportFragmentManager().beginTransaction()
.replace(R.id.map, mapFragment)
.replace(R.id.list_frag, listRestFragment)
.commit();
but, unfortunatly, the recyclerview is not resfreshed while my adapter is notified the data is changed as it shown below:
private void queryToList(Query query) {
query.addSnapshotListener((queryDocumentSnapshots, e) -> {
restaurantList = queryDocumentSnapshots.toObjects(Restaurant.class);
if (!myPrediction.contains("myPrediction")) {
System.out.println(myPrediction);
for (Restaurant item : restaurantList) {
if (item.getRestaurantID().contains(myPrediction)) {
restaurantListPred = new ArrayList<>();
restaurantListPred.add(item);
updateUI(restaurantListPred);
}
}
} else updateUI(restaurantList);
});
}
private void updateUI(List<Restaurant> restaurants) {
configureFab();
configureRecyclerView(restaurants);
}
private void configureRecyclerView(List<Restaurant> restaurant) {
this.adapter = new RestaurantAdapterClassic(restaurant);
this.recyclerView.setAdapter(this.adapter);
this.recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL);
recyclerView.addItemDecoration(new SimpleItemDecorator(getContext()));
}
the new List is updated automatically when the user makes his request, but the recyclerView doesn't display the new data.
if you want to check my adapter implementation:
public class RestaurantAdapterClassic extends RecyclerView.Adapter<RestaurantViewHolder> {
private List<Restaurant> restaurantsList;
// CONSTRUCTOR
public RestaurantAdapterClassic(List<Restaurant> restaurants) {
this.restaurantsList = restaurants;
}
#Override
public RestaurantViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// CREATE VIEW HOLDER AND INFLATING ITS XML LAYOUT
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.listview_pattern, parent, false);
return new RestaurantViewHolder(view);
}
#Override
public void onBindViewHolder(RestaurantViewHolder viewHolder, int position) {
viewHolder.updateWithRestaurant(this.restaurantsList.get(position));
}
// RETURN THE TOTAL COUNT OF ITEMS IN THE LIST
#Override
public int getItemCount() {
return this.restaurantsList.size();
}
public Restaurant getRestaurant(int position) {
return this.restaurantsList.get(position);
}
public void filterList(List<Restaurant> filteredList) {
restaurantsList = filteredList;
notifyDataSetChanged();
}
}
where is my error or my misunderstanding?
EDIT SOLUTION -
Create an Interface
Actually, to send the new data data from my Parent Activity to my Fragment with a listener to observe when the data changes.
Keep The data reference sent to the adapter
Actually, the main big problem that I had was my adapter doesn't refresh when I sent new array. The reason was an adapter creates a reference with the list/array. if you want to refresh it, you need to keep this reference by get the list, erase it, and put/add new data inside by the method addALL for example.
First of all, adapter.notifyDataSetChanged(); doesn't have any effect in the code as inside updateUI you create the adapter every time you call it.
I think, the main problem is in here:
if (item.getRestaurantID().contains(myPrediction)) {
restaurantListPred = new ArrayList<>();
restaurantListPred.add(item);
updateUI(restaurantListPred);
}
This block doesn’t execute at all. Thats why the list not updated.
In my activity, I have a recyclerview which displays the images in grid. The activity shows images from a collection.When the collection already contains images and I open the activity, the recyclerview sets all the images properly. My problem is when the I select image from gallery or camera and in onActivityResult() method try to set that to recyclerview using adapter.notifyDataSetChanged() method, the recyclerview is not getting updated if the collection is empty. And the same code is working fine if the collection contains images and I try to upload new image to it. Basically, I'm selecting images from gallery and then in onActivityResult() method I'm setting the selected images to the recyclerview and the images are uploaded in the background to the server.
My Adapter code
private void setAdapter(final List<AlbumImagesBean> albumImages) {
adapter = new MyRecyclerAdapter<AlbumImagesBean>(R.layout.album_images_grid_adapter_item) {
#Override
protected void setupViewHolder(MAWRecyclerViewHolder holder, final int position, final AlbumImagesBean item) {
mawViewHolder = holder;
ProgressBar progressBar = holder.getView(R.id.progressBar);
((ImageView) holder.getView(R.id.ivImage)).setImageURI(Uri.parse(item.getImage()));
}
};
adapter.init(albumImages, false);
recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
recyclerView.addItemDecoration(new GridRecyclerViewDivider(5, 3));
recyclerView.setAdapter(adapter);
}
onActivityResult() method code
ArrayList<String> images = data.getStringArrayListExtra(UIConstants.INTENT_EXTRA_IMAGES);
if (isNotNull(images)) {
for (int i = 0; i < images.size(); i++) {
String path = images.get(i);
newRequests = new ArrayList<>();
AddFileRequest request = new AddFileRequest();
request.setUserId(userCache.getUserData().getUserId());
request.setType(UIConstants.UPLOAD_FILE_TYPE_ALBUM);
request.setTypeId(albumBean.getAlbumId());
request.setTitle(EMPTY);
request.setFile(path);
AlbumImagesBean imageBean = new AlbumImagesBean(path);
imageBean.setUploading(true);
imageBean.setLocalImage(true);
adapter.addItem(imageBean);
albumImages = adapter.getData();
newRequests.add(request);
uploadAttachmentAPICall();
}
adapter.notifyDataSetChanged();
}
I'm not able to understand what's going wrong.Thanks for any help.
public void addItem(T t) {
createDataIfNotExits();
data.add(t);
notifyChanges();
}
public List<T> getData() {
return data;
}
private void createDataIfNotExits() {
if (data == null) {
data = new ArrayList<>();
}
}
public void init(List<T> data, boolean isPagination) {
MAWRecyclerAdapter.isPagination = isPagination;
if (data == null) {
throw new NullPointerException("data can not be null");
}
this.data = data;
if (isPagination) {
this.data.add(null);
}
notifyChanges();
}
Also I have called adapter.notifyDataSetChanged( ); in the response of API call , but still the image is not added to recyclerview.
I have an app which displays the Matchday of some soccer competitions depending date of the match. I am using a spinner to re-create the adapter of my RecyclerView depending on the User selection with some dummyData(). But sometimes while the RecyclerView is first initialized or recycled it displays literally the format of my .xml.
I managed to reduce the level of visual glitch by adding a custom animation on the Adapter but still sometimes it just happens, like 10% of the time I change Matchday.
format_home.xml
This is the placeholder I created to design this format.
Gif showing problem:
HomeFragment.java
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_home, container, false);
mRecyclerView = view.findViewById(R.id.home_recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));
mSpinner = view.findViewById(R.id.spinner_home);
List<String> matchDay = new ArrayList<>();
for (int i = 0; i < 20; i++){
matchDay.add("Matchday " + (i + 1));
}
HomeSpinnerAdapter dataAdapter = new HomeSpinnerAdapter(getContext(), R.layout.format_home_spinner, matchDay);
mSpinner.setAdapter(dataAdapter);
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
mHomeItems = new ArrayList<>();
mDisposable.add(Observable.fromArray(mHomeItems)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(homeItems -> {
Map<String, List<Matchday>> hashMap = toMap(dummyData());
// Map Key
for (String date : hashMap.keySet()) {
Header header = new Header(date);
homeItems.add(header);
for (Matchday matchday : hashMap.get(date)) {
MatchItem matchItem = new MatchItem(matchday);
homeItems.add(matchItem);
}
}
mAdapter = new HomeAdapter(homeItems, getActivity(), mTeamViewModel);
mRecyclerView.setAdapter(mAdapter);
}));
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
return view;
}
// Map Value List<T>
private Map<String,List<Matchday>> toMap(List<Matchday> matchdays) {
Map<String, List<Matchday>> map = new TreeMap<>();
for (Matchday matchday : matchdays){
List<Matchday> value = map.get(matchday.getDate());
if (value == null) {
value = new ArrayList<>();
map.put(matchday.getDate(), value);
}
value.add(matchday);
}
return map;
}
public List<Matchday> dummyData(){
mMatchdayList = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 20; i ++){
mMatchdayList.add(new Matchday((i+1), buildRandomDateInCurrentMonth(), 1, random.nextInt(36), 0, 0, random.nextInt(36)));
}
return mMatchdayList;
}
If you have any feedback or need any other activity, let me know! TY
Thanks to #jantursky and some deep digging in the web I came across this series of articles and managed to identify that my RecyclerView was recreating all views in the absence of setStableIds(true) because in recyclers Scrap lists are the first place where the RecyclerView looks when searching for a ViewHolder.
So to resolve the problem:
Created an interface to retrieve List IDS.
Created a refreshAdapter() method in the Adapter.
Initialized and instantiate the Adapter outside the ` mSpinner.setOnItemSelectedListener() with setHasStableIds(true).
Inside the mSpinner.setOnItemSelectedListenercall refreshAdapter()
I developing news feed like twitter, facebook and etc.
I have a server that gives me portions of articles for 10 pieces.
When I scroll down, new articles are loaded, added to the database and displayed in RealmRecyclerView.
I added SwipeRefreshLayout to update the data (in the case when new quotes came in).
I have a problem with adding new items to the top when i use SwipeRefreshLayout.
My idea was to sort new articles on id. In that case, when I scroll down the items with the smaller ones the id will be at the bottom, and when I use SwipeRefreshLayout new articles with higher id are download and adding in top list.
MyFragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_view_all_quotes, container, false);
swipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh_layout);
swipeRefreshLayout.setOnRefreshListener(this);
Realm.init(getActivity());
realm = Realm.getDefaultInstance();
recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
adapter = new QuoteAdapter(realm.where(QuoteObject.class).findAllSortedAsync("id", Sort.DESCENDING), getActivity());
recyclerView.setAdapter(adapter);
return view;
}
#Override
public void onRefresh() {
int position = realm.where(QuoteObject.class).findAll().size();
DownloadDataFromServer.DownloadQuotes downloadQuotes =
new DownloadDataFromServer.DownloadQuotes();
try {
DownloadDataFromServer.DownloadQuotes.FlagQuotes = true;
downloadQuotes.execute(position+1).get();
}catch (Exception e){
Log.e(TAG, "Error", e);
}finally {
swipeRefreshLayout.setRefreshing(false);
}
}
RealmRecyclerViewAdapter
class QuoteAdapter extends RealmRecyclerViewAdapter<QuoteObject, QuoteAdapter.AllQuotesViewHolder> {
QuoteAdapter(OrderedRealmCollection<QuoteObject> quotes, Context context) {
super(quotes, true);
this.context = context;
if (quotes.size() == 0) downloadData(0);
}
.
.
.
#Override
public void onBindViewHolder(final QuoteAdapter.AllQuotesViewHolder viewHolder, int position) {
if (position == getData().size() - 1) downloadData(position);
....
}
But it does not work, the new data is loaded but does not add to top
Since I'm doing this for the first time, maybe I did not choose the right approach.
If you did this, tell how to create a news feed correctly.
If you don't have a updated field by which you can do
findAllSortedAsync("updated", Sort.DESCENDING)
Then you might want to consider adding a RANK field, and sort based on that.
For example,
// when downloading new items by "SwipeToRefresh" from top
// inside Realm transaction
RealmResults<FeedItem> feedItems = dao.findAll(realm);
for(int i = feedItems.size() - 1; i >= 0; i--) {
FeedItem feedItem = feedItems.get(i);
feedItem.setRank(feedItem.getRank() + response.size());
}
long rank = 1;
FeedItem defaultInstance = new FeedItem();
for(FeedItemDTO dto : response) {
FeedItem realmObject = mapper.toRealmObject(realm, dto, defaultInstance);
realmObject.setRank(rank++);
dao.insertOrUpdate(realm, realmObject);
}
And
// when download new items by scrolling to bottom
// inside Realm transaction
long count = dao.count(realm);
long rank = count + 1;
FeedItem defaultInstance = new FeedItem();
for(FeedItemDTO dto : response) {
FeedItem realmObject = mapper.toRealmObject(realm, dto, defaultInstance);
realmObject.setRank(rank++);
dao.insertOrUpdate(realm, realmObject);
}
public FeedItem toRealmObject(Realm realm, FeedItemDTO dto, FeedItemInterface defaultInstance) {
if(defaultInstance == null) {
defaultInstance = new FeedItem();
}
defaultInstance.setId(dto.getId());
RealmList<FeedCategory> categoryIds = new RealmList<FeedCategory>();
if(dto.getCategoryIds() != null) {
for(Long categoryId : dto.getCategoryIds()) {
FeedCategory feedCategory = feedCategoryRepository.findOne(realm, categoryId);
if(feedCategory != null) {
categoryIds.add(feedCategory);
}
}
}
defaultInstance.setCategoryIds(categoryIds);
defaultInstance.setCreatedTime(new Date(dto.getCreatedTime()));
defaultInstance.setUpdatedTime(new Date(dto.getUpdatedTime()));
defaultInstance.setTitle(dto.getTitle());
defaultInstance.setShortDescription(dto.getShortDescription());
defaultInstance.setNumberOfLikes(dto.getNumberOfLikes());
defaultInstance.setIsLiked(dto.getIsLiked());
defaultInstance.setIsFavorited(dto.getIsFavorited());
defaultInstance.setIsImportant(dto.getIsImportant());
defaultInstance.setImageUrl(dto.getImageUrl());
defaultInstance.setTypeCode(dto.getTypeCode());
defaultInstance.setResultTypeCode(dto.getResultTypeCode());
return defaultInstance;
}
I'm trying to add some date on scroll top in a recyclerview.
it is work, except the scroll, it mess everything up when some new data is added.
I saw this topics:
Link 1
Link 2
but none of them solved my problem.
maybe someone can help me with this...
I get some data using volley, like so:
try {
//Getting json
json = array.getJSONObject(i);
//Adding data to the object
PostObj.setImageUrl(json.getString(Config.TAG_PPIC));
...
} catch (JSONException e) {
e.printStackTrace();
}
//Adding object to the list
listLf.add(0, PostObj); //0, postobj
}
//Notifying the adapter that data has been added or changed
//adapter.notifyDataSetChanged(); the scroll will jump to position 0
adapter.notifyItemRangeChanged(0, adapter.getItemCount()); // still not working
}
I tried to add adapter.notifyItemRangeChanged(0, adapter.getItemCount()); but still the same thing.
also I tried:
// Save state
private Parcelable recyclerViewState;
recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();
// Restore state
recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);
and it doesn't work too. maybe I place it in the wrong place or something...
I want some suggestions what I'm doing wrong and how can I get the new data, notify the adapter without mess the scroll position?
edit----------------------------------
full class:
public class Comments extends BaseActivity {
private List<LfGetSet> listLf;
//Creating Views
private RecyclerView recyclerView;
private RecyclerView.LayoutManager layoutManager;
private RecyclerView.Adapter adapter;
//Volley Request Queue
private RequestQueue requestQueue;
private String requestCount = "0";
private String lastrequestCount = "0";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ViewGroup content = (ViewGroup) findViewById(R.id.content_frame);
getLayoutInflater().inflate(R.layout.activity_comments, content, true);
//Initializing Views
recyclerView = (RecyclerView) findViewById(R.id.recyclerViewLf);
recyclerView.setHasFixedSize(true);
//layoutManager = new LinearLayoutManager(this);
final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(layoutManager);
//Initializing our list
listLf = new ArrayList<>();
requestQueue = Volley.newRequestQueue(this);
//Calling method to get data to fetch data
getData();
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (isLastItemDisplaying(recyclerView)) {
getData();
}
}
});
//initializing our adapter
adapter = new LfAdapter(listLf, this);
//Adding adapter to recyclerview
recyclerView.setAdapter(adapter);
}
private JsonArrayRequest getDataFromServer(String requestCount) {
...
return jsonArrayRequest;
}
//This method will get data from the web api
private void getData() {
//Adding the method to the queue by calling the method getDataFromServer
requestQueue.add(getDataFromServer(requestCount));
lastrequestCount = requestCount;
//Incrementing the request counter
requestCount++;
}
//This method will parse json data
private void parseData(JSONArray array) {
for (int i = 0; i < array.length(); i++) {
//Creating the object
LfGetSet PostObj = new LfGetSet();
JSONObject json = null;
try {
//Getting json
json = array.getJSONObject(i);
//Adding data to the object
PostObj.setImageUrl(json.getString(Config.TAG_PPIC));
...
} catch (JSONException e) {
e.printStackTrace();
}
//Adding object to the list
listLf.add(0, PostObj); //0, postobj
}
//Notifying the adapter that data has been added or changed
adapter.notifyDataSetChanged();
}
//This method would check that the recyclerview scroll has reached the bottom or not
private boolean isLastItemDisplaying(RecyclerView recyclerView) {
if (recyclerView.getAdapter().getItemCount() != 0) {
int lastVisibleItemPosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
if (lastVisibleItemPosition != RecyclerView.NO_POSITION
&& lastVisibleItemPosition == 1
&& lastrequestCount != requestCount
&& requestCount != "0")
return true;
}
return false;
}
}
You can try this approach where you add a new item to the list as per normal and then update the data in the array and put the new one at the top.
// Add a new item the usual way so it goes at the bottom
int x = 0;
// Create a new Data list. New item goes 1st
// Create a Loop
myList.remove(x); // Remove item from the array at position
MyList myList = new MyList();
myList.add(x, theData); // Add data to array at position
// Now that new data has been inserted to the Array at position X update the List item at that same Position. I usually do this in the Background.
Handler handler = new Handler();
final int finalX = x;
final Runnable r = new Runnable() {
public void run() {
adapter.notifyItemChanged(finalX); // Notify Adapter that the Items data has changed and Update
}
};
handler.post(r);
x++;
Code that reads updated data from the DB and then updates List items on the fly with that new data. But in your case you need to add 1 new item and update the List with the new items data at position 0 in the array and on the List. Old items get their position shifted by 1.
public void updateStocks() {
System.out.println("Updating");
int rec = db.getRecordsCount() - 1;
List<Records> record = db.getAllRecords();
int x = 0;
for (Records cn : record) {
stocksList.remove(x);
Livestocks stocks = new Livestocks();
stocks.setID(cn.getID());
stocks.setName(cn.getName());
stocks.setTicker(cn.getTicker());
stocks.setPrice(cn.getPrice());
stocks.setOpen(cn.getOpen());
stocks.setChange(cn.getChange());
stocks.setPchange(cn.getPchange());
stocks.setDhigh(cn.getDhigh());
stocks.setDlow(cn.getDlow());
stocks.setYhigh(cn.getYhigh());
stocks.setYlow(cn.getYlow());
stocks.setVol(cn.getVol());
stocks.setShares(cn.getShares());
stocks.setIown(cn.getIown());
stocks.setMcap(cn.getMcap());
stocks.setStatus(cn.getStatus());
stocks.setExchange(cn.getExchange());
stocksList.add(x, stocks);
Handler handler = new Handler();
final int finalX = x;
final Runnable r = new Runnable() {
public void run() {
adapter.notifyItemChanged(finalX);
}
};
handler.post(r);
x++;
if (rec == x) {
//do nothing
}
}
}