Infinite call to "onScroll" when reach end of grid view - java

I've found the following code to do something when I reach the end of a gridview:
GridView gridview = (YMAnimatedGridview) v.findViewById(R.id.my_gridview);
gridview.setAdapter(adapter);
final View footerView = mainView
.findViewById(R.id.my_grid_footer_view);
gridview.setOnScrollListener(new GridView.OnScrollListener() {
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (firstVisibleItem + visibleItemCount == totalItemCount) {
// last item in grid is on the screen, show footer:
fetchMoreItems();
}
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
The thing is that, when I reach the end of the gridView, this method is called continously.
private void fetchMoreItems() {
Log.i(LOG_TAG, "Reached the end of grid view"); //for debbug purposes
Integer lastIndex = thumbnails.length; //This is a Bitmap array with the data I'm alredy showing
Cursor cursor = getData();
thumbnails = concat(thumbnails, new Bitmap[30]);
Integer lastIndexToRun = Math.min(30, cursor.getCount() - lastIndex);
for(int j = 0; j < lastIndexToRun; j++) {
Integer i = j + lastIndex;
cursor.moveToPosition(i);
thumbnails[i] = getThumbnail(i);
}
adapter.notifyDataSetChanged(); //This is the adapter used to fill the gridview
cursor.close();
}
I think my mistake should be in the fetchMoreItems() method, but since it's my first time using GridView, I'm not sure

You need to avoid the method to be called multiple times, like this:
private int beforeLastItem;
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
final int lastItem = firstVisibleItem + visibleItemCount;
if (lastItem == totalItemCount) {
// last item in grid is on the screen, show footer:
if (beforeLastItem != lastItem){ //to avoid multiple calls for the last item
fetchMoreItems();
beforeLastItem = lastItem;
}
} else {
beforeLastItem = lastItem;
}
}

You can use a boolean value to prevent multiple calls like this:
private final boolean isFetching = false;
GridView gridview = (YMAnimatedGridview) v.findViewById(R.id.my_gridview);
gridview.setAdapter(adapter);
final View footerView = mainView
.findViewById(R.id.my_grid_footer_view);
gridview.setOnScrollListener(new GridView.OnScrollListener() {
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (firstVisibleItem + visibleItemCount == totalItemCount && !isFetching) {
isFetching = true;
// last item in grid is on the screen, show footer:
fetchMoreItems();
}else{
if(isFetching){
isFetching = false;
}
}
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
You can also set isFetching to false after fetching completed.

Related

Android ListView with Switch buttons - only one switch active at a time

I have a list view with two switches. I want the functionality to work were only one switch may be active at a time.
--UPDATED
My Adapter:
public class NotificationsAdapter extends ArrayAdapter<String> {
private Context context;
private String mTitle[];
private boolean onOff[];
public NotificationsAdapter(Context c, String mTitle[], boolean onOff[]) {
super(c, R.layout.adapter_notifications_layout, R.id.notificationsListTv, mTitle);
this.context = c;
this.mTitle = mTitle;
this.onOff = onOff;
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = layoutInflater.inflate(R.layout.adapter_notifications_layout, parent, false);
Switch notificationSwitch = view.findViewById(R.id.switchNotificationsDaily);
TextView myTitle = view.findViewById(R.id.notificationsListTv);
myTitle.setText(mTitle[position]);
notificationSwitch.setChecked(onOff[position]);
view.setClickable(true);
view.setFocusable(true);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int size = onOff.length;
for (int i = 0; i < size; i++) {
if (i == position) {
break;
}
onOff[i] = false;
}
if (onOff[position]) {
notificationSwitch.setChecked(false);
onOff[position] = false;
} else {
onOff[position] = true;
notificationSwitch.setChecked(true);
}
}
});
return view;
}
My Class:
private String[] mTitle = new String[]{"Once Daily", "Twice Daily"};
private Switch notificationSwitch;
private boolean[] onOff = new boolean[] {false, false};
NotificationsAdapter notificationsAdapter = new NotificationsAdapter(getActivity(), mTitle, onOff);
listView = view.findViewById(R.id.notificationsListView);
listView.setAdapter(notificationsAdapter);
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
listView.setItemsCanFocus(true);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
}
});
}
Currently with this code I can select both switches to be active at the same time. When I select more than one switch I would like the other to deactivate. Any assistance getting this functionality to work would be greatly appreciated.
In your activity:-
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
resetValues(position);
if (onOff[position]) {
onOff[position] = false;
} else {
onOff[position] = true;
}
notifyDataSetChanged();
}
}
private void resetValues(int selectedPosition){
int size = onOff.length;
for(int i=0; i < size; i++){
if(i == selectedPosition){
continue;
}
onOff[i] = false;
}
}
And in your adapter:-
notificationSwitch.setChecked(onOff[position]);
Also, remove your click listener from adapter.
The logic is:- Making all other values as false except the selected item, then changing the state of the selected item based on its previous state.

How to set textListener to dynamically generated editTexts in android

I have dynamically added a layout with editBox's ,and I want to set textListener to each editBox so that I can can perform some calculations on the values that I retrieve from the editBox's. I am using the the code below to inflate the layout. Is it possible to set a counter on the View object that I am using to inflate the layout. Any ideas are welcome.
public void addField1(View v) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View rowView = inflater.inflate(R.layout.field, null);
parentLinearLayout.addView(rowView, parentLinearLayout.getChildCount() - 1);
}
int count1 = parentLinearLayout.getChildCount();
for (int n = 0; n < count1; n++) {
LinearLayout linearLayout = (LinearLayout) parentLinearLayout.getChildAt(n);
editValues3 = (EditText) linearLayout.getChildAt(3);
editValues3.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
Log.e("inside", "before" + s);
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Log.e("inside", "on text" + s);
}
#Override
public void afterTextChanged(Editable s) {
Log.e("inside", "afterText" + s);
if(editValues3.getText().length()>0){
valCalculations();
}
}
});
}
I think you should create your CustomEditText that implementsaddTextChangedListener and inside that custom class make the proper calculations.

exchanging items within a gridView

In the following method, I am trying to exchange two items within the grid view by clicking on one. I have numbers 0-9 and by clicking on one item, other than 0, the item will place itself in position on 0 and the 0 will be set in the place of clicked item. I think I am missing the line of inserting the 0 into its place. Please see the following code:
gridView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Item clickedItem = mList.get(position);
int g = clickedItem.getValue();
String c = clickedItem.getStr();
Item spare = mList.get(8);
Toast.makeText(MainActivity.this, "val is "+g+",", Toast.LENGTH_SHORT).show();
if (clickedItem.getValue() == 0) {
Toast.makeText(MainActivity.this, "unable to find pic", Toast.LENGTH_SHORT).show();
}
if (clickedItem.getValue() != 0) {
Item temp = getItem(position);
// need clicked position to get spare;
spare = temp;
}
}
public Item getItem(int position) {
return mList.get(position);
}
});
gridView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Item clickedItem = mList.get(position);
int g = clickedItem.getValue();
String c = clickedItem.getStr();
Item spare = mList.get(8);
Toast.makeText(MainActivity.this, "val is "+g+",", Toast.LENGTH_SHORT).show();
if (clickedItem.getValue()== 0) {
Toast.makeText(MainActivity.this, "unable to find pic", Toast.LENGTH_SHORT).show();
return
}
if (clickedItem.getValue()!= 0) {
Item temp = getItem(position);
Item temp0 = getItem(0);
tempPosition = position;
mList.remove(tempPosition );
mList.remove(0);
mList.add(0,temp );
if(tempPosition > mList.size() - 1){
mList.add(temp0);
}else{
mList.add(tempPosition ,temp0);
}
mAdapter.notifDataSetChanged();
// need clicked position to get spare;
spare=temp;
}
}
public Item getItem(int position) {
return mList.get(position);
}
});

Show the ActionBar while scrolling up on a ListView

I have a ListView and when I scroll down or up I can detect this using this solution:
listView.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) { }
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if(listView.getFirstVisiblePosition() == 0)
getActionBar().show();
else
getActionBar().hide();
}
});
This way works and the ActionBar shows when the scroll comes back to the first item of the list. I would ALSO like to show the ActionBar again when I begin to scroll up within the list.
EDIT:
this is the last way and it works not always.. sometimes the actionbar disappears and never comes back:
private int mPreviousFirst;
...
listView.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) { }
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if(prevVisibleItem != firstVisibleItem){
if (firstVisibleItem < mPreviousFirst)
getActionBar().show();
else
getActionBar().hide();
mPreviousFirst = firstVisibleItem;
}
}
});
i'm close but something goes wrong :(
You can do so by saving the previous first visible position each time it changes:
private int mPreviousFirst;
...
listView.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) { }
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (firstVisibleItem < mPreviousFirst) {
getActionBar().show();
} else if (firstVisibleItem > mPreviousFirst) {
getActionBar().hide();
}
mPreviousFirst = firstVisibleItem;
}
});
You can try manipulating the following onScrollChanged within your view,
onScrollChanged
which keeps track of the current and previous states of a scroll. If you are going up, then you could do the same behavior.
Something similar to the following would achieve what you are asking:
#Override
protected void onScrollChanged (int currentHorizontalPosition, int currentVerticalPosition, int oldHorizontalPosition, int oldVerticalPosition)
{
if(currentVerticalPosition < oldVerticalPosition && positionDifference > threshold)
{
getActionBar().show();
}
else //condition for downscroll or other actions.
{
getActionBar().hide();
}
}
mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
int mLastFirstVisibleItem = 0;
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) { }
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (view.getId() == mListView.getId()) {
final int currentFirstVisibleItem = mListView.getFirstVisiblePosition();
if (currentFirstVisibleItem > mLastFirstVisibleItem) {
// getSherlockActivity().getSupportActionBar().hide();
activity.hideActionBar();
} else if (currentFirstVisibleItem < mLastFirstVisibleItem) {
// getSherlockActivity().getSupportActionBar().show();
activity.showActionBar();
}
mLastFirstVisibleItem = currentFirstVisibleItem;
}
}
});
this works for me on kitkat

ListView - Load more items with scroll

I need help with listview I am using in a fragment. The app reads data from an API and my code works fine. I load 15 items initially and every next time I load 10 items more. However if a request to the API return less than 15 items, it doesn't work. Also, the app fails when the number of items is not a multiple of 15 or 25 or 35. That is because I load 10 items after the initial setup.
I need to modify my code for it to work with any number of list items.
My code is as follows:
(ListaFragment.java) -> Here is the ListFragment
public class ListaFragment extends ListFragment implements OnScrollListener {
public ListaFragment(){}
View rootView;
ListAdapter customAdapter = null;
ListaLugares listaLugares;
// values to pagination
private View mFooterView;
private final int AUTOLOAD_THRESHOLD = 4;
private int MAXIMUM_ITEMS;
private Handler mHandler;
private boolean mIsLoading = false;
private boolean mMoreDataAvailable = true;
private boolean mWasLoading = false;
public int ventana = 0;
public int nInitial;
private Runnable mAddItemsRunnable = new Runnable() {
#Override
public void run() {
customAdapter.addMoreItems(10);
mIsLoading = false;
}
};
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_lista, container, false);
if (container != null) {
container.removeAllViews();
}
((MainActivity) getActivity()).setBar();
// read number of places of a category
listaLugares.readNumberOfPlaces();
// set
setNumbersOfItems();
// read the places a insert in a List
listaLugares.readPlaces(15, ventana, listaLugares.idCategoria);
//ventana = ventana + 25;
return rootView;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
listaLugares = ((ListaLugares)getActivity().getApplicationContext());
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final Context context = getActivity();
mHandler = new Handler();
customAdapter = new ListAdapter(context, listaLugares.lista);
mFooterView = LayoutInflater.from(context).inflate(R.layout.loading_view, null);
getListView().addFooterView(mFooterView, null, false);
setListAdapter(customAdapter);
getListView().setOnScrollListener(this);
}
public void setNumbersOfItems() {
if (listaLugares.totalItems > 100) {
MAXIMUM_ITEMS = 100;
nInitial = 25;
} else {
MAXIMUM_ITEMS = listaLugares.totalItems;
nInitial = listaLugares.totalItems;
}
Log.v("NUMBER OF ITEMS", "Number: " + MAXIMUM_ITEMS + "-"+ nInitial);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
//Intent myIntent = new Intent(getActivity(), DetalleActivity.class);
//myIntent.putExtra("param_id", appState.lista.get(position).id);
//getActivity().startActivity(myIntent);
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (!mIsLoading && mMoreDataAvailable) {
if (totalItemCount >= MAXIMUM_ITEMS) {
mMoreDataAvailable = false;
getListView().removeFooterView(mFooterView);
} else if (totalItemCount - AUTOLOAD_THRESHOLD <= firstVisibleItem + visibleItemCount) {
ventana = ventana + 10;
listaLugares.readPlaces(10, ventana, listaLugares.idCategoria);
mIsLoading = true;
mHandler.postDelayed(mAddItemsRunnable, 1000);
}
}
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// Ignore
}
#Override
public void onStart() {
super.onStart();
if (mWasLoading) {
mWasLoading = false;
mIsLoading = true;
mHandler.postDelayed(mAddItemsRunnable, 1000);
}
}
#Override
public void onStop() {
super.onStop();
mHandler.removeCallbacks(mAddItemsRunnable);
mWasLoading = mIsLoading;
mIsLoading = false;
ventana = ventana;
}
#Override
public void onDestroyView() {
super.onDestroyView();
}
(ListAdapter.java) -> Method in adapter class to add more items.
public class ListAdapter extends ArrayAdapter<Lugar> {
Context context;
List<Lugar> values;
ListaLugares listaLugares;
private int mCount = 20;
public ListAdapter(Context context, List<Lugar> values) {
super(context, R.layout.row, values);
this.context = context;
this.values = values;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(R.layout.row, parent, false);
TextView firstText = (TextView) row.findViewById(R.id.titulo);
TextView secondText = (TextView) row.findViewById(R.id.categoria);
firstText.setText(values.get(position).nombre);
secondText.setText(values.get(position).categoriaNombre);
return row;
}
public void addMoreItems(int count) {
mCount += count;
notifyDataSetChanged();
}
#Override
public int getCount() {
return mCount;
}
#Override
public long getItemId(int position) {
return position;
}
}
I acceded to a previous edited version to see some code.
The first call works because you are starting with a value of 15 on initiaItems and you set it on mCount on the ListAdapter constructor.
public ListAdapter(Context context, List<Lugar> values, int count) {
super(context, R.layout.row, values);
this.context = context;
this.values = values;
this.mCount = count;
}
So when android renders the listview, it acces to the function ListAdapter.getCount() and returns mCount (15).
But when you scroll it can call to
public void addMoreItems(int count, int idCategoria) {
appState = ((ListaLugares)getContext().getApplicationContext());
appState.readPlaces(15, mCount, idCategoria);
mCount += count;
notifyDataSetChanged();
}
If the number of items returned by the appState.readPlaces is unknown why are you adding a number to mCount mCount += count;?
If appState.readPlaces returns 14 and count is 15 when the listview is rendered it will suppose there are 15+15 items when there are 15+14 so the last item will crash.
mCount should obtain the length from the object that keeps the data from the API calls, in your case I think it will be appState.lista.

Categories

Resources