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
}
}
}
Related
My RecyclerView does not update.Even when i call adapterNotifyDataSetChanged().Questions,and answers are loaded properly but items in recycler do not.
They just stay same,not even reordered.
MainActivity
public class MainActivity extends AppCompatActivity implements MyRecyclerViewAdapter.ItemClickListener {
MyRecyclerViewAdapter adapter;
EditText edit;
Button provjeri;
List<Item> questions;
int curquestion=0;
TextView pitanje;
public List<String> suggestSource = new ArrayList<>();
public static char[] user_submit_answer;
public char[]answeri;
String corect_answer;
// data to populate the RecyclerView with
String[]data = {"a","b","c","č","ć","d","đ","e","f","g","h","i","j","k","l","m"
,"n","o","p","r","s","š","t","u","v","z","ž"};
String[]simpleArray;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
edit=findViewById(R.id.editText);
provjeri=findViewById(R.id.button);
pitanje=findViewById(R.id.textView);
Random random=new Random();
questions=new ArrayList<>();
for(int i=0;i<Database.questions.length;i++){
questions.add(new Item(Database.questions[i], answers[i]));
}
pitanje.setText(questions.get(curquestion).getQuestion());
// data to populate the RecyclerView with
// String[]data = {"a","b","c","č","ć","d","đ","e","f","g","h","i","j","k","l","m"
// ,"n","o","p","r","s","š","t","u","v","z","ž"};
corect_answer=(questions.get(curquestion).getAnswer());
answeri=corect_answer.toCharArray();
user_submit_answer=new char[answeri.length];
suggestSource.clear();
for(char item:answeri)
{
//Add logo name to list
suggestSource.add(String.valueOf(item));}
for(int i = answeri.length; i< answeri.length*2; i++){
suggestSource.add(data[random.nextInt(data.length)]);
}
Collections.shuffle(suggestSource);
simpleArray = new String[ suggestSource.size() ];
suggestSource.toArray( simpleArray );
// set up the RecyclerView
final RecyclerView recyclerView = findViewById(R.id.recyclerView);
int numberOfColumns = 6;
recyclerView.setLayoutManager(new GridLayoutManager(this, numberOfColumns));
adapter = new MyRecyclerViewAdapter(this, simpleArray);
adapter.setClickListener(this);
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
provjeri.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(edit.getText().toString().equalsIgnoreCase(questions.get(curquestion).getAnswer())){
curquestion++;
pitanje.setText(questions.get(curquestion).getQuestion());
corect_answer=(questions.get(curquestion).getAnswer());
adapter.notifyDataSetChanged();
Toast.makeText(MainActivity.this,"blblblbl",Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(MainActivity.this,"netacno",Toast.LENGTH_SHORT).show();
}
}
});
//("btw:why must write so much unnecesary text,to accept my question"and still not enough to post question,so wtf i must write some novel here,maybe make up some problems thath do not even exist,or just ....wtf maaaaaaaaan)//
Since nobody didnt answered,i found answer by myself.So the thing was i didnt refresh adapter after updating it,and it was called before it was created
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 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 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;
}
Am I using RecyclerView correctly? Whenever I update the data, the is one moment in which the view still displays the old data, despite having modified the dataset and having called the relevant notify method. On top of that, I don't see any animations, so I must be doing something wrong.
Here is the relevant snippet of code:
private void refreshData() {
Utils.hideSoftKeyboard(this);
if (!Utils.isOnline(getApplicationContext())) {
Toast.makeText(getApplicationContext(), R.string.toast_no_conn, Toast.LENGTH_SHORT).show();
return;
}
String stopNumber = mStopEdit.getText().toString();
if (stopNumber.isEmpty()) {
Toast.makeText(getApplicationContext(), R.string.toast_no_stop, Toast.LENGTH_SHORT).show();
return;
}
mResultNoStop.setVisibility(View.GONE);
mResults.setVisibility(View.GONE);
mProgressCircle.setVisibility(View.VISIBLE);
if (!mDataset.isEmpty()) {
int size = mDataset.size();
mDataset.clear();
mAdapter.notifyItemRangeRemoved(0, size);
}
FiveT.getStopData(stopNumber, mRequestQueue, new FiveT.StopDataClientListener() {
#Override
public void onResponse(ApiResult result) {
mProgressCircle.setVisibility(View.GONE);
if (result.getStopResults().size() == 0) {
mResultNoStop.setVisibility(View.VISIBLE);
Toast.makeText(getApplicationContext(), R.string.toast_no_data, Toast.LENGTH_SHORT).show();
return;
}
int i = 0;
mStopName.setText(result.getStopName());
for (StopResult res : result.getStopResults()) {
mDataset.add(res);
mAdapter.notifyItemInserted(i++);
}
mResults.setVisibility(View.VISIBLE);
}
});
}
EDIT: I initialize the RecyclerView like this:
mDataset = new ArrayList<StopResult>();
mRecyclerView = (RecyclerView) findViewById(R.id.results_recycler_view);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new RecyclerViewAdapter(mDataset, getApplicationContext());
mRecyclerView.setAdapter(mAdapter);
Based on the question's comments:
What you're missing there, in RecyclerView's initialisation, is the set of a item animator.
According to your code:
mDataset = new ArrayList<StopResult>();
mRecyclerView = (RecyclerView) findViewById(R.id.results_recycler_view);
mRecyclerView.setItemAnimator(new MyItemAnimator());
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new RecyclerViewAdapter(mDataset, getApplicationContext());
mRecyclerView.setAdapter(mAdapter);
You can refer to this library in order to find an animation that it is best for your needs.