Load more data into ListView - Android - java

I have implemented a listview in which data is loaded through an asynctask, To load more data I have used this code found here (exactly copy pasted)
Main Activity
class load_data extends AsyncTask<Integer, Void, Void>{
// This asynctask takes in a page number as argument and depending on that page number
// data is loaded and stored in various String[] arrays and their length is extended as new items are loaded
protected void onPostExecute(Void result) {
if(adapter == null){
adapter = new Listadapter(Main.this,String[],String[],String[]);
list.setAdapter(adapter);
}
else if(adapter != null){
adapter.notifyDataSetChanged();
}
}
}
This task is called as new load_data().execute(1);
after this code I have the load more data snippet from the above link
everything works perfectly no syntax errors, and also the data loads after reacing the given threshold (20) in my case, however new data is not shown. how do I notifiy the adapter that more data has been added or data has been changed
Thanks!.
EDIT: LOADMORE CLASS
class EndlessScrollListener implements OnScrollListener {
private int visibleThreshold = 5;
private int currentPage = 1;
private int previousTotal = 0;
private boolean loading = true;
public EndlessScrollListener() {
}
public EndlessScrollListener(int visibleThreshold) {
this.visibleThreshold = visibleThreshold;
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
currentPage++;
}
}
if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
// I load the next page of gigs using a background task,
// but you can call any function here.
new load_data().execute(currentPage);
adapter.notifyDataSetChanged();
loading = true;
}
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
}
list.setOnScrollListener(new EndlessScrollListener(20)); // 20 being the threshold

Finally! for those who are stuck with the same problem, I have the solution.
I have noticed that adapter.notifyDataSetChanged(); does not work if you are using String[] arrays to store data which is displayed into the listview rows.
Instead if you use List<String> list = new ArrayList<String>();and store data in the
the listview will be updated with new data :)

you can check this github project if you have any problem with your previous code.
otherwise if you want to load data like pagination you can try this example and this one

Related

Prevent image reload on filtering adapter data [Android]

I am calling the filter data function from the fragment search view , it is working fine and the data are getting filtered but the images are getting reloaded.How can this be prevented
public menuadapter(ArrayList<GridItem> mGridDat, Context context, OnItemClickListener listener) {
this.mGridData=new ArrayList<GridItem>();
this.orignallist=new ArrayList<GridItem>();
mGridData.addAll(mGridDat);
orignallist.addAll(mGridDat);
this.context = context;
this.listener = listener;
this.Session=new session(context);
}
public void onBindViewHolder(final MyViewHolder holder, final int position) {
final Activity activity = (Activity)context;
String capital=mGridData.get(position).getTitle().substring(0,1).toUpperCase()+mGridData.get(position).getTitle().substring(1).toLowerCase();
holder.txtview.setText(capital);
Picasso.with(context).load(mGridData.get(position).getImage()).fit().centerCrop().skipMemoryCache().into(holder.imageView);
}
Filter Data function
public void filterData(String query){
query=query.toLowerCase();
//Log.v("check1",String.valueOf(orignallist.size()));
mGridData.clear();
if(query.isEmpty()){
mGridData.addAll(orignallist);
// Log.v("check2",String.valueOf(orignallist.size()));
}
else {
//Log.v("check0",String.valueOf(orignallist.size()));
ArrayList<GridItem> newlist = new ArrayList<>();
for(GridItem gd: orignallist) {
if ((gd.getTitle().toLowerCase().contains(query)) ) {
newlist.add(gd);
}
}
if(newlist.size()> 0){
mGridData.addAll(newlist);
}
}
notifyDataSetChanged();
}
Try this
Picasso.with(context).load(mGridData.get(position).getImage()).fit().centerCrop().networkPolicy(NetworkPolicy.OFFLINE).into(holder.imageView);
You need to use below property and not skipMemoryCache()
OFFLINE
public static final NetworkPolicy OFFLINE
Forces the request through the disk cache only, skipping network.
https://square.github.io/picasso/2.x/picasso/com/squareup/picasso/NetworkPolicy.html
To avoid reloding of images, solutions I think of are
one is to remove the images which are not matching the query and keeping others. Something like -
if(!(gd.getTitle().toLowerCase().contains(query)))
{
// get it from holder.getAdapterPosition();
contentsArrayList.remove(position);
notifyItemRemoved(position);
}
// out of for loop
notifyItemRangeChanged(firstRemovedPostion,contentsArrayList.size());
You can hide the element which is not matching the query and then show if it's matching another
itemView.setVisibility(View.GONE);

Save data from fragments when switching tabs

I've written a game where the user inputs the number of player and every player gets an own tab with an empty table.
Therefore I used a PagerAdapterClass (extends FragmentStatePagerAdapter) and a viewpager.
So every player has the same fragmentView.
Now the user can put variables into the table, bu everytime I switch between the tabs, the input gets lost.
Well, i 'fixed' that problem by adding this to my pageradapter:
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
}
But it's more stopping the viewpager from destroying than actually saving the data.
My main goal is to really save that stuff in that table.
I already tried https://stackoverflow.com/a/17135346/11956040 but i cannot get mContent because i cannot get the reference of the fragment, because all fragments are not created on their own but all at the same time (or something like that).
I also don't know how to set a Tag.
This way: https://stackoverflow.com/a/18993042/11956040
doesn't work for me.
MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
Toolbar toolbar = findViewById(R.id.toolbar2);
setSupportActionBar(toolbar);
...
//numPlayer = num of tabs
SectionsPagerAdapter adapter = new SectionsPagerAdapter(numPlayer, getSupportFragmentManager());
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(adapter);
TabLayout tabs = findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
if(numPlayer >= 5) {
tabs.setTabMode(TabLayout.MODE_SCROLLABLE);
}
}
PagerAdapter:
public class SectionsPagerAdapter extends FragmentStatePagerAdapter {
private int tabNum;
public SectionsPagerAdapter(int tabNum, FragmentManager fm) {
super(fm);
this.tabNum = tabNum;
}
#Override
public PlaceholderFragment getItem(int position) {
return PlaceholderFragment.newInstance(position);
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
int playerNum = position + 1;
return "Spieler " + playerNum;
}
#Override
public int getCount() {
// Show 2 total pages.
return tabNum;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
}
}
Fragment:
public static PlaceholderFragment newInstance(int index) {
PlaceholderFragment fragment = new PlaceholderFragment();
Bundle bundle = new Bundle();
bundle.putInt("player", index);
fragment.setArguments(bundle);
return fragment;
}
There must be a solution but I cannot find it or cannot implement it.
Pls help.
Solved my problem this way:
define 2 dimensional ArrayList for rows and columns and counter for columns:
private ArrayList<ArrayList<Integer>> columnArray;
private int column;
onCreateView (for fragments) set column = 0 and add one entry with an empty list to columnArray
and set the first rowList on column index of columnArray:
pointArray.add(column, new ArrayList<Integer>());
final ArrayList<Integer> rowList = pointArray.get(column);
fill the empty rowListwith 0 (maybe it also works in an other way, but I made it this way to have on empty EditTexts a 0 and can easily replace them)
define View.OnFocusChangeListener for all EditTexts like this:
/*I dont know if I could set column final in general,
but you need to set a final int because you call this value in an inner class*/
final int pos = column
for (int i = 0; i <= getEditTexts(pos).size() - 1; i++) {
EditText editTexts = getEditTexts(pos).get(i);
final String editTextsTag = editTexts.getTag().toString();
View.OnFocusChangeListener listener = new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View view, final boolean b) {
if (view.getTag().toString().equals(editTextsTag) && !b) {
//fills rowList
addEntries(pos, rowList);
//adds rowList to columnArray
columnArray.set(pos, rowList);
//save the columnsArray or use it
saveData(columnArray);
}
}
};
editTexts.setOnFocusChangeListener(listener);
define method which collects data from each cell, depending on column position (pos), add it to rowList
for example:
private void addEntries(int pos, ArrayList<Integer> rowList) {
for(int i = 0; i <= 16; i++) {
//this requires EditText_label, i made them dynamically
String edit_label = "edit_" + pos + i;
EditText editText = table.findViewWithTag(edit_label);
String mEditTextString = editText.getText().toString();
try {
int thisValue = Integer.parseInt(mEditString);
rowList.set(j, thisValue);
} catch (NumberFormatException e) {
//maybe you do not need this, but I need it for something else
int thisValue = 0;
rowList.set(j, thisValue);
}
}
}
define a method for saving the columnArray. I used an interface to give it to parent Activity: Here you can find how I made it
Otherwise you can convert the columnArray to a String and save it in a database.
NOTE
I made it with column value set beacuse I increase the value for every column I add during runtime using a method. If you just have one column, you dont need to set it. Just use 0 instead of pos, column

How to change background colour to specific viewholder items in a RecycleView?

I am trying to change background color in specific item(s) in a RecycleView.
Because I need to set text too, I have the following code that works fine:
protected void populateViewHolder(RankingViewHolder viewHolder, final Ranking model, int position)
{
final Context mContext = getActivity().getApplicationContext();
viewHolder.txt_name.setText(model.getUserName());
viewHolder.txt_score.setText(String.valueOf(model.getScore()));
viewHolder.txt_class.setText(model.getUser_class());
Picasso.with(mContext).load(model.getAvatarUrl()).error(R.drawable.ic_people_black_24dp).into(viewHolder.personPhoto);
int totalRanking = adapter.getItemCount();
int realRank = totalRanking - viewHolder.getAdapterPosition();
viewHolder.ranknumber.setText("# "+String.valueOf(realRank));
}
This works as I want and realRanktakes the correct values, and the viewHolder.ranknumber.setText("# "+String.valueOf(realRank));
Sets the right text with no problem.
Now I am trying (as I got a number/text result correct, to make an if statement like this:
if(adapter.getItemCount() -viewHolder.getAdapterPosition() == 0)
{
viewHolder.itemView.setBackgroundColor(Color.GREEN);
}
if(adapter.getItemCount() -viewHolder.getAdapterPosition() == 1)
{
viewHolder.itemView.setBackgroundColor(Color.YELLOW);
}
if(adapter.getItemCount() -viewHolder.getAdapterPosition() == 2)
{
viewHolder.itemView.setBackgroundColor(Color.BLUE);
}
(I tried with String.valueOf(realRank)equality, with realRankequality too)
In all cases I have the same result. The color changes as its should at positions 1,2,3 BUT it changes at positions 7,8,9 and 14,15,16 and 21,22,23 etc.
What am I missing here?
public class RankingViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
{
private ItemClickListener itemClickListener;
public TextView txt_name, txt_score, txt_class, ranknumber;
public ImageView personPhoto;
public RankingViewHolder(View itemView)
{
super(itemView);
txt_name = itemView.findViewById(R.id.txt_name);
txt_score = itemView.findViewById(R.id.txt_score);
personPhoto = itemView.findViewById(R.id.person_photo);
txt_class = itemView.findViewById(R.id.txt_class);
ranknumber = itemView.findViewById(R.id.ranknumber);
itemView.setOnClickListener(this);
}
public void setItemClickListener(ItemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
#Override
public void onClick(View view) {
itemClickListener.onClick(view , getAdapterPosition(),false);
}
}
The adapter:
adapter = new FirebaseRecyclerAdapter<Ranking, RankingViewHolder>(
Ranking.class,
R.layout.layout_ranking,
RankingViewHolder.class,
rankingTbl.orderByChild("score").limitToFirst(100)
)
This line of code int realRank = totalRanking - viewHolder.getAdapterPosition();gives a number (1,2,3,4,5,6 etc.) Why i cannot use this number to check equality?
Notice
Keeping this code for NOT working solution, just for future reference:
if(position == 0){
viewHolder.itemView.setBackgroundColor(Color.GREEN);
}
else if(position == 1){
viewHolder.itemView.setBackgroundColor(Color.YELLOW);
}
else if(position == 2){
viewHolder.itemView.setBackgroundColor(Color.BLUE);
}
else{
viewHolder.itemView.setBackgroundColor(Color.WHITE);
}
This changes the color BUT not for only 3 first items. As you scroll down, changes the color for every 3 first viewable items like before, meaning 1,2,3, 7,8,9, etc.
Update:
I dont use a custom adapter, i use FirebaseRecyclerAdapter.
Thanks to #Muhammad Haroon comment i checked that has getItemViewType. So now i m trying with it like
position =adapter.getItemViewType( 0);
if(position == 0){
viewHolder.itemView.setBackgroundColor(Color.GREEN);
}
Not working for now, but i think its the correct direction...
Update 2
With position its not possible as RecycleView recycles the views so i have the same result. The working code is
if (linearLayoutManager.findFirstVisibleItemPosition() > 0) {
viewHolder.itemView.setBackgroundResource(R.drawable.blackframe);
}
else{
viewHolder.itemView.setBackgroundResource(R.drawable.goldframe);
}
Works fine except that after scrolling loosing the change of background.
So as we want and need the perfection, any idea for keeping even after scroll?
hi try add this in your Adapater it may solve your problem.
#Override
public int getItemViewType(int position) {
return position;
}
Please give this a try
override in your custom adapter
#Override
public long getItemId(int position) {
return position;
}
and in in your adapter object:
myAdapter.setHasStableIds(true);
In populateViewHolder add these line of code
if(position == 0){
viewHolder.itemView.setBackgroundColor(Color.GREEN);
}
else if(position == 1){
viewHolder.itemView.setBackgroundColor(Color.YELLOW);
}
else if(position == 2){
viewHolder.itemView.setBackgroundColor(Color.BLUE);
}
else{
viewHolder.itemView.setBackgroundColor(Color.WHITE);
}
position is a parameter in populateViewHolder.

ListView's getCount() always returns 0

I'm using ParseQueryAdapter to display a ListView including the set of elements given by the Parse query:
ParseQueryAdapter.QueryFactory<AlertObject> factory =
new ParseQueryAdapter.QueryFactory<AlertObject>() {
public ParseQuery<AlertObject> create() {
ParseQuery<AlertObject> query = AlertObject.getQuery();
query.orderByDescending(AlertObject.TIMESTAMP_KEY);
query.fromLocalDatastore();
return query;
}
};
alertsListAdapter = new AlertListItemAdapter(activity, factory, thisFragment);
ListView alertsListView = (ListView) rootView.findViewById(R.id.alerts_list_view);
alertsListView.setAdapter(alertsListAdapter);
Now, I'd like to know the number of items in the ListView, but if I call alertsListView.getCount(), it returns 0. What am I doing wrong?
EDIT: someone gave this post a negative vote, but without leaving a comment or a request for clarification. So, I ask for some explanation about the reason of that in order to improve the readability of my question.
UPDATE: below my adapter
public class AlertListItemAdapter extends ParseQueryAdapter<AlertObject> {
private Context context;
private Fragment listAlertsFragment;
public AlertListItemAdapter(Context context,
ParseQueryAdapter.QueryFactory<AlertObject> queryFactory,
Fragment fragment) {
super(context, queryFactory);
this.context = context;
this.listAlertsFragment = fragment;
}
#Override
public View getItemView(final AlertObject alertObject, View view, final ViewGroup parent) {
[...]
}
#Override
public int getCount() {
return super.getCount();
}
}
I suspect (cannot be sure without seeing the Parse code/docs) that the adapter is not immediately populated with items, and when the query is executed, it'll call notifyDataSetChanged() on itself so that the ListView requeries it for item Views.
This would explain why your getCount() returns 0 immediately after setAdapter(ListAdapter) but why you can also see 33 items.
You can verify this logging adapter.getCount() as you do, and in addition, overriding notifyDataSetChanged to then observe the order of statements:
public class AlertListItemAdapter extends ParseQueryAdapter<AlertObject> {
public AlertListItemAdapter(
Context context,
ParseQueryAdapter.QueryFactory<AlertObject> queryFactory,
Fragment fragment) {
super(context, queryFactory);
}
#Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
Log.d("FOO", "item count: " + getCount());
}
#Override
public View getItemView(AlertObject alertObject, View view, ViewGroup parent) {
Log.d("FOO", "getItemView()");
...
}
...
}
If you need to know when the data changes, you can register a dataset changed listener on the adapter:
adapter.registerDataSetObserver(new DataSetObserver() {
#Override
public void onChanged() {
super.onChanged();
Log.d("Foo", adapter.getCount());
}
});
Are you sure you populated your ListView with parse AlertObjects?
I think you should add something like this to your query:
query.findInBackground(new FindCallback<AlertObject>() {
#Override
public void done(List<AlertObject> alerts, ParseException e) {
if (e == null) {
// Success
mAlerts = alerts;
String[] alertObjects = new String[mAlerts.size()];
Log.v(TAG, "There are " + mAlerts.size() + “ on the parse");
}
} else {
Log.e(TAG, e.getMessage());
}
}
});
Log can tell you how many objects you have on Parse.
In this Callback you can populate your ListView and then use
.getCount();
on your alertListAdapter.
Make sure you pass, store, and override the getCount() method correctly.
Please provide the code of adapter class if possible
Simply write following line to get number of items in the list view:
int count = alertsListView.alertsListAdapter().getCount();
After:
alertsListView.setAdapter(alertsListAdapter);
So your code will look like:
alertsListAdapter = new AlertListItemAdapter(activity, factory, thisFragment);
ListView alertsListView = (ListView) rootView.findViewById(R.id.alerts_list_view);
alertsListView.setAdapter(alertsListAdapter);
int count = alertsListView.alertsListAdapter().getCount();

problem in forming gridview with images in the web

in my app i am trying to form a grid view from the xml file that been stored in web.
Following is my code
grid = (GridView)findViewById(R.id.gridView1);
imageXMLfn();
grid.setAdapter(new ImageAdapter(this));
private void imageXMLfn()
{
try
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setIgnoringComments(true);
factory.setCoalescing(true);
factory.setNamespaceAware(false);
factory.setValidating(false);
DocumentBuilder parser = factory.newDocumentBuilder();
URL url = new URL(UserManual.IMAGE_URL);
Log.e("ViewImage3",""+UserManual.IMAGE_URL);
Document document= parser.parse(new InputSource(url.openStream()));
NodeList sections = document.getElementsByTagName("application");
numSections = sections.getLength();
for (int i = 0; i < numSections; i++)
{
Element section = (Element) sections.item(i);
if(section.hasChildNodes()==true)
{
NodeList section1=section.getChildNodes();
for(int j=0;j<section1.getLength();j++)
{
if(section1.item(j).hasChildNodes()==true)
{
for(int k=0;k<section1.item(j).getChildNodes().getLength();k++)
{
xmlvalue=String.valueOf(section1.item(j).getChildNodes().item(k).getNodeValue()).trim();
arl.add(xmlvalue);
}
}
}
}
}
}
catch(Exception e)
{
System.out.println(e);
Log.e("ViewImage Error1",e.getMessage());
}
Iterator<String> itr = arl.listIterator();
int z=0,x=0,increment=0;
while (itr.hasNext())
{
id = itr.next();
img = img+id;
z++;
}
}
public class ImageAdapter extends BaseAdapter
{
private Context myContext;
private String[] myRemoteImages = {id};
public ImageAdapter(Context c)
{
this.myContext = c;
}
}
Either i am getting only the first image stored in that url or else i am not getting any other images
Following is the link from which i am trying to get the images
http://94.23.207.167/websiteIphone/Admin/XML/Santa.xml
Your code is not complete, so it's a bit difficult to give a definite answer but in the while loop at the end if imageXMLfn you're assigning id = it.next(). The image adapter than uses only id which is set to the last value in the iterator. Supposing that arl is a field in your class of type ArrayList<String> it probably can be solved by:
public class ImageAdapter extends BaseAdapter
{
private Context myContext;
private String[] myRemoteImages = arl.toArray(new String[arl.size()]);
public ImageAdapter(Context c)
{
this.myContext = c;
}
}
However, your code is really incomplete in this regard, so I'm not sure whether this will work out.
What I found out mostly to this same problem is this. Lazy Load your images in the background. When in onpostexecute, don't display any images at all, due to the fact that when flinging the screen the current display then goes nuts like a multimedia slideshpw where your images blink in and out and may get updated incorrectly. Then I found that if you do a Fling detection on the gridview variable such as:
mPhotoView.setOnScrollListener(new OnScrollListener() {
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
}
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
if(scrollState != SCROLL_STATE_IDLE) {
mAdapter.setFlinging(true);
} else {
mAdapter.setFlinging(false);
}
int first = view.getFirstVisiblePosition();
int count = view.getChildCount();
if (scrollState == SCROLL_STATE_IDLE || (first + count >
mAdapter.getCount()) ) {
mPhotoView.invalidateViews();
}
}
});
the invalidateViews will cause the newly downloaded images to be refreshed. Also, on the call to the Lazy background loader, right after the process is set in motion I load a placeholder bitmap. I only now need to find a way to get a few of the leftover placeholder images that somehow find its way through on some of the scrolls to be updated when the images are available. Lazy Loaders are all over the Internet and think its a far better way to load remote images than you code above. This is one of the trickiest things Androider's try to accomplish. Took me as long as all the rest of the "gotchas" I've faced. Hope this clears things up for you. Happy coding.

Categories