PagerAdapter always getting called two times in ViewPager - arraylist

I am trying to make a slider between TouchImageView and PlayerView (Exoplayer) but I am unable to catch up with certain issues that are persisting even after several changes. All the suggestions and answers are welcome. Pardon my questioning skills and please let me know if more inputs are needed for your analysis. Kindly also let me know if there is any other alternative to successfully meet my expectations of properly implementing views smoothly in ViewPager.
Problem description:-
Issues related to click on view :-
When the image is clicked, the audio of next video (if any) starts playing in background.
The same issue is with PlayerView. When the video thumbnail is clicked, the audio of clicked video as well as next video plays together.
Issues related to slider :-
When an we slide and reach to an image preceding to a video, the audio starts playing in background. However, after sliding once toward video and sliding again in forward or backward direction from video for once, the audio stops. But this issue persists after viewing more than one images in forward or backward direction of video.
Attempts made by me to solve this issue :-
I tried to use playerView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {...}) method in PagerAdapter to handle player states while sliding between views. Unfortunately, I was unable to grasp to use different player states.
I also tried to use viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {...} method in StatusViewer class.
StatusViewer Java class (Setting PagerAdapter class object inViewPager) :-
modelFeedArrayList = (ArrayList<File>) getIntent().getSerializableExtra("modelFeedArrayList");
position = intent.getIntExtra("position", 0);
ImageSlideAdapter imageSlideAdapter = new ImageSlideAdapter(this,modelFeedArrayList,position);
viewPager.setAdapter(imageSlideAdapter);
viewPager.setCurrentItem(position);
viewPager.setOffscreenPageLimit(0);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
File currentFile = modelFeedArrayList.get(position);
String filePath = currentFile.toString();
if (filePath.endsWith(".jpg") || currentPage == position){
currentPage = position;
ImageSlideAdapter.player.pause();
}
else {
currentPage = position;
ImageSlideAdapter.player.play();
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
ImageSliderAdapter (PagerAdapter) (code mentioned below is inside instantiateItem):-
File currentFile = modelFeedArrayList.get(position);
String filePath = currentFile.toString();
if (currentFile.getAbsolutePath().endsWith(".mp4")) {
statusImageView.setVisibility(View.GONE);
playerView.setVisibility(View.VISIBLE);
player = new ExoPlayer.Builder(context).build();
MediaItem mediaItem = MediaItem.fromUri(filePath);
player.addMediaItem(mediaItem);
playerView.setPlayer(player);
player.prepare();
playerView.setBackgroundColor(context.getResources().getColor(android.R.color.black));
playerView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
#Override
public void onViewAttachedToWindow(View v) {
Log.d("Filepath", filePath);
Log.d("Position", "" + position);
}
#Override
public void onViewDetachedFromWindow(View v) {
if (filePath.endsWith(".jpg") || currentPage == position || modelFeedArrayList.get(currentPage).getAbsolutePath().endsWith(".jpg")){
currentPage = position;
player.pause();
Objects.requireNonNull(playerView.getPlayer()).pause();
}
else {
player.release();
Objects.requireNonNull(playerView.getPlayer()).release();
}
}
});
} else {
playerView.setVisibility(View.GONE);
statusImageView.setVisibility(View.VISIBLE);
Glide.with(context).load(modelFeedArrayList.get(position)).into(statusImageView);
statusImageView.setBackgroundColor(context.getResources().getColor(android.R.color.black));
}
Objects.requireNonNull(container).addView(itemView);
return itemView;
}
#Override
public void destroyItem(#NonNull #NotNull ViewGroup container, int position, #NonNull #NotNull Object object) {
container.removeView((ConstraintLayout) object);
}

Thank you StackOverflow community for viewing this question. I resolved the above issue by below mentioned modifications :-
Changes in ImageSliderAdapter (PagerAdapter) :-
-> Below mentioned code was added in onViewAttachedToWindow(View v) :-
if (filePath.endsWith(".jpg") || currentPage == position || modelFeedArrayList.get(currentPage).getAbsolutePath().endsWith(".jpg")){
currentPage = position;
player.pause();
Objects.requireNonNull(playerView.getPlayer()).pause();
}
else {
player.pause();
Objects.requireNonNull(playerView.getPlayer()).pause();
if (filePath.endsWith(".mp4")){
player.pause();
Objects.requireNonNull(playerView.getPlayer()).pause();
}
else {
player.play();
Objects.requireNonNull(playerView.getPlayer()).play();
}
}
-> Below mentioned code was added in onViewDetachedFromWindow(View v) :-
if (filePath.endsWith(".mp4")){
player.release();
Objects.requireNonNull(playerView.getPlayer()).release();
}
-> player.play() was added after player.prepare().
Changes in StatusViewer Java class :-
-> The below changes cured the issue of player malfunctioning and player's play state and release state. I used the smoothScroll: false in setCurrentItem.
viewPager.setCurrentItem(position,false);

Related

Xamarin.Android how to remember the position of items in a recyclerview

I have a recyclerview set up in xamarin.android as per the code in this link
https://www.appliedcodelog.com/2019/08/reorder-list-items-by-drag-and-drop-in.html
My question is, how can I remember the position of these items when the app is restarted etc. When the user adds items they are inserted at adapter position 0,1,2,3 etc but when they close the app and come back in, it is not always in the same order.
The user can also rearrange by drag and drop so this seems to add even more confusion!
Currently I have the items in the recyclerview being saved by converting the list to Json and loading when the app opens again but as I said, the items aren't always in the same order as before the app was closed.
Can anyone advise the best way to do this? I have tried to add the item name and position number to a list converting to json then trying to insert the item at the saved position index but can't get it to work..
Thanks
Do you want to achieve the result like following GIF?
You can use PreferenceManager to store position of items(Before store data, I will Serialize data) in a recyclerview.
You can override OnPause() method, this method will be executed when application is background or app is killed. So we can store the position and data in this method.Here is code about ReOrderActivity
[Activity(Label = "ReOrderList")]
public class ReOrderActivity : Activity, IOnStartDragListener
{
private ItemTouchHelper _mItemTouchHelper;
public static ObservableCollection<string> ResourceList;
private RecyclerView _resourceReorderRecyclerView;
ReOrderAdapters resourceAdapter;
ISharedPreferences prefs;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.ReOrderLayout);
prefs = PreferenceManager.GetDefaultSharedPreferences(this);
GetCollection();
resourceAdapter = new ReOrderAdapters(ResourceList, this);
// Initialize the recycler view.
_resourceReorderRecyclerView = FindViewById<RecyclerView>(Resource.Id.ResourceReorderRecyclerView);
Button mDone = FindViewById<Button>(Resource.Id.mDone);
mDone.Click += MDone_Click;
_resourceReorderRecyclerView.SetLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.Vertical, false));
_resourceReorderRecyclerView.SetAdapter(resourceAdapter);
_resourceReorderRecyclerView.HasFixedSize = true;
ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(resourceAdapter);
_mItemTouchHelper = new ItemTouchHelper(callback);
_mItemTouchHelper.AttachToRecyclerView(_resourceReorderRecyclerView);
}
protected override void OnPause()
{
base.OnPause();
string ConvertData = JsonConvert.SerializeObject(ResourceList);
ISharedPreferencesEditor editor = prefs.Edit();
editor.PutString("ObservableCollection_ConvertData", ConvertData);
// editor.Commit(); // applies changes synchronously on older APIs
editor.Apply(); // applies changes asynchronously on newer APIs
}
private void MDone_Click(object sender, System.EventArgs e)
{
resourceAdapter.AddItem("Add item");
}
public void OnStartDrag(RecyclerView.ViewHolder viewHolder)
{
_mItemTouchHelper.StartDrag(viewHolder);
}
//Added sample data record here
public void GetCollection()
{
//ISharedPreferencesEditor editor = prefs.Edit();
//editor.PutString("ObservableCollection_ConvertData", "");
//editor.Apply();
string ConvertData = prefs.GetString("ObservableCollection_ConvertData","");
if(string.IsNullOrEmpty(ConvertData))
{
ResourceList = new ObservableCollection<string>();
ResourceList.Add("OnPause()");
ResourceList.Add("OnStart()");
ResourceList.Add("OnCreate()");
}
else
{
ResourceList= JsonConvert.DeserializeObject<ObservableCollection<string>>(ConvertData);
}
//var or= ResourceList.ToString();
}
}
}
You can download my demo
https://drive.google.com/file/d/1mQTKf3rlcIVnf2N97amrqtnrSCRk-8ZW/view?usp=sharing

How can I load - inflate items to a Recycler view without locking the UI or showing a load icon?

I just want to be able to display a list of contacts (without even communicating to a server) just the way it is displayed on the "Contacts" native app on the phone.
I have like 1500 contacts and when I try to load the recycler view, all items at once, it lags a lot 2 - 3 seconds.
I've achieved loading more items but with a loading bar and thats not what I want.
I've already tried Threads, Executors, postOnUIThread(), handler.post() and even AsyncTask -> Override -> doOnBackground. Nothing works.
private class CustomTask extends AsyncTask<Void, Void, Void> {
int inserted;
#Override
protected Void doInBackground(Void... param) {
//Do some work
try {
lcf.getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
((BaseActivity) lcf.getActivity()).showProgressDialog();
}
});
int currentSize = contactsLoaded.size();
for (inserted = 0; inserted < lcf.getController().getContacts().size() && contactsLoaded.size() < lcf.getController().getContacts().size(); inserted++) {
contactsLoaded.add(lcf.getController().getContacts().get(currentSize + inserted));
notifyItemRangeInserted(contactsLoaded.size() - 1, inserted);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void param) {
//Print Toast or open dialog
//notifyItemRangeInserted(contactsLoaded.size() - 1, 0);
if(!lcf.getController().isSelectedAffiliated()){
lcf.disclaimerLayout.setVisibility(View.VISIBLE);
}else{
lcf.disclaimerLayout.setVisibility(View.GONE);
}
lcf.isLoading=false;
((BaseActivity) lcf.getActivity()).hideProgressDialog();
}
}
That code lives within my adapter, "lcf" is a reference to the fragment. If I use the already loaded list saved on the controller (that I get from the fragment reference) and then just call notifyDataSetChanged() it LAGS like hell. So with this CustomTask I tried to load every item and notify it one by one to a Background task hoping it would make the items pop up quickly and sequentially without interfereing with the UI thread to not freeze the screen. It doesn't work. I am out of options now. I've tried everything.

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 to set xaml mapcontrol mapicon always visible

I'm fairly new to programming in XAML and I'm making a test application on windows phone 8.1 emulator with a MapControl.
I wanted to add a MapIcon to my map but the icon doesn't appear when the map is zoomed out. I've searched the internet and couldn't find anything regarding my problem.
I want my zoomlevel 12 and show the mapicon on that zoomlevel.
namespace TEST.APPLICATION
{
public partial class MapView : Page
{
Geolocator geo = null;
public MapView()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
HardwareButtons.BackPressed += HardwareButtons_BackPressed;
}
void HardwareButtons_BackPressed(object sender, BackPressedEventArgs e)
{
if (Frame.CanGoBack)
{
e.Handled = true;
Frame.GoBack();
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
map.Center = new Geopoint(new BasicGeoposition()
{
Latitude = 51.5856935784736,
Longitude = 4.79448171225132
});
map.ZoomLevel = 12;
displaySightings();
}
private void displaySightings()
{
MapIcon sighting1 = new MapIcon();
sighting1.Location = new Geopoint(new BasicGeoposition()
{
Latitude = 51.5940,
Longitude = 4.7795
});
//sighting1.NormalizedAnchorPoint = new Point(0.5, 1.0);
sighting1.Title = "VVV";
map.MapElements.Add(sighting1);
}
}
Is there any way to make the MapIcon always visible?
The MapIcon is not guaranteed to be shown. It may be hidden when it obscures other elements or labels on the map.
For some stupid reason, Microsoft thought that labels and other map elements should outrank map icons when rendering the display. So, if you're making an app displaying the locations of all the nearby Starbucks, the name of the high school across the street from the Starbucks is more important than the pushpin, according to them.
You'll need to render the pushpins using XAML instead.

AutoCompleteTextView OnItemClickListener null param (landscape mode on HTC Desire S)

My Problem : I have an AutoCompleteTextView with an OnItemClickListener. This has been working fine for 18 months, but I have now noticed it throws a NullPointerException when I select an item in landscape mode on my HTC Desire S. (There is no error in portrait mode or on any other phone or emulator I've tested it on).
The AdapterView<?> av parameter is coming through as null. Why would this be, and how can I get around it?
Code :
myAutoCompleteTextView = (AutoCompleteTextView) findViewById(R.id.myAutoCompleteTextView);
myAutoCompleteTextView.setSingleLine();
myAutoCompleteTextView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> av, View v, int index, long arg) {
String selectedItem = (String)av.getItemAtPosition(index);
//Do stuff with selected item ...
}
}
Error :
java.lang.NullPointerException
at uk.co.myCompany.mobile.android.myCompanymobile.pages.groups.AbstractGroupSelectionPage$3.onItemClick(AbstractGroupSelectionPage.java:228)
at android.widget.AutoCompleteTextView.onCommitCompletion(AutoCompleteTextView.java:993)
at com.android.internal.widget.EditableInputConnection.commitCompletion(EditableInputConnection.java:76)
at com.android.internal.view.IInputConnectionWrapper.executeMessage(IInputConnectionWrapper.java:368)
at com.android.internal.view.IInputConnectionWrapper$MyHandler.handleMessage(IInputConnectionWrapper.java:86)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:150)
at android.app.ActivityThread.main(ActivityThread.java:4385)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:849)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:607)
at dalvik.system.NativeStart.main(Native Method)
Extra Code - my custom adapter inner class :
/**
* An inner class to simply make a custom adapter in which we can alter the on-screen look of selected groups.
*/
private class SelectedGroupAdapter extends ArrayAdapter<Group> {
private ArrayList<Group> items;
private int layout;
public SelectedGroupAdapter(Context context, int layout, ArrayList<Group> items) {
super(context, layout, items);
this.items = items;
this.layout = layout;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(layout, null);
}
Group o = items.get(position);
//Display the group name and number of contacts
if (o != null) {
TextView groupName = (TextView) v.findViewById(R.id.groupName);
TextView noOfContacts = (TextView) v.findViewById(R.id.noOfContacts);
if (groupName != null) {
groupName.setText(o.getGroupName());
}
if(noOfContacts != null) {
if (o.isDynamic())
noOfContacts.setText(getString(R.string.dynamic));
else {
int contactsCount = o.getGroupSize();
if(contactsCount == 1) noOfContacts.setText(contactsCount + " " + getString(R.string.contact));
else noOfContacts.setText(contactsCount + " " + getString(R.string.contacts));
}
}
}
return v;
}
}
My hunch is that since you are declaring android:configChanges="orientation" in your manifest, then when you rotate the old OnItemClickListener is still sticking around, and since you technically have a new layout, the AdapterView that was being used prior to orientation change doesn't exist anymore, thus is null when you click on an item.
There's 2 things I think that would solve this if this is the case:
Remove the orientation option in your manifest. Any events you place in configChanges tells Android "I'm taking care of this configuration change, so let me handle it" as opposed to letting Android handle it. The normal operation for Android in the event of an orientation change is to destroy and recreate your Activity (it will take care of repopulating some Views with data automatically).
If you decide you need to handle orientation changes, then override onConfigurationChanged() and set the OnItemClickListener to the new AdapterView object (ListView, GridView, whichever you are using) that should have been recreated in the onCreate method.