I am using Firebase for my apps back end and I am retrieving my data as excepted. After I retrieve my data, I am posting it by using otto bus and the code can be seen below.
#Subscribe
public void loadBrothers(ServiceCalls.SearchBrothersRequest request) {
final ServiceCalls.SearchBrothersResponse response = new ServiceCalls.SearchBrothersResponse();
response.Brothers = new ArrayList<>();
Firebase reference = new Firebase("my data's url here");
reference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
int index = 0;
for (DataSnapshot brotherSnapchat : dataSnapshot.getChildren()) {
BrotherFireBase bro = brotherSnapchat.getValue(BrotherFireBase.class);
Log.i(LOG_TAG, bro.getName());
Log.i(LOG_TAG, bro.getWhy());
Log.i(LOG_TAG, bro.getPicture());
Log.i(LOG_TAG, bro.getMajor());
Log.i(LOG_TAG, bro.getCross());
Log.i(LOG_TAG, bro.getFact());
Brother brother = new Brother(
index,
bro.getName(),
bro.getWhy(),
bro.getPicture(),
bro.getMajor(),
bro.getCross(),
bro.getFact());
response.Brothers.add(brother);
index++;
}
bus.post(response);
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
Once the data is in my RecyclerView, I am to click an item and it's respective activity is to pop up in a custom activity dialog. However, since the activity is a dialog, you can see the RecyclerView reloading in the background. This does not happen when I do not retrieve the data from the internet. After a few clicks around, the app crashes due to an out of memory exception. Is there something I am missing?
Here is the activity where the recyclerView is found:
#Override
public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_meet_a_brother, container, false);
adapter = new BrotherRecycleAdapter((BaseActivity) getActivity(),this);
brothers = adapter.getBrothers();
recyclerView =(RecyclerView) view.findViewById(R.id.fragment_meet_a_brother_recycleView);
recyclerView.setLayoutManager(new GridLayoutManager(getActivity(),3));
setUpAdapter();
bus.post(new ServiceCalls.SearchBrothersRequest("Hello"));
return view;
}
private void setUpAdapter(){
if(isAdded()){
recyclerView.setAdapter(adapter);
}
}
#Subscribe
public void onBrosLoaded(final ServiceCalls.SearchBrothersResponse response){
int oldBrotherLength = brothers.size();
brothers.clear();
adapter.notifyItemRangeRemoved(0, oldBrotherLength);
brothers.addAll(response.Brothers);
//Delete for Debug method...
adapter.notifyItemRangeChanged(0,brothers.size());
Log.i(LOG_TAG, Integer.toString(brothers.size()));
}
#Override
public void onBrotherClicked(Brother brother) {
Intent intent = BrotherPagerActivity.newIntent(getActivity(),brother);
Log.i(LOG_TAG,brother.getBrotherName() + " was Clicked");
startActivity(intent);
}
Just in case, here is also the activity that is started when a list item is clicked, it is a viewPager activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_brother_pager);
brothers = new ArrayList<>();
bus.post(new ServiceCalls.SearchBrothersRequest("Hello"));
FragmentManager fragmentManager = getSupportFragmentManager();
viewPager = (ViewPager) findViewById(R.id.activity_brother_viewPager);
viewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) {
#Override
public Fragment getItem(int position) {
Brother brother = brothers.get(position);
return BrotherDetailsFragment.newInstance(brother);
}
#Override
public int getCount() {
return brothers.size();
}
});
}
#Subscribe
public void onBrosLoad(final ServiceCalls.SearchBrothersResponse response){
brothers.clear();
brothers.addAll(response.Brothers);
viewPager.getAdapter().notifyDataSetChanged();
Brother brother = getIntent().getParcelableExtra(BROTHER_EXTRA_INFO);
int brotherId = brother.getBrotherId();
for(int i=0;i<brothers.size();i++){
if(brothers.get(i).getBrotherId() == brotherId){
viewPager.setCurrentItem(i);
break;
}
}
Log.i(LOG_TAG, Integer.toString(brothers.size()));
}
public static Intent newIntent(Context context, Brother brother){
Intent intent = new Intent(context,BrotherPagerActivity.class);
intent.putExtra(BROTHER_EXTRA_INFO,brother);
return intent;
}
Any help is greatly appreciated thank you!
in public void onBrotherClicked(Brother brother) where RecyclerView resides, you call:
Intent intent = BrotherPagerActivity.newIntent(getActivity(),brother);
which will call
Intent intent = new Intent(context,BrotherPagerActivity.class);`
in newIntent of your viewPager activity.
This could be a recursive call.
try adding:
public static Intent newIntent(Context context, Brother brother){
Intent intent = new Intent(context,BrotherPagerActivity.class);
intent.putExtra(BROTHER_EXTRA_INFO,brother);
return intent;
}
in Activity where your RecyclerView resides. And call your ViewPage activity there.
-- UPDATE --
Call your viewpager activity (which is used to show Brother data) with the following code:
private void showBrotherData(Brother brother){
Intent intent = new Intent(this, BrotherPagerActivity.class);
intent.putExtra(BROTHER_EXTRA_INFO, brother);
this.startActivity(intent);
}
I found the answer! I changed my recyclerView to only updated if the size of the array was zero.
#Subscribe
public void onBrosLoaded(final ServiceCalls.SearchBrothersResponse response){
int oldBrotherLength = brothers.size();
Log.i(LOG_TAG, "Brother lists old size" + Integer.toString(oldBrotherLength));
if(oldBrotherLength ==0){
brothers.clear();
adapter.notifyItemRangeRemoved(0, oldBrotherLength);
brothers.addAll(response.Brothers);
//Delete for Debug method...
adapter.notifyItemRangeChanged(0,brothers.size());
} else{
return;
}
Log.i(LOG_TAG, Integer.toString(brothers.size()));
}
I don't know how good this solution is in terms of cleaness but it works for me. I hope this helps someone.
Related
I have a FloatingActionButton and RecyclerView in one of my fragments. Fab opens a new activity where user can save a task into sqlite and all the saved tasks from sqlite are shown in the recycler view. Now what I want is that when the user saves a new task and click on the back button of the activity from toolbar, the recycler view should be updated automatically. Right now, I have to switch to another fragment and then come back to the previous one to see the newly created task. I researched about it and found that interfaces are the best option for this but I am having problems passing the context of the fragment to the activity.
Here is the activity for new task creation:
public class AddTaskActivity extends AppCompatActivity {
DataUpdateListener dataUpdateListener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_task);
dataUpdateListener = (CalendarFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_calendar);
ActionBar supportActionBar = getSupportActionBar();
if (supportActionBar != null) {
supportActionBar.setTitle(R.string.add_task);
supportActionBar.setDisplayHomeAsUpEnabled(true);
}
}
private void saveTask(String task_type, String task) {
// this method is used to save the task in sqlite
byte[] imageByteArray;
if (addPictureBtn.getVisibility() == View.GONE) {
imageByteArray = Utils.getImageByteArray(selectedImage);
if (Utils.saveTask(task_type, imageByteArray, task, 0) != -1) {
AlertDialog alertDialog = Utils.showProgressDialog(this, R.layout.success_popup);
Button okBtn = (Button) alertDialog.findViewById(R.id.okBtn);
okBtn.setOnClickListener(v -> {
alertDialog.dismiss();
finish();
});
}
dataUpdateListener.onDataUpdate();
}
}
public interface DataUpdateListener {
void onDataUpdate();
}
}
This is my fragment which is implementing the interface:
public class CalendarFragment extends Fragment implements AddTaskActivity.DataUpdateListener {
CalendarView calendarView;
TextView noTaskFoundTV;
RecyclerView recyclerView;
FloatingActionButton addTaskBtn;
private FragmentCalendarBinding binding;
CalendarTasksAdapter calendarTasksAdapter;
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentCalendarBinding.inflate(inflater, container, false);
return binding.getRoot();
}
#Override
public void onViewCreated(#NonNull #NotNull View view, #Nullable #org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
calendarView = view.findViewById(R.id.calendar);
Calendar calendar = Calendar.getInstance();
long milliTime = calendar.getTimeInMillis();
calendarView.setDate(milliTime, true, true);
recyclerView = view.findViewById(R.id.rv);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setHasFixedSize(false);
noTaskFoundTV = view.findViewById(R.id.noTaskFound);
addTaskBtn = view.findViewById(R.id.fab);
addTaskBtn.setOnClickListener(v -> {
Intent intent = new Intent(getContext(), AddTaskActivity.class);
startActivity(intent);
});
fetchTodayPendingTasks();
}
public void fetchTodayPendingTasks() {
JSONObject todayTasksFromDB = Utils.getTodayPendingTasksFromDB();
if (todayTasksFromDB != null) {
noTaskFoundTV.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
try {
JSONArray tasks = todayTasksFromDB.getJSONArray("tasks");
calendarTasksAdapter = new CalendarTasksAdapter(getActivity(), tasks);
recyclerView.setAdapter(calendarTasksAdapter);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
#Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
#Override
public void onDataUpdate() {
//this toast never triggers/shown when the task is created from the activity
Toast.makeText(getContext(), "Triggered", Toast.LENGTH_SHORT).show();
}
}
For this kind of usage, the best practice is to use Room database which is basically wrapping sqlite with abstraction layer. And then you could use LiveData.
Perfect example with source code can be found here.
Please try to open activity through startActivityResult like
In fragment
Intent intent = new Intent(getContext(), AddTaskActivity.class); startActivityForResult(intent,requestcode);
In addtaskactivity
Intent inten =new Intent()
setResult with OK
and then again check onActivityResult in fragment with request code, you can refresh you view here
Or another way to check and refresh in onStart() method of fragment with one static Boolean variable updated from task activity and again false this Boolean from onstart when you finish refreshing. But first of all I would prefer first way.
You should use onResumed method of fragment lifecycle.
you should override onResumed Method on CalendarFragment
This method is called after returning to the main page.
call fetchTodayPendingTasks(); in onResumed method.
It is better to make changes in the fetchTodayPendingTasks. like this:
public class CalendarFragment extends Fragment implements AddTaskActivity.DataUpdateListener {
CalendarView calendarView;
TextView noTaskFoundTV;
RecyclerView recyclerView;
FloatingActionButton addTaskBtn;
private FragmentCalendarBinding binding;
CalendarTasksAdapter calendarTasksAdapter;
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentCalendarBinding.inflate(inflater, container, false);
return binding.getRoot();
}
#Override
public void onViewCreated(#NonNull #NotNull View view, #Nullable #org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
calendarView = view.findViewById(R.id.calendar);
Calendar calendar = Calendar.getInstance();
long milliTime = calendar.getTimeInMillis();
calendarView.setDate(milliTime, true, true);
recyclerView = view.findViewById(R.id.rv);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setHasFixedSize(false);
noTaskFoundTV = view.findViewById(R.id.noTaskFound);
addTaskBtn = view.findViewById(R.id.fab);
addTaskBtn.setOnClickListener(v -> {
Intent intent = new Intent(getContext(), AddTaskActivity.class);
startActivity(intent);
});
calendarTasksAdapter = new CalendarTasksAdapter(getActivity());
recyclerView.setAdapter(calendarTasksAdapter);
fetchTodayPendingTasks();
}
public void fetchTodayPendingTasks() {
JSONObject todayTasksFromDB = Utils.getTodayPendingTasksFromDB();
if (todayTasksFromDB != null) {
noTaskFoundTV.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
try {
JSONArray tasks = todayTasksFromDB.getJSONArray("tasks");
adapter.setData(tasks)
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
#Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
#Override
public void onDataUpdate() {
//this toast never triggers/shown when the task is created from the activity
Toast.makeText(getContext(), "Triggered", Toast.LENGTH_SHORT).show();
}
}
and you should define a setData method in your adapter. Do not forget to call notifyDataSetChanged().
public void setData(JSONArray array){
// set to your data list
notifyDataSetChanged();
}
I have multiple instances of the same CustomView inside one fragment.
I implemented savedInstance for this CustomView but the problem is since there are multiple instances of this CustomView, savedInstance of the last one, overrides them all.
for example, if there are 3 instances of this CustomView which has a recyclerview inside, If I scroll the last one, it applies to them all. because i'm using key value pairs and the key is the same for all of them. (I can change the key to differ for each one but I think there is a better way)
Here is the code for savedInstance saving and restoring inside my CustomView:
#Nullable
#Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable(SavedInstanceKey.SUPERSTATE.name(), super.onSaveInstanceState());
bundle.putParcelable(SavedInstanceKey.RECYCLERVIEW.name(), recyclerView.getLayoutManager().onSaveInstanceState()); // ... save stuff
return bundle;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) // implicit null check
{
Bundle bundle = (Bundle) state;
this.recyclerView.getLayoutManager().onRestoreInstanceState(bundle.getParcelable(SavedInstanceKey.RECYCLERVIEW.name())); // ... load stuff
state = bundle.getParcelable(SavedInstanceKey.SUPERSTATE.name());
}
super.onRestoreInstanceState(state);
}
and here is my fragment's OnCreateView:
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_artist, container, false);
final GridListView gv_new = view.findViewById(R.id.gridlist_new_songs);
final GridListView gv_best = view.findViewById(R.id.gridlist_best);
final GridListView gv_singles = view.findViewById(R.id.gridlist_singles);
final GridListView gv_feats = view.findViewById(R.id.gridlist_feats);
final RecyclerView rc_albums = view.findViewById(R.id.rcview_album);
if(!alreadyInitialized) {
alreadyInitialized = true;
apiService = new ApiService(getContext());
try {
artistID = getArguments().getString(KeyIntent.ARTIST.name());
} catch (Exception e) {
Log.e(TAG, "onCreateView: Artist Fragment doesnt have args.\t", e);
}
apiService.getArtist(artistID, new ApiService.OnArtistReceived() {
#Override
public void onSuccess(Artist artist) {
ArtistFragment.this.artist=artist;
setArtistToViews(artist, view);
}
#Override
public void onFail() {
Toast.makeText(getContext(), "Error on receiving artist.", Toast.LENGTH_LONG).show();
}
});
apiService.getNewSongs(artistID, new ApiService.OnSongsReceived() {
#Override
public void onSuccess(List<Song> songs) {
ArtistFragment.this.newSongs=songs;
List<GridListable> gridListables = new ArrayList<>();
gridListables.addAll(songs);
gv_new.load(gridListables, 1);
}
#Override
public void onFail(ApiService.ApiResponse response) {
Toast.makeText(getContext(), "Error on receiving artist.", Toast.LENGTH_LONG).show();
}
});
apiService.getBestSongs(artistID, new ApiService.OnSongsReceived() {
#Override
public void onSuccess(List<Song> songs) {
ArtistFragment.this.bestSongs=songs;
List<GridListable> gridListables = new ArrayList<>();
gridListables.addAll(songs);
gv_best.load(gridListables, 1);
}
#Override
public void onFail(ApiService.ApiResponse response) {
Toast.makeText(getContext(), "Error on receiving artist.", Toast.LENGTH_LONG).show();
}
});
apiService.getSingleSongs(artistID, new ApiService.OnSongsReceived() {
#Override
public void onSuccess(List<Song> songs) {
ArtistFragment.this.singleSongs=songs;
List<GridListable> gridListables = new ArrayList<>();
gridListables.addAll(songs);
gv_singles.load(gridListables, 1);
}
#Override
public void onFail(ApiService.ApiResponse response) {
}
});
apiService.getFeats(artistID, new ApiService.OnSongsReceived() {
#Override
public void onSuccess(List<Song> songs) {
ArtistFragment.this.feats=songs;
List<GridListable> gridListables = new ArrayList<>();
gridListables.addAll(songs);
gv_feats.load(gridListables, 1);
}
#Override
public void onFail(ApiService.ApiResponse response) {
}
});
apiService.getAlbums(artistID, new ApiService.OnAlbumsReceived() {
#Override
public void onSuccess(List<Album> albums) {
ArtistFragment.this.albums=albums;
List<Projective> projectives = new ArrayList<>();
projectives.addAll(albums);
rc_albums.setAdapter(new AlbumAdapter(getContext(), projectives));
rc_albums.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, true));
}
#Override
public void onFail(ApiService.ApiResponse response) {
Toast.makeText(getContext(), "Loading albums failed.", Toast.LENGTH_SHORT).show();
}
});
}else {
Log.i(TAG, "onCreateView: Fragment already initialized, restoring from existing artist");
setArtistToViews(artist,view);
gv_new.load(new ArrayList<>(newSongs),1);
gv_best.load(new ArrayList<>(bestSongs),1);
gv_singles.load(new ArrayList<>(singleSongs),1);
gv_feats.load(new ArrayList<>(feats),1);
rc_albums.setAdapter(new AlbumAdapter(getContext(), new ArrayList<>(albums)));
rc_albums.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, true));
}
return view;
}
I think the problem is the Keys that you use for your Bundles. All your instances of the custom view use the same SavedInstanceKey.SUPERSTATE.name().
You could try to have the Fragment pass a different key to each of the custom views (BEST, NEW...). This way, each of your GridView has its own unique key to use in the saveInstanceState and restoreInstanceState methods.
I try to make my own gallery. User can add a rating to every photo.
I want something like this: Main class put all photos on a screen. User click a photo then he can add a rating. Click back button on phone and main class refresh a rating, but intent is always null. Take a look on comments in code.
//My main class.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView)findViewById(R.id.imagegallery);
recyclerView.setHasFixedSize(true);
RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getApplicationContext(),2);
recyclerView.setLayoutManager(layoutManager);
createLists = prepareData();
adapter = new MyAdapter(getApplicationContext(), createLists);
recyclerView.setAdapter(adapter);
}
//My Adapter class from I send an Intent.
public MyAdapter(Context context, ArrayList<CreateList> galleryList) {
this.galleryList = galleryList;
this.context = context;
}
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.photo_layout, viewGroup, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final MyAdapter.ViewHolder viewHolder, final int i) {
viewHolder.title.setText(galleryList.get(i).getImage_title());
stars = (RatingBar) viewHolder.itemView.findViewById(R.id.ratingBar1);
stars.setRating(galleryList.get(i).getStars());
Picasso.with(context)
.load(galleryList.get(i)
.getImage_ID()).centerCrop()
.resize(240, 240)
.onlyScaleDown()
.into(viewHolder.img);
viewHolder.img.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent nextScreen = new Intent(context, ShowPhotoActivity.class);
nextScreen.putExtra("fullPhoto", galleryList.get(i));
nextScreen.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(nextScreen); //everything is OKAY
}
});
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.show_photo_activity_layout);
fullPhoto = (CreateList) getIntent().getSerializableExtra("fullPhoto"); //IS OKAY
photoID = fullPhoto.getImage_ID();
stars = (RatingBar)findViewById(R.id.ratingBar);
stars.setRating(fullPhoto.getStars());
if(savedInstanceState != null){
stars.setNumStars(savedInstanceState.getInt(starsPoint));
}
mImageView = (ImageView) findViewById(photoID);
mImageView = (ImageView) findViewById(R.id.image1);
mImageView.setImageResource(photoID);
//message = new Intent(getApplicationContext(), MainActivity.class);
stars.setOnRatingBarChangeListener(new RatingBar.OnRatingBarChangeListener() {
public void onRatingChanged(RatingBar ratingBar, float rating,
boolean fromUser) {
fullPhoto.set_Stars(rating);
message = new Intent(getApplicationContext(), MainActivity.class);
message.putExtra("Photo", fullPhoto);
message.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
System.out.println(fullPhoto.getStars()); //OKAY
startActivity(message);
}
});
}
//Now we are in main class. ALWAYS null. I've tried every solution on stack
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
CreateList photo = (CreateList) getIntent().getSerializableExtra("Photo"); //NULL
for(CreateList photoTemp : createLists) {
if (photoTemp.getImage_ID() == photo.getImage_ID()) {
photoTemp.set_Stars(photo.getStars());
}
}
}
Use onNewIntent callback provides intent parameter instead of call getIntent() method, so, your code must be like the follow:
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
CreateList photo = (CreateList) intent.getSerializableExtra("Photo");
for(CreateList photoTemp : createLists) {
if (photoTemp.getImage_ID() == photo.getImage_ID()) {
photoTemp.set_Stars(photo.getStars());
}
}
}
I load a recyclerview based on Firebase data via the following method:
#Override
public void onStart() {
super.onStart();
mChildEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
String newPollEpoch = dataSnapshot.getKey();
if (mNewPollsAray.contains(newPollEpoch)) {
Log.v("POLL_ADDED", "POLL ADDED: " + newPollEpoch);
} else {
Log.v("Child_Added", "The new child is " + newPollEpoch);
String newPollImageURL = dataSnapshot.child(IMAGE_URL).getValue(String.class);
//TODO: On additional devices, numbesr are not appearing as the question
String newPollQuestion = dataSnapshot.child(QUESTION_STRING).getValue(String.class);
String convertedQuestion = newPollQuestion.toString();
mNewPollsAray.add(0, new Poll(convertedQuestion, newPollImageURL, newPollEpoch));
mNewPollsAdapter.notifyDataSetChanged();
Log.v("OnChildChanged", "OnCHILDCHANGEDCALLED " + dataSnapshot.getKey());
}
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
mPollsRef.addChildEventListener(mChildEventListener);
}
#Override
public void onStop() {
super.onStop();
mPollsRef.removeEventListener(mChildEventListener);
}
Here is the method I call when an item in the recyclerview is clicked:
#Override
public void onClick(View view) {
view.getId();
int itemPosition = getAdapterPosition();
String passEpoch = mNewPollsAray.get(itemPosition).getPollID();
Log.v("PASSED_ID", "The passed ID is " + passEpoch);
Intent toPoll = new Intent(getActivity(), PollHostActivity.class);
toPoll.putExtra("POLL_ID", passEpoch);
startActivity(toPoll);
}
The fragment I am loading it from is part of a TabLayout. When I navigate between the tabs the recyclerview loads correctly.
However, when I click an item in the recyclerview (which takes me to a new activity) and then navigate back to the fragment containing the recyclerview, items get duplicated and the recyclerview items are all out of order. I think it has to do with onStart() being called multiple times and essentially "stacking" new items onto the recyclerview instead of replacing them, but I was hoping to confirm.
This happens because you add a listener, but never remove it. So the next time when you enter the view, you add a second listener and thus get two calls to onChildAdded() for each item in the database.
The solution is to remove the listener when you exit the view. Since you attach the listener in onStart(), you should remove it again in onStop():
#Override
public void onStop() {
mPollsRef.removeEventListener(mChildEventListener);
}
You can try with code, I was facing similar issue got resolved with bellow changes.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(mMainLayout == null)
{
mMainLayout = inflater.inflate(R.layout.fragment_main, container,false);
...
}
return mMainLayout;
}
When mMainlayout is not null, it mean that your fragment instance has already one instance of the mMainLayout and already added to ViewGroup container no need to add it again. You may be facing issue as you are adding same view again to same container.
By Clear the data set you can avoid loading of similar items again in Recycler View. It worked for me.
listOftrailers.clear();
try {
JSONObject jsonObject = new JSONObject(data);
JSONArray jsonArray = jsonObject.getJSONArray("results");
for (int i = 0; i < jsonArray.length(); i++) {
MovieTrailer item = new MovieTrailer();
JSONObject js = jsonArray.getJSONObject(i);
item.setVideoID(js.getString("id"));
item.setVideoName(js.getString("name"));
item.setVideoKey(js.getString("key"));
item.setVideoSite(js.getString("site"));
item.setVideoType(js.getString("type"));
String name = item.getVideoName();
if (name.contains("Official Trailer") ||
name.startsWith("Official"))
listOftrailers.add(item);
}
} catch (JSONException e) {
e.printStackTrace();
}
videosadapter = new TrailerListAdapter(listOftrailers.size(),
listOftrailers, MoviePage.this);
recyclerView.setAdapter(videosadapter);
Well I've run into an issue in my inventory app. I'm trying to retrieve a list of inventory items from Parse. This isn't the hardest thing in the world to do. At this point, I'm at a loss as to why the data is coming back as empty, when I can clearly see in Parse.com that there is data in the class I have requested from. Any ideas? (NOTE: I am able to add items to the database without a problem... it's just in the retrieval).
MainActivity:
public class MainActivity extends AppCompatActivity {
private ImageView mAddButton;
private ImageView mBackButton;
private Inventory mInventory;
private RecyclerView mRecyclerView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAddButton = (ImageView) findViewById(R.id.addItemButton);
mBackButton = (ImageView) findViewById(R.id.backButton);
mBackButton.setVisibility(View.INVISIBLE);
mInventory = new Inventory();
ParseUser user = ParseUser.getCurrentUser();
if (user == null) {
navToLogin();
} else {
Toast.makeText(MainActivity.this, "Welcome!", Toast.LENGTH_SHORT).show();
getInventoryFromParse();
Toast.makeText(MainActivity.this, mInventory.toString(), Toast.LENGTH_LONG).show();
}
mAddButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
addItem();
}
});
}
private void updateView() {
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
InventoryListAdapter adapter = new InventoryListAdapter(this, mInventory.getItemList());
mRecyclerView.setAdapter(adapter);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(layoutManager);
}
private void addItem() {
Intent intent = new Intent(MainActivity.this, AddItemActivity.class);
startActivityForResult(intent, 1);
}
private void navToLogin() {
Intent intent = new Intent(this, LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case (1):
updateView();
}
}
}
public void getInventoryFromParse() {
ParseQuery<ParseObject> query = ParseQuery.getQuery("Item");
query.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> objects, ParseException e) {
if (e == null) {
mInventory.setItemList(objects);
} else {
Toast.makeText(MainActivity.this, "There was an error.", Toast.LENGTH_LONG).show();
}
}
});
}
}
The Inventory Class:
public class Inventory {
private List<ParseObject> mItemList;
public Inventory() {
mItemList = new ArrayList<>();
}
public List<ParseObject> getItemList() {
return mItemList;
}
public void setItemList(List<ParseObject> itemList) {
mItemList = itemList;
}
public void addItem(ParseObject item) {
mItemList.add(item);
}
#Override
public String toString() {
return "Inventory{" +
"mItemList=" + mItemList +
'}';
}
}
The query creates a new thread which runs in the background, then your main thread moves on, exits the function, and the query still hasn't completed when you go to print out the inventory. setInventory has not been called when the main thread prints mInventory to string.
That's why your code isn't working.
As for a solution, I'm not sure how the Android dev kit works, but my suggestion to keep your code split up the way it is would be to make getInventoryFromParse have a return type, and call return inside of the query callback. I'm not sure if that'll throw errors since the main thread reaches the end of the function... If that doesn't work, you'll have to rewrite your code so that anything that needs to happen after the items are fetched happens inside of the callback.