Passing a variable between methods inside the same activity - variables

Within the MainActivity I've two main methods. One is a cursor and the other is a spinner. Within the cursor method, I retrieve some data from the SQlite database to be used as variables.
in MainActivity
//reinserted EGG outside
String EGG;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
.....
private void gettree() {
Cursor cursor = db.getBall();
cur(cursor);
}}
public void cur(Cursor ur) {
int orange = 0;
if (ur.moveToFirst()) {
do {
int apple = ur.getInt(0);
orange = 0 + apple;
} while(ur.moveToNext());
}
ur.close();
//the EGG variable
int EGG = orange;
}}
#Override
public void onItemSelected(AdapterView<?> adapterView,
View view, int i, long l) {
//eclipse does not recognize EGG here
Toast.makeText(this, EGG, Toast.LENGTH_SHORT).show();
Spinner clink = (Spinner)adapterView;
if(clink.getId() == R.id.dpz)
{ TextView mysdp =(TextView) view;
....
}}
in DatabaseAdapter
public Cursor getBall() {
String[] A = new String[] {"first"};
String condition = "_id = '1'";
Cursor e = db.query(true, "maintable", A,
condition, null, null, null, null, null);
if (returnee != null) {
e.moveToFirst();
}
return returnee;
}
What I hope to do is use the EGG variable to the onItemSelected method for the spinner using the fastest and most efficient way. I'm new to android and I'm not sure exactly how to do this. I tried declaring the EGG variable but it does not update (in fact eclipse says its not being used).
Do I have to reaccess the getBall to get the variable from inside the onItemSelected method and I'm not sure how to do that. I guess I am really hoping for a more simple way to access the EGG variable I've already found in the first method.
Thanks in advance.

As pointed out by Tenfour04, I've declared the variable outside of the class and its viewable across the activity.

Related

Trying to make Unending Boundaries In a Runner Game

Trying to make a runner game. I don't want my character to fall off the edge of the map. How do I make the character always center of the path? I want to make so that my character is always center of the path as it is being instantiated and to never fall off the map.
public class GroundTile : MonoBehaviour
{
GroundSpawner groundSpawner;
// Start is called before the first frame update
private void Start()
{
groundSpawner = GameObject.FindObjectOfType<GroundSpawner>();
}
private void OnTriggerExit (Collider other)
{
groundSpawner.SpawnTile();
Destroy(gameObject, 2);
}
// Update is called once per frame
void Update()
{
}
}
public class GroundSpawner : MonoBehaviour
{
public GameObject groundTile;
Vector3 nextSpawnPoint;
// Start is called before the first frame update
public void SpawnTile()
{
GameObject temp = Instantiate(groundTile, nextSpawnPoint, Quaternion.identity);
nextSpawnPoint = temp.transform.GetChild(1).transform.position;
}
private void Start()
{
for (int i = 0; i < 10; i++)
{
SpawnTile();
}
}
}
I tried so many methods.

change item color on click in recyclerview android

I am trying to change the color of the items on click when the action mode is active. The problem is that e.g if there are five items in a recyclerview and you click one, scroll down and select sixth item and destroy the action mode. The next time you start selecting, that sixth item has automatically changed its color without you selecting it. I don't know why it is happening.
public static List<ModelClass> items = new ArrayList<>();
boolean isSelectMode = false;
boolean isActionModeEnabled = false;
public static List<ModelClass> selectList = new ArrayList<>();
#Override
public void onBindViewHolder(#NonNull MyAdapter.MyViewHolder holder, int
position) {
holder.bind(items.get(position));
ModelClass modelClass = items.get(position);
if (modelClass.isChecked() && isActionModeEnabled){
holder.row.setBackgroundColor(Color.GREEN);
modelClass.setChecked(true);
} else {
holder.row.setBackgroundColor(Color.TRANSPARENT);
modelClass.setChecked(false);
}
}
public class MyViewHolder extends RecyclerView.ViewHolder{
public MyViewHolder(#NonNull View itemView) {
super(itemView);
row = itemView.findViewById(R.id.row);
public void bind(ModelClass model) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (isActionModeEnabled) {
isSelectMode = true;
s = items.get(getAdapterPosition());
if (!selectList.contains(s)){
selectList.add(s);
row.setBackgroundColor(Color.GREEN);
model.setChecked(true);
} else {
selectList.remove(s);
row.setBackgroundColor(Color.TRANSPARENT);
model.setChecked(false);
}
}
});
}
The problem is going to be in your view holder binding:
if (modelClass.isChecked() && isActionModeEnabled){
holder.row.setBackgroundColor(Color.GREEN);
modelClass.setChecked(true);
} else {
holder.row.setBackgroundColor(Color.TRANSPARENT);
modelClass.setChecked(false);
}
Remember that view holders are reused. That means that they will retain their internal state unless you change them. Your item list will also remember its state. Make sure you cover all the possible states of the item list and the reused view holders in the code above: You are probably missing a combination.
I recommend that you set a break point in the code above to make sure it is doing what you want. It should become obvious to you once you take a closer look.

how to know if a certain view item is visible [duplicate]

I need to know which elements are currently displayed in my RecyclerView. There is no equivalent to the OnScrollListener.onScroll(...) method on ListViews. I tried to work with View.getGlobalVisibleRect(...), but that hack is too ugly and does not always work too.
Someone any ideas?
First / last visible child depends on the LayoutManager.
If you are using LinearLayoutManager or GridLayoutManager, you can use
int findFirstVisibleItemPosition();
int findFirstCompletelyVisibleItemPosition();
int findLastVisibleItemPosition();
int findLastCompletelyVisibleItemPosition();
For example:
GridLayoutManager layoutManager = ((GridLayoutManager)mRecyclerView.getLayoutManager());
int firstVisiblePosition = layoutManager.findFirstVisibleItemPosition();
For LinearLayoutManager, first/last depends on the adapter ordering. Don't query children from RecyclerView; LayoutManager may prefer to layout more items than visible for caching.
For those who have a logic to be implemented inside the RecyclerView adapter, you can still use the #ernesto approach combined with an on scrollListener to get what you want as the RecyclerView is consulted.
Inside the adapter you will have something like this:
#Override
public void onAttachedToRecyclerView(#NonNull RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
if(manager instanceof LinearLayoutManager && getItemCount() > 0) {
LinearLayoutManager llm = (LinearLayoutManager) manager;
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int visiblePosition = llm.findFirstCompletelyVisibleItemPosition();
if(visiblePosition > -1) {
View v = llm.findViewByPosition(visiblePosition);
//do something
v.setBackgroundColor(Color.parseColor("#777777"));
}
}
});
}
}
Finally, I found a solution to know if the current item is visible, from the onBindViewHolder event in the adapter.
The key is the method isViewPartiallyVisible from LayoutManager.
In your adapter, you can get the LayoutManager from the RecyclerView, which you get as parameter from the onAttachedToRecyclerView event.
You can use recyclerView.getChildAt() to get each visible child, and setting some tag convertview.setTag(index) on these view in adapter code will help you to relate it with adapter data.
Addendum:
The proposed functions findLast...Position() do not work correctly in a scenario with a collapsing toolbar while the toolbar is expanded.
It seems that the recycler view has a fixed height, and while the toolbar is expanded, the recycler is moved down, partially out of the screen. As a consequence the results of the proposed functions are too high. Example: The last visible item is told to be #9, but in fact item #7 is the last one that is on screen.
This behaviour is also the reason why my view often failed to scroll to the correct position, i.e. scrollToPosition() did not work correctly (I finally collapsed the toolbar programmatically).
Every answer above is correct and I would like to add also a snapshot from my working codes.
recycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
// Some code when initially scrollState changes
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// Some code while the list is scrolling
LinearLayoutManager lManager = (LinearLayoutManager) recyclerView.getLayoutManager();
int firstElementPosition = lManager.findFirstVisibleItemPosition();
}
});
Following Linear / Grid LayoutManager methods can be used to check which items are visible.
int findFirstVisibleItemPosition();
int findLastVisibleItemPosition();
int findFirstCompletelyVisibleItemPosition();
int findLastCompletelyVisibleItemPosition();
and if you want to track is item visible on screen for some threshold then you can refer to the following blog.
https://proandroiddev.com/detecting-list-items-perceived-by-user-8f164dfb1d05
For StaggeredGridLayoutManager do this:
RecyclerView rv = findViewById(...);
StaggeredGridLayoutManager lm = new StaggeredGridLayoutManager(...);
rv.setLayoutManager(lm);
And to get visible item views:
int[] viewsIds = lm.findFirstCompletelyVisibleItemPositions(null);
ViewHolder firstViewHolder = rvPlantios.findViewHolderForLayoutPosition(viewsIds[0]);
View itemView = viewHolder.itemView;
Remember to check if it is empty.
You can find the first and last visible children of the recycle view and check if the view you're looking for is in the range:
var visibleChild: View = rv.getChildAt(0)
val firstChild: Int = rv.getChildAdapterPosition(visibleChild)
visibleChild = rv.getChildAt(rv.childCount - 1)
val lastChild: Int = rv.getChildAdapterPosition(visibleChild)
println("first visible child is: $firstChild")
println("last visible child is: $lastChild")
For those who are looking for an answer in Kotlin:
fun getVisibleItem(recyclerView : RecyclerView) {
recyclerView.addOnScrollListener(object: RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
if(newState == RecyclerView.SCROLL_STATE_IDLE) {
val index = (recyclerView.layoutManager.findFirstVisibleItemPosition
//use this index for any operation you want to perform on the item visible on screen. eg. log(arrayList[index])
}
}
})
}
You can explore other methods for getting the position as per your use case.
int findFirstCompletelyVisibleItemPosition()
int findLastVisibleItemPosition()
int findLastCompletelyVisibleItemPosition()
if the visible item position is different from the item position toast message will show on the screen.
myRecyclerview.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
LinearLayoutManager manager= (LinearLayoutManager) myRecyclerview.getLayoutManager();
assert manager != null;
int visiblePosition = manager.findLastCompletelyVisibleItemPosition();
if(visiblePosition > -1&&a!=visiblePosition) {
Toast.makeText(context,String.valueOf(visiblePosition),Toast.LENGTH_SHORT).show();
//do something
a=visiblePosition;
}
}
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//Some code while the list is scrolling
}
});

Recycler View: Inconsistency detected. Invalid view holder adapter positionViewHolder

Recycler View Inconsistency Detected error, coming while scrolling fast or scrolling while loading more items..
FATAL EXCEPTION: main
Process: com.pratap.endlessrecyclerview, PID: 21997
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{56a082c position=40 id=-1, oldPos=39, pLpos:39 scrap [attachedScrap] tmpDetached no parent}
at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:4251)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4382)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4363)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1961)
at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1370)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1333)
at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:562)
at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2864)
at android.support.v7.widget.RecyclerView.consumePendingUpdateOperations(RecyclerView.java:1445)
at android.support.v7.widget.RecyclerView.access$400(RecyclerView.java:144)
at android.support.v7.widget.RecyclerView$1.run(RecyclerView.java:282)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
at android.view.Choreographer.doCallbacks(Choreographer.java:670)
at android.view.Choreographer.doFrame(Choreographer.java:603)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
at android.os.Handler.handleCallback(Handler.java:746)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5443)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
Adapter
public class DataAdapter extends RecyclerView.Adapter {
private final int VIEW_ITEM = 1;
private final int VIEW_PROG = 0;
private List<Feed> mFeed;
// The minimum amount of items to have below your current scroll position
// before loading more.
private int visibleThreshold = 5;
private int lastVisibleItem, totalItemCount;
private boolean loading;
private OnLoadMoreListener onLoadMoreListener;
public DataAdapter(List<Feed> feeds, RecyclerView recyclerView) {
mFeed = feeds;
if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView
.getLayoutManager();
recyclerView
.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView,
int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager
.findLastVisibleItemPosition();
if (!loading
&& totalItemCount <= (lastVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
loading = true;
}
}
});
}
}
#Override
public int getItemViewType(int position) {
return mFeed.get(position) == null ? VIEW_PROG : VIEW_ITEM;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
RecyclerView.ViewHolder vh;
if (viewType == VIEW_ITEM) {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.list_row, parent, false);
vh = new StudentViewHolder(v);
}
else {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.progress_item, parent, false);
vh = new ProgressViewHolder(v);
}
return vh;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof StudentViewHolder) {
Feed singleStudent= (Feed) mFeed.get(position);
((StudentViewHolder) holder).tvName.setText(singleStudent.getTitle());
((StudentViewHolder) holder).student= singleStudent;
} else {
ProgressViewHolder.PROGRESS_BAR.setIndeterminate(true);
}
}
public void setLoaded() {
loading = false;
}
public void addFeed(Feed feed) {
mFeed.add(feed);
//mFeed.addAll(0, (Collection<? extends Feed>) feed);
notifyItemInserted(mFeed.size());
//notifyItemRangeInserted(0,mFeed.size());
notifyDataSetChanged();
//notifyItemInserted(mFeed.size());
//setLoaded();
//notifyItemInserted(mFeed.size());
}
public void removeAll(){
mFeed.clear();
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return mFeed.size();
}
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener = onLoadMoreListener;
}
public static class StudentViewHolder extends RecyclerView.ViewHolder {
public TextView tvName;
public Feed student;
public StudentViewHolder(View v) {
super(v);
tvName = (TextView) v.findViewById(R.id.tvName);
//tvEmailId = (TextView) v.findViewById(R.id.tvEmailId);
}
}
public static class ProgressViewHolder extends RecyclerView.ViewHolder {
//public ProgressBar progressBar;
public static ProgressBar PROGRESS_BAR;
public ProgressViewHolder(View v) {
super(v);
PROGRESS_BAR = (ProgressBar) v.findViewById(R.id.progressBar1);
// progressBar = (ProgressBar) v.findViewById(R.id.progressBar1);
}
}
}
Activity
public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {
private Toolbar toolbar;
private TextView tvEmptyView;
private RecyclerView mRecyclerView;
private DataAdapter mAdapter;
private LinearLayoutManager mLayoutManager;
private RestManager mManager;
private List<Feed> mFeed;
SwipeRefreshLayout mSwipeRefreshLayout;
protected Handler handler;
private int currentPage=1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
tvEmptyView = (TextView) findViewById(R.id.empty_view);
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
mSwipeRefreshLayout= (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
mSwipeRefreshLayout.setOnRefreshListener(this);
//studentList = new ArrayList<Student>();
mFeed = new ArrayList<Feed>();
handler = new Handler();
if (toolbar != null) {
setSupportActionBar(toolbar);
getSupportActionBar().setTitle("Android Students");
}
mManager = new RestManager();
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
// use a linear layout manager
mRecyclerView.setLayoutManager(mLayoutManager);
// create an Object for Adapter
mAdapter = new DataAdapter(mFeed,mRecyclerView);
// set the adapter object to the Recyclerview
mRecyclerView.setAdapter(mAdapter);
// mAdapter.notifyDataSetChanged();
loadData(false);
// if (mFeed.isEmpty()) {
// mRecyclerView.setVisibility(View.GONE);
// tvEmptyView.setVisibility(View.VISIBLE);
//
// } else {
// mRecyclerView.setVisibility(View.VISIBLE);
// tvEmptyView.setVisibility(View.GONE);
// }
mAdapter.setOnLoadMoreListener(new OnLoadMoreListener() {
#Override
public void onLoadMore() {
//add null , so the adapter will check view_type and show progress bar at bottom
mFeed.add(null);
mAdapter.notifyItemInserted(mFeed.size() - 1);
handler.postDelayed(new Runnable() {
#Override
public void run() {
// remove progress item
mFeed.remove(mFeed.size() - 1);
// mAdapter.notifyItemRemoved(mFeed.size());
//add items one by one
int start = mFeed.size();
currentPage++;
Log.d("CurrentPage", String.valueOf(currentPage));
Call<Results> listCall = mManager.getFeedApi().getAllFeeds(1);
listCall.enqueue(new Callback<Results>() {
#Override
public void onResponse(Call<Results> call, Response<Results> response) {
mSwipeRefreshLayout.setRefreshing(false);
if (response.isSuccess()) {
if (response.body() != null) {
Results feedList = response.body();
// List<Results> newUsers = response.body();
Log.d("Retrofut", String.valueOf(feedList));
for (int i = 0; i < feedList.results.size(); i++) {
Feed feed = feedList.results.get(i);
// mFeed.add(feed);
mAdapter.addFeed(feed);
// mAdapter.notifyDataSetChanged();
//mAdapter.notifyItemInserted(mFeed.size());
}
// mAdapter.notifyDataSetChanged();
}
}
}
#Override
public void onFailure(Call<Results> call, Throwable t) {
Log.d("Retrofut", "Error");
mFeed.remove(mFeed.size() - 1);
mAdapter.notifyItemRemoved(mFeed.size());
mAdapter.setLoaded();
mSwipeRefreshLayout.setRefreshing(false);
}
});
// for (int i = 1; i <= 20; i++) {
// studentList.add(new Student("Student " + i, "androidstudent" + i + "#gmail.com"));
//
// }
mAdapter.setLoaded();
//or you can add all at once but do not forget to call mAdapter.notifyDataSetChanged();
}
}, 2000);
}
});
}
// load initial data
private void loadData(final boolean removePreData) {
Call<Results> listCall = mManager.getFeedApi().getAllFeeds(1);
listCall.enqueue(new Callback<Results>() {
#Override
public void onResponse(Call<Results> call, Response<Results> response) {
if (response.isSuccess()) {
if (response.body() != null) {
// if(removePreData) mAdapter.removeAll();
Results feedList = response.body();
Log.d("Retrofut", String.valueOf(feedList));
for (int i = 0; i < feedList.results.size(); i++) {
Feed feed = feedList.results.get(i);
// mFeed.add(feed);
//mAdapter.notifyDataSetChanged();
mAdapter.addFeed(feed);
}
mSwipeRefreshLayout.setRefreshing(false);
}
}
}
#Override
public void onFailure(Call<Results> call, Throwable t) {
Log.d("Retrofut", String.valueOf(t));
mFeed.remove(mFeed.size() - 1);
mAdapter.notifyItemRemoved(mFeed.size());
mAdapter.setLoaded();
mSwipeRefreshLayout.setRefreshing(false);
}
}
);
// for (int i = 1; i <= 20; i++) {
// studentList.add(new Student("Student " + i, "androidstudent" + i + "#gmail.com"));
//
// }
mSwipeRefreshLayout.setRefreshing(true);
}
#Override
public void onRefresh() {
mFeed.clear();
mAdapter.notifyDataSetChanged();
loadData(true);
currentPage=1;
}
}
put this line along with setting recyclerView. issue was fixed by
setting ItemAnimator to null for RecyclerView.
in kotlin
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.itemAnimator = null
in java
recyclerView.setItemAnimator(null);
It looks similar with known android bug
There are quite ugly, but working approach
public class WrapContentLinearLayoutManager extends LinearLayoutManager {
//... constructor
#Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
try {
super.onLayoutChildren(recycler, state);
} catch (IndexOutOfBoundsException e) {
Log.e("Error", "IndexOutOfBoundsException in RecyclerView happens");
}
}
}
mRecyclerView.setLayoutManager(new WrapContentGridLayoutManager(getContext(), spanCount));
For me it works without any by-effect.
This issue is a known bug of RecyclerView. The best solution is, clear the list every time before refresh RecyclerView.
For fix this issue just call notifyDataSetChanged() with empty list before updating recycle view.
For example
//Method for refresh recycle view
if (!yourList.isEmpty())
yourList.clear(); //The list for update recycle view
adapter.notifyDataSetChanged();
Use this to refresh a RecyclerView
items.clear(); //here items is an ArrayList populating the RecyclerView
adapter.notifyDataSetChanged();
items.addAll(list);// add new data
adapter.notifyItemRangeInserted(0, items.size);// notify adapter of new data
`
I had similiar issue, and also this solution has helped me, after I've added new item to my RV:
recyclerView.getRecycledViewPool().clear();
adapter.notifyDataSetChanged();
Maybe you can try this before refresh the adapter:
dataList.clear();
patrolListAdapter.notifyDataSetChanged();
In my case I was doing it as notifyItemInserted(position);
That caused me this issue then i used as and it worked perfectly.notifyItemRangeInserted(startIndex,endIndex);
I had this problem when scrolling fast through my endless/paging RecyclerView. The root of my problem came from the fact that I had a “header” item at the beginning of the list, this header item was not a part of the data source, it was just inserted at the beginning of the adapter list. So when scrolling fast and adding new pages of items to the RecyclerView Adapter and notify the adapter that new data had been inserted, I was not taking into account the additional header item, thus making the size of the adapter’s list wrong... and causing this exception...
So in short, if you’re using a header/footer in our RecyclerView adapter make sure you take it into account when updating the adapters data.
Example:
public void addNewPageToList(List<MyData> list)
{ //
// Make sure you account for any header/footer in your list!
//
// Add one to the currentSize to account for the header item.
//
int currentSize = this.adapterList.size() + 1;
this.adapterList.addAll(list);
notifyItemRangeInserted(currentSize, this.adapterList.size());
}
Edit:
I guess you could always just use the adapter method getItemCount() to get the size, instead of getting the size from the “data list” and adding to it. Your getItemCount() method should already be taking into account any additional headers/footers/etc that you have in your list.
The problem is in this line of code:
mFeed = feeds;
you are assigning mFeed to the caller's instance feeds so whenever the caller changes it's variable (may be adding, clearing or removing items), your local mFeed will change
try to change to
mFeed.addAll(feeds);
don't forget to initialize mFeed to any list tat fits your needs like mFeed = new ArrayList<>();
put this line along with setting recyclerView. issue was fixed by setting ItemAnimator to null for RecyclerView.
in kotlin
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.itemAnimator = null
I'm using the recyclerview from mikepenz. And any update to the items using .set(item) was causing this issue.
For some reason, setting recylerView.itemAnimator = null, resolved the issue. This is a known android bug.
In my case, I was using RecyclerView from Firebase UI. Initially, the logic to initialize the RecyclerView was in onCreate(). To fix, I put the logic in onResume() and seems to be working for me. I had this error when going back to the Activity which had the RecyclerView. So, everytime the Activity screen is refreshed, the new data is loaded.
I had similar problem. Removing all views from RecyclerView helped me:
RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
layoutManager.removeAllViews();
For me the issue was I wasn't posting notifyDatasetChanged when the data set changed as I implemented incremental search.
I had a list that was filtered based on what the user searched in the search widget. For each item in the list, I was making a remote request, and when I got the result back, I was updating that particular cell.
I had to do both notifies for the recycler view to work
Filter the original data set then post the dataset change
this.searchResultTable?.post {
this.searchResultTable?.adapter?.notifyDataSetChanged()
}
After receiving response, post notifications again
this.searchResultTable?.post {
this.searchResultTable?.adapter?.notifyItemChanged(index, updateDataHashMap)
}
You have to post updates rather than sending notifiy messages directly in order to prevent the recycler view from crashing when the update comes in before the view is laid out.
Another important gotcha is that when you post the individual updates after the remote response, you have to make sure that the list the user currently sees is the list that existed when the requests were sent.
For my case in adapter there was notifyItemRangeInserted and I replaced it with notifyItemRangeChanged

How do I hide a PivotItem?

I have a Page with an Pivot-control and in some cases I don't want to show a particular PivotItem.
Setting the Visibility to collapsed doesn't seem to affect it at all.
Any suggestions?
you should be able to remove or add PivotItems dynamically in your Pivot by using the respective collection methods on Pivot.Items .
Let me know if this doesn't work for your scenario.
I've created a custom behavior for showing/hiding pivot item
Usage:
< i:Interaction.Behaviors>
< common:HideablePivotItemBehavior Visible="{Binding variable}" />
</ i:Interaction.Behaviors >
Code:
/// <summary>
/// Behavior which enables showing/hiding of a pivot item`
/// </summary>
public class HideablePivotItemBehavior : Behavior<PivotItem>
{
#region Static Fields
public static readonly DependencyProperty VisibleProperty = DependencyProperty.Register(
"Visible",
typeof(bool),
typeof(HideablePivotItemBehavior),
new PropertyMetadata(true, VisiblePropertyChanged));
#endregion
#region Fields
private Pivot _parentPivot;
private PivotItem _pivotItem;
private int _previousPivotItemIndex;
private int _lastPivotItemsCount;
#endregion
#region Public Properties
public bool Visible
{
get
{
return (bool)this.GetValue(VisibleProperty);
}
set
{
this.SetValue(VisibleProperty, value);
}
}
#endregion
#region Methods
protected override void OnAttached()
{
base.OnAttached();
this._pivotItem = AssociatedObject;
}
private static void VisiblePropertyChanged(DependencyObject dpObj, DependencyPropertyChangedEventArgs change)
{
if (change.NewValue.GetType() != typeof(bool) || dpObj.GetType() != typeof(HideablePivotItemBehavior))
{
return;
}
var behavior = (HideablePivotItemBehavior)dpObj;
var pivotItem = behavior._pivotItem;
// Parent pivot has to be assigned after the visual tree is initialized
if (behavior._parentPivot == null)
{
behavior._parentPivot = (Pivot)behavior._pivotItem.Parent;
// if the parent is null return
if (behavior._parentPivot == null)
{
return;
}
}
var parentPivot = behavior._parentPivot;
if (!(bool)change.NewValue)
{
if (parentPivot.Items.Contains(behavior._pivotItem))
{
behavior._previousPivotItemIndex = parentPivot.Items.IndexOf(pivotItem);
parentPivot.Items.Remove(pivotItem);
behavior._lastPivotItemsCount = parentPivot.Items.Count;
}
}
else
{
if (!parentPivot.Items.Contains(pivotItem))
{
if (behavior._lastPivotItemsCount >= parentPivot.Items.Count)
{
parentPivot.Items.Insert(behavior._previousPivotItemIndex, pivotItem);
}
else
{
parentPivot.Items.Add(pivotItem);
}
}
}
}
#endregion
}
You can remove the pivot item from the parent pivot control
parentPivotControl.Items.Remove(pivotItemToBeRemoved);
Removing PivotItems is easy, but if you want to put them back afterwards I've found that the headers get messed up and start overlapping each other. This also happens if you set the Visibility of a header to Collapsed and then later make it Visible again.
So I solved my particular problem by setting the opacity of each unwanted PivotItem (and its header) to 0.
PivotItem p = (PivotItem)MainPivot.Items.ToList()[indexToHide];
p.Opacity = 0;
((UIElement)p.Header).Opacity = 0;
However, this leaves gaps where the missing PivotItems are.
For me, the gaps were not a problem because I only want to remove items at the end of my PivotItemList, so I get some whitespace between the last and first PivotItems. The problem was, I was still able to swipe to a hidden PivotItem. In order to fix this, I overrode Pivot.SelectionChanged() so that whenever the user swipes to a hidden PivotItem, the code moves on to the next item instead. I had to use a DispatchTimer from within SelectionChanged() and actually move to the next PivotItem from the DispatchTimer callback, since you have to be in the UI thread to change PivotItem.SelectedIndex.
private void MainPivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
t.Stop(); //t is my DispatchTimer, set to 100ms
if (MainPivot.SelectedIndex >= mFirstHiddenPivotItemIndex)
{
//move to the first or last PivotItem, depending on the current index
if (mCurrentSelectedPivotItemIndex == 0)
mPivotItemToMoveTo = mFirstHiddenPivotItemIndex - 1;
else
mPivotItemToMoveTo = 0;
t.Start();
}
mCurrentSelectedPivotItemIndex = MainPivot.SelectedIndex;
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
MainPivot.SelectedIndex = mPivotItemToMoveTo;
t.Stop();
}
foreach (PivotItem item in MyPivot.Items.ToList())
{
if (item.Visibility == Visibility.Collapsed)
MyPivot.Items.Remove(item);
}
Setting IsLocked property to true will make all other Pivot items to disappear except the current pivot item.
But this will not hide one particular pivot item of our choice.
To elaborate on the solution of adding/removing pivotItems, rather than hiding them.
Let's say we want the pivotItem to be initially invisible, and appear only on a certain event.
mainPivot.Items.Remove(someTab);
Then to add it again,
if (!mainPivot.Items.Cast<PivotItem>().Any(p => p.Name == "someTab"))
{
mainPivot.Items.Insert(1,someTab);
}
I've used Insert rather than add to control the position where the tab appears.
You have to ensure you don't add the same tab twice, which is the reason for the if statement.
I've modified the Bajena behavior to improve it, solving the issue with losing the original position of the PivotItem when showing/hiding repeteadly and the issue when parentpivot control is null (not initialized yet).
Notice that this behavior must be attached to the Pivot, not to the PivotItem.
Code:
public class PivotItemHideableBehavior : Behavior<Pivot>
{
private Dictionary<PivotItem, int> DictionaryIndexes { get; set; }
public static readonly DependencyProperty VisibleProperty = DependencyProperty.Register(
"Visible",
typeof(bool),
typeof(PivotItemHideableBehavior),
new PropertyMetadata(true, VisiblePropertyChanged));
public static readonly DependencyProperty PivotItemProperty = DependencyProperty.Register(
"PivotItem",
typeof(PivotItem),
typeof(PivotItemHideableBehavior),
new PropertyMetadata(null));
public bool Visible
{
get { return (bool)GetValue(VisibleProperty); }
set { SetValue(VisibleProperty, value); }
}
public PivotItem PivotItem
{
get { return (PivotItem)GetValue(PivotItemProperty); }
set { SetValue(PivotItemProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += AssociatedObject_Loaded;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Loaded -= AssociatedObject_Loaded;
}
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
DictionaryIndexes = new Dictionary<PivotItem, int>();
int index = 0;
foreach (PivotItem item in AssociatedObject.Items)
DictionaryIndexes.Add(item, index++);
}
private static void VisiblePropertyChanged(DependencyObject dpObj, DependencyPropertyChangedEventArgs change)
{
var behavior = (PivotItemHideableBehavior)dpObj;
var pivot = behavior.AssociatedObject;
if (!behavior.Visible)
{
if (pivot.Items.Contains(behavior.PivotItem))
pivot.Items.Remove(behavior.PivotItem);
}
else if (!pivot.Items.Contains(behavior.PivotItem))
{
int index = 0;
foreach (var item in behavior.DictionaryIndexes)
{
if (item.Key == behavior.PivotItem)
pivot.Items.Insert(index, behavior.PivotItem);
else if (pivot.Items.Contains(item.Key))
index++;
}
}
}
}
Usage:
<Interactivity:Interaction.Behaviors>
<Behaviors:PivotItemHideableBehavior PivotItem="{x:Bind PivotItemName}" Visible="{Binding IsPivotItemVisible}" />
</Interactivity:Interaction.Behaviors>