Gridview onScroll method gets called always, without user scroll - android-gridview

I have a customized gridview where i'm checking onScroll method to find the end of the list. If the scroll reaches the end of the list, it will again add few elements in to the list.
gridview.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView arg0, int arg1) {
}
#Override
public void onScroll(AbsListView arg0, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
int lastInScreen = firstVisibleItem + visibleItemCount;
//is the bottom item visible & not loading more already ? Load more !
if((lastInScreen == totalItemCount) && (!loadingMore))
{
new LoadDataTask().execute();
}
}
});
And this is my Asynchronous task class..
private class LoadDataTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
if (isCancelled()) {
return null;
}
loadingMore = true;
for (int i = 0; i < mNames.length; i++)
mListItems.add(mNames[i]);
return null;
}
#Override
protected void onPostExecute(Void result) {
mListItems.add("Added after load more");
loadingMore=false;
adapter.notifyDataSetChanged();
super.onPostExecute(result);
}
#Override
protected void onCancelled() {
}
}
Now the issue is that the onScroll method keep on calling. It doesn't stop even when the user not scrolling. Can anyone have a solution ?

Please check the answer for this question: onScroll gets called when I set listView.onScrollListener(this), but without any touch .
The same is true for the GridView, since it has AbsListView as superclass just as ListView does.

Related

Best approach to use DiffUtil with LIveData + Room Database?

I am using Room Database with LiveData , but my Local Database is updating too fast as per our requirement and at the same time i have to reload my recycler view .instead of calling notifyDataSetChanged() to adapter , i am trying to use DiffUtil , but is crashing or not reloading properly , this is uncertain .
i am following this tutorial :
Tutorials Link here
MyAdapter :
public class SwitchGridAdapter extends RecyclerView.Adapter<SwitchGridAdapter.ViewHolder> {
private List<Object> allItemsList;
private LayoutInflater mInflater;
private OnItemClickListener mClickListener;
private Context context;
private Queue<List<Object>> pendingUpdates =
new ArrayDeque<>();
// data is passed into the constructor
public SwitchGridAdapter(Context context,List<Appliance> applianceList,List<ZmoteRemote> zmoteRemoteList) {
this.mInflater = LayoutInflater.from(context);
this.context = context;
allItemsList = new ArrayList<>();
if (applianceList!=null) allItemsList.addAll(applianceList);
if (zmoteRemoteList!=null)allItemsList.addAll(zmoteRemoteList);
}
// inflates the cell layout from xml when needed
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R .layout.switch_grid_item, parent, false);
return new ViewHolder(view);
}
// binds the data to the textview in each cell
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
// Doing some update with UI Elements
}
// total number of cells
#Override
public int getItemCount() {
return allItemsList.size();
}
// stores and recycles views as they are scrolled off screen
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener,View.OnLongClickListener {
TextView myTextView;
ImageView imgSwitch;
ViewHolder(View itemView) {
super(itemView);
myTextView = (TextView) itemView.findViewById(R.id.txtSwitchName);
imgSwitch = (ImageView) itemView.findViewById(R.id.imgSwitchStatus);
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
}
#Override
public void onClick(View view) {
// handling click
}
#Override
public boolean onLongClick(View view) {
return true;
}
// convenience method for getting data at click position
Object getItem(int id) {
return allItemsList.get(id);
}
// allows clicks events to be caught
public void setClickListener(OnItemClickListener itemClickListener) {
this.mClickListener = itemClickListener;
}
// parent activity will implement this method to respond to click events
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongPressListner(View view, int position);
}
// ✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅
// From This Line Reloading with Diff Util is Done .
//✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅
public void setApplianceList( List<Appliance> applianceList,List<ZmoteRemote> zmoteRemoteList)
{
if (allItemsList == null)
allItemsList = new ArrayList<>();
List<Object> newAppliances = new ArrayList<>();
if (applianceList!=null) newAppliances.addAll(applianceList);
updateItems(newAppliances);
}
// when new data becomes available
public void updateItems(final List<Object> newItems) {
pendingUpdates.add(newItems);
if (pendingUpdates.size() > 1) {
return;
}
updateItemsInternal(newItems);
}
// This method does the heavy lifting of
// pushing the work to the background thread
void updateItemsInternal(final List<Object> newItems) {
final List<Object> oldItems = new ArrayList<>(this.allItemsList);
final Handler handler = new Handler();
new Thread(new Runnable() {
#Override
public void run() {
final DiffUtil.DiffResult diffResult =
DiffUtil.calculateDiff(new DiffUtilHelper(oldItems, newItems));
handler.post(new Runnable() {
#Override
public void run() {
applyDiffResult(newItems, diffResult);
}
});
}
}).start();
}
// This method is called when the background work is done
protected void applyDiffResult(List<Object> newItems,
DiffUtil.DiffResult diffResult) {
dispatchUpdates(newItems, diffResult);
}
// This method does the work of actually updating
// the backing data and notifying the adapter
protected void dispatchUpdates(List<Object> newItems,
DiffUtil.DiffResult diffResult) {
// ❌❌❌❌❌❌ Next Line is Crashing the app ❌❌❌❌❌
pendingUpdates.remove();
dispatchUpdates(newItems, diffResult);
if (pendingUpdates.size() > 0) {
updateItemsInternal(pendingUpdates.peek());
}
}
}
Observing LiveData
public void setUpAppliancesListLiveData()
{
if (applianceObserver!=null)
{
applianceObserver = null;
}
Log.e("Appliance Fetch","RoomName:"+this.roomName);
applianceObserver = new Observer<List<Appliance>>() {
#Override
public void onChanged(#Nullable List<Appliance> applianceEntities) {
// Log.e("Appliance Result","Appliance List \n\n:"+applianceEntities.toString());
new Thread(new Runnable() {
#Override
public void run() {
List<Appliance> applianceListTemp = applianceEntities;
zmoteRemoteList = new ArrayList<>(); //appDelegate.getDatabase().zmoteRemoteDao().getRemoteList(roomName);
// Sort according to name
Collections.sort(applianceListTemp, new Comparator<Appliance>() {
#Override
public int compare(Appliance item, Appliance t1) {
String s1 = item.getSwitchName();
String s2 = t1.getSwitchName();
return s1.compareToIgnoreCase(s2);
}
});
if(getActivity()!=null) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
applianceList = applianceListTemp;
mRecyclerView.getRecycledViewPool().clear();
adapter.setApplianceList(applianceList,zmoteRemoteList);
}
});
}
}
}).start();
}
};
appDelegate.getDatabase().applianceDao().getApplinaceListByRoomName(this.roomName).observe(this, applianceObserver);
}

Recyclerview not populating in asynctask android

I have two methods which I need to run asynchronously in android.First I want to run getContactsList() and then populateList().I tried to use Asynctask but it skips the recyclerview populating code and then prints the next line in the log while it works well in onCreate.Please help me
private class TaskOne extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
Log.i("do in background", "running");
getContactList();
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
pDialog.cancel();
Log.i("onPostExecute", "running");
populateList();
isContacts_fetched();
Log.i("populateList", "finished");
}
}
calling in onCreate() methods
new TaskOne().execute();
populateList() code
public void populateList() {
Log.i("Populate List","Entered");
// Toast.makeText(this,String.valueOf(Common.selectedContactNos.size()),Toast.LENGTH_LONG).show();
displayRecyclerAdapter = new DisplayRecyclerAdapter(DisplayContacts.this);
LinearLayoutManager mLinearLayoutManager = new LinearLayoutManager(this);
recyclerView_contacts.setAdapter(displayRecyclerAdapter);
recyclerView_contacts.setLayoutManager(mLinearLayoutManager);
displayRecyclerAdapter.notifyDataSetChanged();
}

Return value from AsyncTask

I have a class AsyncTask as an inner class to a fragment.
public class MyAsyncTask extends AsyncTask<String,double[],double[]> {
double posScoreb = 0.0;
double negScoreb= 0.0;
public AsyncResponse delegate = null;
#Override
protected void onPreExecute() {
progressBar.setVisibility(View.VISIBLE);}
#Override
protected double[] doInBackground(String... params) {
s = new SentiCalc(getActivity().getAssets());
s.setInput(String.valueOf(params[0]));
double[] set = new double[]{};
try {
set = s.getSenti();
} catch (IOException e) {
e.printStackTrace();
}
posScoreb = set[1];
negScoreb = set[2];
return new double[]{posScoreb,negScoreb};
}
#Override
protected void onPostExecute(double[] result) {
progressBar.setVisibility(View.GONE);
result[0] = posScoreb;
result[1] = negScoreb;
delegate.processFinish(result);
Toast.makeText(getActivity(), "Done",Toast.LENGTH_LONG).show();
}
}
I am calling it from a method saveNote() inside the same fragment.`
asyncTask = new MyAsyncTask() ;
asyncTask.delegate =this;
asyncTask.execute(content);
I want AsyncTask class to return values inside a result(double array) to saveNote method. As shown in this, How to get the result of OnPostExecute() to main activity because AsyncTask is a separate class?.
I created an interface and made my fragment class to implement it.
public interface AsyncResponse {
void processFinish(double[] output);}
As a result of which I am able to get the result inside a method processFinish
within the same fragment.
#Override
public void processFinish(double[] output) {
double posScore,negScore;
posScore = output[0];
negScore = output[1];
Toast.makeText(getActivity(),String.valueOf(posScore),Toast.LENGTH_LONG).show();
Toast.makeText(getActivity(), String.valueOf(negScore), Toast.LENGTH_LONG).show();
}
How can I get this result inside a saveNote method from which I am calling AsyncTask class?

SeekBar won't run when media player are playing on xamarin android

The SeekBar widget is an interactive slider that allows the user to select one value from a range of values. As the user moves the slider left or right, the value of the SeekBar will change
public int getProgressPercentage(int currentDuration, int totalDuration)
{
int percentage;
int currentSeconds = (int)(currentDuration / 1000);
int totalSeconds = (int)(totalDuration / 1000);
//calculating percentage
percentage = (((int)currentSeconds) / totalSeconds) * 100;
return percentage;
}
public void UpdatedTimerTask()
{
//Displaying time
//txtCurrentTimer.Text = utils.miliSecondsTotimer (player.CurrentPosition);
//txtTotalTimer.Text = utils.miliSecondsTotimer (player.Duration);
//Updating progress bar(seekbar)
int progress=(int)(utils.getProgressPercentage(player.CurrentPosition,player.Duration));
seekBar.Progress = progress;
}
void SeekBar_ProgressChanged (object sender, SeekBar.ProgressChangedEventArgs e)
{
UpdatedTimerTask ();
}
public void StartMedia(string url_string)
{
player = new MediaPlayer ();
seekBar.Progress = 0;
seekBar.Max = 100;
player.Reset ();
player.SetAudioStreamType (Stream.Music);
player.SetDataSource(url_string);
player.Prepare();
player.Start ();
imgPlayorPause.SetImageResource (Resource.Drawable.ic_pause_black_36dp);
UpdatedTimerTask ();
}
seekBar.SetOnSeekBarChangeListener (this); has some invalid argument.
SeekBar won't run when media player are playing.
You have two options:
SetOnSeekBarChangeListener()
If you want to use SetOnSeekBarChangeListener you have to implement IOnSeekBarChangeListener in the class of this (usually your activity. The disadvantage of this is, that you can only have one event listener.
[Activity]
public class MyActivity : Activity, SeekBar.IOnSeekBarChangeListener
{
protected override void OnCreate(Bundle bundle)
{
// ...
seekbar.SetOnSeekBarChangeListener(this);
}
public void OnProgressChanged(SeekBar seekBar, int progress, bool fromUser)
{
// do some stuff
}
public void OnStartTrackingTouch(SeekBar seekBar)
{
}
public void OnStopTrackingTouch(SeekBar seekBar)
{
}
}
ProgressChanged Event
Xamarin maps Java methods that are called like SetXyzListener to the event called Xyz.
If you want to use ProgressChanged you have to register your handler with seekbar.ProgressChanged += SeekbarOnProgressChanged. The disadvantage of this is, that you have to ensure to remove the handler with seekbar.ProgressChanged -= SeekbarOnProgressChanged when you do not need the event anymore. You should do this in the counterpart of the lifecycle method where you have added the handler. In the following example I used OnResume and OnPause.
[Activity]
public class MyActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// ...
}
protected override void OnResume()
{
base.OnResume();
seekbar.ProgressChanged += SeekbarOnProgressChanged;
}
protected override void OnPause()
{
seekbar.ProgressChanged -= SeekbarOnProgressChanged;
base.OnPause();
}
private void SeekbarOnProgressChanged(object sender, SeekBar.ProgressChangedEventArgs progressChangedEventArgs)
{
// do some stuff
}
}

Recycler View with Header and Edit Text

I have a recyclerview with a header achieved by using two different element types. In my header there is an edit text which I want to use for filtering the nonheader elements of the list. Below is my current implementation, I have one concern and one problem with it.
My concern is that what I am doing in publishResults with the notifyItemRangeRemoved and notifyItemInserted is the wrong way to update the recycler view. I originally was doing notifyDatasetChanged but his would cause the header row to be refreshed too and the edit text to lose focus. What I really want is a way to refresh only the item rows and leave the header row untouched.
My current problem is that with the existing code if I scroll down too much the edit text looses focus. I want the edit text to keep focus even if I scroll to the bottom of the list.
The code used to use a ListView with setHeaderView and that worked somehow so there must be someway of achieving the goal just not sure what the trick with a recycler view is. Any help is much appreciated.
public class SideListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Filterable {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private final List<String> data;
public List<String> filteredData;
private HeaderActionListener headerActionListener;
public SideListAdapter(Context context, ArrayList<String> data, HeaderActionListener headerActionListener) {
this.data = data;
filteredData = new ArrayList<>(data);
this.context = context;
this.headerActionListener = headerActionListener;
}
#Override
public Filter getFilter() {
return new TestFilter();
}
static class SideListItem extends RecyclerView.ViewHolder {
LinearLayout baseLayout;
public SideListItem(View itemView) {
super(itemView);
baseLayout = (LinearLayout) itemView.findViewById(R.id.settings_defaultcolor);
}
}
class SideListHeader extends SideListHeader {
EditText sort;
public SideListHeaderLoggedIn(View itemView) {
super(itemView);
sort = (EditText) itemView.findViewById(R.id.sort);
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_ITEM) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
return new SideListItem(v);
} else if (viewType == SideListHeader) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header, parent, false);
return new SideListHeader(v);
}
throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");
}
public interface HeaderActionListener {
boolean onSortEditorAction(TextView arg0, int arg1, KeyEvent arg2);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof SideListHeader) {
final SideListHeader sideListHeader = (SideListHeader) holder;
sideListHeader.sort.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
}
});
sideListHeader.sort.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
#Override
public void afterTextChanged(Editable editable) {
String result = sideListHeader.sort.getText().toString().replaceAll(" ", "");
getFilter().filter(result);
}
});
}
if (holder instanceof SideListItem) {
// Inflate normal item //
}
}
// need to override this method
#Override
public int getItemViewType(int position) {
if (isPositionHeader(position)) {
return TYPE_HEADER;
}
return TYPE_ITEM;
}
private boolean isPositionHeader(int position) {
return position == 0;
}
//increasing getItemcount to 1. This will be the row of header.
#Override
public int getItemCount() {
return filteredData.size() + 1;
}
private class TestFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
String prefix = constraint.toString().toLowerCase();
if (prefix.isEmpty()) {
ArrayList<String> list = new ArrayList<>(data);
results.values = list;
results.count = list.size();
} else {
final ArrayList<String> list = new ArrayList<>(data);
final ArrayList<String> nlist = new ArrayList<>();
for (int i = 0 ; i < list.size(); i++) {
String item = list.get(i);
if (item.contains(prefix)) {
nlist.add(item);
}
}
results.values = nlist;
results.count = nlist.size();
}
return results;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
notifyItemRangeRemoved(1, getItemCount()-1);
filteredData.clear();
filteredData.addAll((List<String>)results.values);
for(int i = 1; i < getItemCount() - 1; i++){
notifyItemInserted(i);
}
}
}
}
I'm not sure how correct this way is, but in my code I implemented it like that
private var headerList: List<HeaderItem> = listOf(HeaderItem("Title"))
private fun searchItem(items: List<Items>, query: String) {
items.filterIsInstance<MainItem>().filter { filteredItems ->
filteredItems.header.lowercase().contains(query.lowercase())
}.let { searchedItems ->
rvAdapter.submitList(headerList + searchedItems)
}
}
This way I was able to preserve header element when I did my search

Resources