add elements to top RealmRecyclerViewAdapter - android

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

Related

Android recyclerview not setting the items on first attempt

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.

RecyclerView Not Showing Data Passed From List Through Bundle

I have a recyclerview in a dialog fragment, which hosts a tab layout, containing a list of checkable items and a button each. When an item is selected, it is passed on click through the bundle to this parent dialog fragment like this:
doneBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
retrieveSessions();
}
});
private void retrieveSessions() {
NewProgramDialog dialog = new NewProgramDialog(); // parent dialog fragment is passed the list items here
Bundle bundle = new Bundle();
String s = new Gson().toJson(myItemsList);
bundle.putString(KEY_MY_SESSIONS, s);
bundle.putBoolean("from_selections", true);
dialog.setArguments(bundle);
dialog.show(getChildFragmentManager(), "NewProgramDialog");
dismiss();
}
The data for recyclerview is received through a bundle lie shown above and the list items are retrieved using Gson and converted to type String, a query is run on the database to get the session model class info.
These sessions are added to the adapter arraylist like below:
private void populateSessions() {
addProgramSessionsRV.setHasFixedSize(true);
addProgramSessionsRV.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false));
// TODO: 2/05/2019 show the added sessions here
Bundle bundle = getArguments();
if (bundle != null) {
String s = bundle.getString(KEY_MY_SESSIONS); / getting the items from bundle
myItemsList = new Gson().fromJson(s, ArrayList.class); // parsing to List<String> type
Log.d(TAG, "Items size:\t" + myItemsList.size()); //works
}
adapter = new AddProgramSessionsAdapter(getActivity(), sessionsList);
addProgramSessionsRV.setAdapter(adapter);
Log.d(TAG, "Items in adapter:\t" + adapter.getItemCount()); // shows total items received from bundle
for (String s : myItemsList){
Log.d(TAG, "Ids:\t" + s);
Sessions session = SQLite.select()
.from(Sessions.class)
.where(Sessions_Table.prog_sessionId.eq(s))
.querySingle();
sessionsList.add(session);
Log.d(TAG, "Session List Size:\t" + sessionsList.size());
adapter = new AddProgramSessionsAdapter(getActivity(), sessionsList);
addProgramSessionsRV.setAdapter(adapter);
Log.d(TAG, "Items in adapter:\t" + adapter.getItemCount());
}
}
Still the ui shows no items from this method. I have added the data manually using this snippet and it shows Session 1 item in view but not the data from db in for loop.
Test Case:
Sessions sessions = new Sessions();
sessions.setSession_name("Test one");
sessions.setProgram_id("234");
sessions.setProg_sessionId("91");
sessionsList.add(sessions);
adapter = new AddProgramSessionsAdapter(getActivity(), sessionsList);
addProgramSessionsRV.setAdapter(adapter);
Log.d(TAG, "Items in adapter:\t" + adapter.getItemCount());
Is there any reason why setting the adapter to a list which has items does not work in the for loop db query (works)? Please explain to me why the ui shows nothing. Thanks.
Here's the adapter code:
public class AddProgramSessionsAdapter extends RecyclerView.Adapter<AddProgramSessionViewHolder> {
private final Context context;
private List<Sessions> itemsList;
public AddProgramSessionsAdapter(Context context, List<Sessions> itemsList) {
this.context = context;
this.itemsList = itemsList;
}
#Override
public AddProgramSessionViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.add_program_sessions_items_layout, parent, false);
return new AddProgramSessionViewHolder(view);
}
#Override
public void onBindViewHolder(AddProgramSessionViewHolder viewholder, int position) {
Sessions sessions = itemsList.get(position);
viewholder.add_program_sessionNameTV.setText(sessions.session_name);
Picasso.with(context)
.load(R.drawable.app_logo_resized)
.into(viewholder.add_program_sessionIV);
}
#Override
public int getItemCount() {
if (itemsList == null) {
return 0;
}
return itemsList.size();
}
}

RecyclerView partially recycling some views

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()

recyclerview add data to the top without mess up scroll?

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

AsyncTask in Multiple Tabs with different content

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

Categories

Resources