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

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.

Related

How to update the map properly using canvas and a button when the players relocate to a new location?

My game snapshot Attachment> Blockquote
My canvas constructs
public class DrawingView4 extends View{
DrawingView4(Context context4)
{
super(context4);
}
#Override protected void onDraw(Canvas canvas4)
{
int tile4=0;
if (DoneDrawFacilities && doneloading) {
}
else {
for (int i4=0; i4< 17 && notfixingorientation; i4++){
if (i4< 16) {
for (int ii4=0; ii4 < 16; ii4++){
try {
//Checking the data of all spots of the game map from package directory.
//Then Draw in canvas if the data of the spot occupied by type of human and core facilities,
FacilityList = new Gson().fromJson(FileUtil.readFile(FileUtil.getPackageDataDir(getApplicationContext()).concat("/GameResource/Tile".concat(String.valueOf((long)(tile4 + 1)).concat(".data")))), new TypeToken<ArrayList<HashMap<String, Object>>>(){}.getType());
if (FacilityList.get((int)0).get("Type").toString().equals("Human")) {
canvas4.drawBitmap(BitmapFactory.decodeFile(AllObjects.getString(String.valueOf((long)(tile4)), "")),null,new Rect(ii4*120, i4*120, 120*(ii4+1),120*(i4+1)), null);
}
if (FacilityList.get((int)0).get("Type").toString().equals("Core")) {
if (FacilityList.get((int)0).get("Name").toString().equals("Arena")) {
canvas4.drawBitmap(BitmapFactory.decodeFile(AllObjects.getString(String.valueOf((long)(tile4)), "")),null,new Rect(7*120, 7*120, 120*(8+1),120*(8+1)), null);
}
else {
}
}
} catch (Exception e) {
}
FacilityList.clear();
tile4++;
}
}
else {
DoneDrawFacilities = true;
}
}
}
}}
Blockquote
My Relocate button
//I use sharepreference called AllObjects rather than a list and I Only update the path of objects in specific tile in sharedpreference such as in variable 1, 2, etc. and then re-draw using this code below, next time I update the objects path and get this object path from the same json file in the package directory. Too decode to Bitmap and then Draw in canvas.
//Some other code are removed that just updating some data in specific file directory.
AA_structures_facilities.removeAllViews();
AA_structures_facilities.addView(new DrawingView4(GameActivity.this));
// But It freezes the screen or stop me from touching the touch event in a second everytime I update new canvas.
//WHILE MY touchevent is hundled in the parent LinearLayout where the canvas is placed.

PagerAdapter always getting called two times in ViewPager

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);

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

RecyclerView.Adapter get ItemView through position

In my Adapter, I call LayoutManager.ChildAt(position) to get the itemview, but the view I get is not the matched itemview, and when i call notifyItemChanged(position), app crashes:
E/AndroidRuntime: FATAL EXCEPTION: mainProcess: com.paicaifu.riches, PID: 8502
Java.lang.IllegalArgumentException: Called attach on a child which is not detached: ViewHolder{adb21588 position=0 id=-1, oldPos=-1, pLpos:-1}
at Android.support.v7.widget.RecyclerView$5.attachViewToParent(RecyclerView.java:654)
at android.support.v7.widget.ChildHelper.attachViewToParent(ChildHelper.java:239)
at android.support.v7.widget.RecyclerView.addAnimatingView(RecyclerView.java:1107)
at android.support.v7.widget.RecyclerView.animateChange(RecyclerView.java:3270)
at android.support.v7.widget.RecyclerView.dispatchLayoutStep3(RecyclerView.java:3088)
at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2917)
at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3283)
…
at android.view.Choreographer.doCallbacks(Choreographer.java:574)
Thanks,this is my code in the adapter:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder,
final int position) {
if (holder instanceof CardHolder) {
final CardHolder cardHolder = (CardHolder) holder;
cardHolder.rootview.setOnClickListener(this);
cardHolder.rootview.setTag(position);
cardHolder.rootview.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//get last itemview in adapter,but preView isnot match the
//position,it is the visibable views on screen
View preView = mLinearLayoutManager.getChildAt(preIndex);
//change the datasource
mData.getResult().getCardList().get(preIndex).setOpen(false);
//update item
notifyItemChanged(preIndex);//when the item is out of screen,
//this line will cause crashes
preIndex = position;
}
});
}
I just want find the last item and change it's state(open or close),when the item is out of screen,notifyItemChanged(int position) will appear problem.
This is probably a bit too late, but the crash "Called attach on a child which is not detached" typically happens when you do NOT create a new view in onCreateViewHolder and instead return a cached view that's already inflated and attached.

What the right time for registering listener for Share/Search charms

I need to register different share charm listener for every page. I have 2 pages. I added following code in every one:
DataTransferManager.GetForCurrentView().DataRequested += App_DataRequested;
I added it in constructor of one page and in UserControl_Loaded event of another (first page just doesn't have UserControl_Loaded so why I added it directly to constructor). At the moment when second page tryting to load, I got exception:
WinRT information: An event handler has already been registered
Additional information: A method was called at an unexpected time.
Where should I place it and what is "right" time to do this??
Also it looks confusing that we have different DataTransferManager for every view, but only one is active at current time. Ever more, I noticed, if you add only one listener for first page, other pages will share this listener anyway. If I have only one shared listener for all pages, is it correct register it in app.xaml.cs?
The way I resolved this issue was to deregister the event in the onNavigatedfrom event as below:
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
DataTransferManager.GetForCurrentView().DataRequested -= App_DataRequested;
base.OnNavigatedFrom(e);
}
In BasePage.cs in constructor I added
public BasePage()
{
if (!_isListenToDataRequested)
{
_isListenToDataRequested = true;
DataTransferManager manager = DataTransferManager.GetForCurrentView();
manager.DataRequested += AppDataRequested;
}
}
private async void AppDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
IShareable shareable = Frame.Content as IShareable;
if (shareable != null)
{
DataRequestDeferral deferral = args.Request.GetDeferral();
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => shareable.AppDataRequested(sender, args));
deferral.Complete();
}
}
And all my pages look like
public sealed partial class ContentPage : IShareable
{
public void AppDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{...}
}
Another solution was run this as below
private DataTransferManager dataTransferManager;
Put this in page loaded event
this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, new DispatchedHandler(() =>
{
this.dataTransferManager = DataTransferManager.GetForCurrentView();
this.dataTransferManager.DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(this.OnDataRequested);
}));
And
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
// Unregister the current page as a share source.
this.dataTransferManager.DataRequested -=
new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>
(this.OnDataRequested);
}
I'd suggest doing it in the navigating events, the OnNavigatingFrom event will be triggered before the OnNavigatingTo of the page you're going to so you won't have this problem.
protected override Task OnNavigatingTo(WinRTXamlToolkit.Controls.AlternativeNavigationEventArgs e)
{
DataTransferManager.GetForCurrentView().DataRequested += dataTransfer_DataRequested;
return base.OnNavigatingTo(e);
}
protected override Task OnNavigatingFrom(WinRTXamlToolkit.Controls.AlternativeNavigatingCancelEventArgs e)
{
DataTransferManager.GetForCurrentView().DataRequested -= dataTransfer_DataRequested;
return base.OnNavigatingFrom(e);
}
//Note: This is the WinRT Xaml Toolkit version of the events, but the standard events will work the same way.