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());
}
}
}
Related
In fragment'A' when a certain condition is established, it should go to 'ChatActivity'.
Intent intent = new Intent(getContext(), ChatActivity.class);
intent.addFlags(FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra("uid", matchedUid);
startActivity(intent);
The problem is when other fragment or Activity is showing on the top, the condition is established so startActivity is not executed.. occur an error
'java.lang.String android.content.Context.getPackageName()' on a null object reference
below is entire code
public class MatchFragment extends Fragment implements MatchMVP.View {
private static final String TAG = "MatchFragment";
private MatchPresenter matchPresenter;
private ToggleButton randomMatchBtn;
private ProgressBar progressBar, progressCircle;
private TextView searchingText;
private AdView adView;
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v;
setupMVP();
if(matchPresenter.checkOnlineStatus(getContext())) {
v = inflater.inflate(R.layout.fragment_match, container, false);
setupView(v);
matchPresenter.isSearching();
initAd(v);
} else {
v = inflater.inflate(R.layout.fragment_offline, container, false);
}
return v;
}
private void setupMVP() {
matchPresenter = new MatchPresenter(this);
}
private void setupView(View v) {
progressBar = v.findViewById(R.id.progressbar);
progressCircle = v.findViewById(R.id.progressbar_circle);
searchingText = v.findViewById(R.id.searching_text);
progressBar.setVisibility(View.INVISIBLE);
progressCircle.setVisibility(View.INVISIBLE);
searchingText.setVisibility(View.INVISIBLE);
randomMatchBtn = v.findViewById(R.id.random_match_btn);
randomMatchBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(matchPresenter.checkOnlineStatus(getContext())) {
if(randomMatchBtn.isChecked()) {
matchPresenter.searchRandomUser();
progressBar.setVisibility(View.VISIBLE);
} else {
matchPresenter.stopMatch();
progressBar.setVisibility(View.VISIBLE);
}
} else {
showSnackBar("error");
}
}
});
}
private void initAd(View v) {
MobileAds.initialize(getActivity(), "ca-app-pub-6263138384822549~5566878684");
adView = v.findViewById(R.id.adView);
AdRequest adRequest = new AdRequest.Builder().build();
adView.loadAd(adRequest);
}
#Override
public void createChatRoom(String matchedUid) {
Intent intent = new Intent(getContext(), ChatActivity.class);
intent.addFlags(FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra("uid", matchedUid);
startActivity(intent);
Vibrator vibrator;
if(getContext() != null) {
vibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(700);
}
}
#Override
public void showSnackBar(String msg) {
Snackbar snackbar = Snackbar.make(getActivity().findViewById(android.R.id.content), msg, 2500);
View snackBarLayout = snackbar.getView();
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
// Layout must match parent layout type
lp.setMargins(0, 300, 0, 0);
// Margins relative to the parent view.
snackBarLayout.setLayoutParams(lp);
snackbar.show();
}
#Override
public void randomMatchBtnOff() {
randomMatchBtn.setChecked(false);
}
#Override
public void randomMatchBtnDisable() {
randomMatchBtn.setEnabled(false);
}
#Override
public void randomMatchBtnEnable() {
randomMatchBtn.setEnabled(true);
progressBar.setVisibility(View.INVISIBLE);
}
#Override
public void showProgressCircle() {
progressCircle.setVisibility(View.VISIBLE);
searchingText.setVisibility(View.VISIBLE);
}
#Override
public void hideProgressCircle() {
progressCircle.setVisibility(View.INVISIBLE);
searchingText.setVisibility(View.INVISIBLE);
}
#Override
public void goAuthActivity() {
Intent intent = new Intent(getContext(), AuthActivity.class);
intent.putExtra("isSanctioned", true);
startActivity(intent);
assert getActivity() != null;
getActivity().finish();
}
#Override
public void onResume() {
super.onResume();
matchPresenter.checkIsSan();
}
#Override
public void onPause() {
super.onPause();
if(isThreadRunning) {
timeCheckThread.interrupt();
}
}
}
Cause of error is 'Fragment is not attached to its Activity'. Yeah I know. I'm making a randomChatting app with firebase. In this MatchFragment, I'm searching other users. When other users start searching, matched with me then let me know by go to 'ChatActivity'.
But if I'm in other fragment of activity, searching is activating, it can't go ChatActivity. 'Fragment is not attached to its Activity'.
Because I'm in other activity not in this MatchFragment. MatchFragment detached to its Activity.
How go to ChatActivity even if I'm in other activity.
You can either try using requireActivity() instead of getActivity() but since you only need a Context object and not necessarily an Activity object, I suggest you replace getActivity() with requireContext().
If that doesn't work out then you can try following this answer: https://stackoverflow.com/a/30498143
PS: I know this should be shared as a comment but my reputation is currently only 41 and I can't post a comment so writing this as an answer.
I have a problem with my RecyclerView.
I have a ProductDetailActivity which shows the detail of a product and i have a RecyclerView with its adapter in it.
The user can click on the give rating button which navigates to the RatingActivity where you can give a rating to the product.
The problem is that when i submit my rating and automatically go back to my RatingActivity, the RecyclerView does not get the recently added rating. i have to go back to my productlist and reclick on the product to see the recently added rating.
Here is my code:
ProductDetailActivity:
public class ProductDetailActivity extends AppCompatActivity {
public AppDatabase appDatabase;
private static final String DATABASE_NAME = "Database_Shop";
private RecyclerView mRecycleviewRating;
private RatingAdapter mAdapterRating;
private Button btnGoToRatingActivity;
List<Rating> ratings;
Product p;
int id;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_product_detail);
appDatabase = Room.databaseBuilder(getApplicationContext(),AppDatabase.class,DATABASE_NAME)
.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.build();
btnGoToRatingActivity = findViewById(R.id.btn_goToRatingActivity);
Intent intent = getIntent();
id = intent.getIntExtra("productid", -1);
// pour montrer tous les ratings d'un produit, tu fais un getall
p = appDatabase.productDAO().getProductById(id);
ImageView imageView = findViewById(R.id.imageDetail);
TextView textViewName = findViewById(R.id.txt_nameDetail);
TextView textViewAuthor = findViewById(R.id.txt_authorDetail);
TextView textViewCategory = findViewById(R.id.txt_categoryDetail);
TextView textViewDetail = findViewById(R.id.txt_descriptionDetail);
Picasso.get().load(p.getProductImage()).fit().centerInside().into(imageView);
textViewName.setText(p.getProductName());
textViewAuthor.setText(p.getProductAuthor());
textViewCategory.setText(p.getProductCategory());
textViewDetail.setText(p.getProductDescription());
ratings = appDatabase.ratingDAO().getRatingByProductId(id);
mRecycleviewRating = findViewById(R.id.recyclerRating_view);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mRecycleviewRating.setLayoutManager(linearLayoutManager);
//recyclerView.setLayoutManager(new LinearLayoutManager(this));
mAdapterRating = new RatingAdapter(ratings);
mRecycleviewRating.setAdapter(mAdapterRating);
btnGoToRatingActivity.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(ProductDetailActivity.this, RatingActivity.class);
i.putExtra("productid", p.getProduct_id());
startActivity(i);
}
});
mAdapterRating.notifyDataSetChanged();
}
#Override
public void onResume() {
super.onResume();
ratings = appDatabase.ratingDAO().getRatingByProductId(id); // reload the items from database
mAdapterRating.notifyDataSetChanged();
System.out.println(mAdapterRating.ratings.size());
}
}
RatingActivity:
public class RatingActivity extends AppCompatActivity implements RatingGiveFragment.RatingListener {
RelativeLayout mRelativeLayout;
private Button btnConfirmRating;
private EditText mComment;
private RatingBar mRatingBar;
public AppDatabase appDatabase;
private RatingAdapter mAdapter;
List<Rating> ratings;
private static final String DATABASE_NAME = "Database_Shop";
Product p;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_rating);
appDatabase = Room.databaseBuilder(getApplicationContext(),AppDatabase.class,DATABASE_NAME)
.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.build();
int idProduct = RatingActivity.this.getIntent().getIntExtra("productid",-1);
p = appDatabase.productDAO().getProductById(idProduct);
mRatingBar = findViewById(R.id.rating_bar);
mComment = findViewById(R.id.txt_insertOpinionText);
mRelativeLayout = findViewById(R.id.activity_rating);
btnConfirmRating = findViewById(R.id.buttonConfirmRating);
mAdapter = new RatingAdapter(ratings);
btnConfirmRating.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(!checkEmptyFields()) {
Rating rating = new Rating(p.getProduct_id(),UserConnected.connectedUser.getUser_id(),mRatingBar.getRating(), UserConnected.connectedUser.getUsername(), mComment.getText().toString());
appDatabase.ratingDAO().insertRating(rating);
mAdapter.notifyDataSetChanged();
finish();
}else{
Toast.makeText(RatingActivity.this, "Empty Fields", Toast.LENGTH_SHORT).show();
}
}
});
}
/*private class insertRating extends AsyncTask<String,Integer, Integer>
{
#Override
protected Integer doInBackground(String... strings) {
Rating rating = new Rating(Integer.parseInt(strings[0]), Integer.parseInt(strings[1]), Integer.parseInt(strings[2]), strings[3], strings[4]);
appDatabase.ratingDAO().insertRating(rating);
return 1;
}
#Override
protected void onPostExecute(Integer integer) {
super.onPostExecute(integer);
if (integer == 1)
{
Toast.makeText(getApplicationContext(), getString(R.string.createRating), Toast.LENGTH_SHORT).show();
}
}
}*/
#Override
public void ratingChanged(int newRating) {
RatingTextFragment textFragment = (RatingTextFragment) getSupportFragmentManager().findFragmentById(R.id.fmt_text);
textFragment.setRating(newRating);
}
private boolean checkEmptyFields(){
if(TextUtils.isEmpty(mComment.getText().toString())){
return true;
}else{
return false;
}
}
}
RatingAdapter:
public class RatingAdapter extends RecyclerView.Adapter<RatingAdapter.RatingViewHolder> {
List<Rating> ratings;
public RatingAdapter(List<Rating> ratings){
this.ratings = ratings;
}
#NonNull
#Override
public RatingViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.rating_row,viewGroup, false);
return new RatingViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull RatingViewHolder ratingViewHolder, int position) {
ratingViewHolder.ratingUsername.setText(ratings.get(position).getRatingUsername());
ratingViewHolder.ratingNumber.setText(String.valueOf(ratings.get(position).getRatingNumber()) + "/5");
ratingViewHolder.ratingComment.setText(ratings.get(position).getRatingText());
}
#Override
public int getItemCount() {
return ratings.size();
}
public static class RatingViewHolder extends RecyclerView.ViewHolder{
public TextView ratingUsername;
public TextView ratingNumber;
public TextView ratingComment;
public RatingViewHolder(#NonNull View itemView) {
super(itemView);
ratingUsername = itemView.findViewById(R.id.txt_usernamerating);
ratingNumber = itemView.findViewById(R.id.num_rating);
ratingComment = itemView.findViewById(R.id.txt_ratingComment);
}
}
}
Pictures:
You get no update in the ProductDetailActivity because you are not updating the data object ratings in the ProductDetailActivity that is the basis for the RatingAdapter.
It would be better to use startActivityForResult in the onClick()method of the ProductDetailActivity. Then you need to override the onActivityResult() method in the ProductDetailActivity. Evaluate the return values and update your data source if necessary, then call notifyDataSetChanged.
This is just pseudo code!
Changes to ProductDetailActivity:
btnGoToRatingActivity.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(ProductDetailActivity.this, RatingActivity.class);
i.putExtra("productid", p.getProduct_id());
// with this you are telling the activity to expect results and..
//..to deal with them in onActivityResult
startActivityForResult(i, 1);
}
});
// You do not need this next line because setting the adaper triggers the first
//mAdapterRating.notifyDataSetChanged();
}
Add the onActivityResult() method to the ProductDetailActivity.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1) {
if(resultCode == Activity.RESULT_OK){
// trigger a method to update the data object that is linked to the adapter
ratings = appDatabase.ratingDAO().getRatingByProductId(id);
// and now that the data has actually been updated you can call notifyDataSetChanged!!
mAdapterRating.notifyDataSetChanged();
}
if (resultCode == Activity.RESULT_CANCELED) {
//Probably do nothing or make a Toast "Canceled"??
}
}
}
Changes to RatingActivity:
btnConfirmRating.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(!checkEmptyFields()) {
// I will just assume this works!
Rating rating = new Rating(p.getProduct_id(),UserConnected.connectedUser.getUser_id(),mRatingBar.getRating(), UserConnected.connectedUser.getUsername(), mComment.getText().toString());
appDatabase.ratingDAO().insertRating(rating);
Intent intent = new Intent();
//If you need to return some value.. do it here other you do not need it
//intent.putExtra("result", result);
setResult(Activity.RESULT_OK, intent);
finish();
}else{
Toast.makeText(RatingActivity.this, "Empty Fields", Toast.LENGTH_SHORT).show();
}
}
});
Please be aware in RatingActivity that in btnConfirmRating.setOnClickListener notifying the adapter with mAdapter.notifyDataSetChanged(); does nothing: firstly, because the adapter in the RatingActivity has nothing to do with the adapter in the ProductDetailActivity; secondly: you call finish(); in the next line of code.
I am creating an android app, where I'll be asking for multiple types of questions using RadioButtons. I don't want to make multiple Activities for these questions. Can anyone please tell me how to do that with a short example, of at least two questions?
You can use multiples fragments... or call the activity itself multiple times...
I did an app like yours and i choose the first method!
This is some fragment of a project that i wrote, and the activity that manipulate it, you will have to change it according to your needs.
Activity
public class CollectActivity extends FragmentActivity {
MyPageAdapter pageAdapter;
NonSwipeableViewPager pager;
SpringIndicator springIndicator;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_collect);
List<Fragment> fragments = getFragments();
pager = (NonSwipeableViewPager) findViewById(R.id.view_pager);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
PagerModelManager manager = new PagerModelManager();
manager.addCommonFragment(fragments, getTitles());
ModelPagerAdapter adapter = new ModelPagerAdapter(getSupportFragmentManager(), manager);
pager.setAdapter(adapter);
springIndicator = (SpringIndicator) findViewById(R.id.indicator);
springIndicator.setViewPager(pager);
springIndicator.setOnTabClickListener(new TabClickListener() {
#Override
public boolean onTabClick(int position) {
return false;
}
});
}
private List<Fragment> getFragments() {
List<Fragment> fList = new ArrayList<Fragment>();
fList.add(CollectFragment.newInstance("Fragment 1"));
fList.add(CollectFragment.newInstance("Fragment 2"));
fList.add(CollectFragment.newInstance("Fragment 3"));
//add your fragments with a loop
return fList;
}
private List<String> getTitles() {
return Lists.newArrayList("1", "2", "3");
}
public void swipeFragment() {
pager.setCurrentItem(pager.getCurrentItem() + 1);
}
public int getFragment() {
return pager.getCurrentItem();
}
}
Fragment
public class CollectFragment extends Fragment {
private Button openButton;
private Button confirmationCloseButton;
private Button yesRenew;
private Button noRenew;
private BroadcastReceiver udpMessages;
public static final String EXTRA_MESSAGE = "EXTRA_MESSAGE";
public static final CollectFragment newInstance(String message) {
CollectFragment f = new CollectFragment();
Bundle bdl = new Bundle(1);
bdl.putString(EXTRA_MESSAGE, message);
f.setArguments(bdl);
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
String message = getArguments().getString(EXTRA_MESSAGE);
View v = null;
if (message.compareTo("Fragment 1") == 0) {
v = inflater.inflate(R.layout.fragment_collect_open, container, false);
openButton = (Button) v.findViewById(R.id.open_button);
openButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent i2 = new Intent();
i2.setComponent(new ComponentName("qira.com.locker", "qira.com.locker.Service.MessageService"));
i2.putExtra("Message", "CONFIRM_LOCKER_1_CLOSED");
getContext().startService(i2);
}
});
}
if (message.compareTo("Fragment 2") == 0) {
v = inflater.inflate(R.layout.fragment_collect_close, container, false);
confirmationCloseButton = (Button) v.findViewById(R.id.confirmation_close_button);
confirmationCloseButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent i2 = new Intent();
i2.setComponent(new ComponentName("qira.com.locker", "qira.com.locker.Service.MessageService"));
i2.putExtra("Message", "OPEN_LOCKER_1");
getContext().startService(i2);
}
});
}
if (message.compareTo("Fragment 3") == 0) {
v = inflater.inflate(R.layout.fragment_collect_renew, container, false);
yesRenew = (Button) v.findViewById(R.id.yes_button);
noRenew = (Button) v.findViewById(R.id.no_button);
yesRenew.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
((CollectActivity) getActivity()).swipeFragment();
}
});
noRenew.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent i = new Intent(getContext(), ReserveActivity.class);
startActivity(i);
}
});
}
return v;
}
#Override
public void onResume() {
super.onResume();
udpMessages = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() != null && intent.getAction().equals("UDP.MESSAGES.COLLECT")) {
if (intent.getExtras().getString("Type").compareTo("OPEN_LOCKER_1-LOCKER_OPENED") == 0) {
if (((CollectActivity) getActivity()).getFragment() != 0) { // onCreateView called twice, dont know why... workaround to solve this problem
((CollectActivity) getActivity()).swipeFragment();
}
}
if (intent.getExtras().getString("Type").compareTo("CONFIRM_LOCKER_1_CLOSED-TRUE") == 0) {
if (((CollectActivity) getActivity()).getFragment() != 1) { // onCreateView called twice, dont know why... workaround to solve this problem
((CollectActivity) getActivity()).swipeFragment();
}
}
}
}
};
getContext().registerReceiver(udpMessages, new IntentFilter("UDP.MESSAGES.COLLECT"));
}
#Override
public void onPause() {
super.onPause();
getContext().unregisterReceiver(udpMessages);
}
#Override
public void onDestroyView() {
super.onDestroyView();
}
}
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.
I'm doing an activity to measure how long it takes a person to do an exercise, but it has a bug that I couldn't resolve yet...
The TrainingFragment shows a list of exercises that the user can click and then my ExerciseActivity is launched and runs until the variable "remainingsSets" is setted to 0.
When I click in the first time at any exercise, everything works fine, the ExerciseActivity works correctly end return to the TrainingFragment. But then, if I try to click in another exercise, the ExerciseActivity is just closed.
In my debug, I could see that the variable "remainingSets" comes with it's right value (remainingSets = getIntent().getIntExtra("remaining_sets", 3)), but when the startButton is clicked, I don't know why the variable "remainingSets" is setted to 0 and then the activity is closed because this condition: if (remainingSets > 0){...}.
Here is my TrainingFragment:
public class TrainingFragment extends Fragment {
private final static int START_EXERCISE = 1;
private Training training;
private String lastItemClicked;
private String[] values;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
Bundle bundle = getArguments();
if (bundle != null) {
training = bundle.getParcelable("training");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return (ScrollView) inflater.inflate(R.layout.template_exercises, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
LinearLayout exercisesContainer = (LinearLayout) getView().findViewById(R.id.exercises);
LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
List<Exercise> exercises = training.getExercises();
values = new String[exercises.size()];
if (savedInstanceState != null) {
values = savedInstanceState.getStringArray("values");
}
for (int i = 0; i < exercises.size(); i++) {
final View exerciseView = inflater.inflate(R.layout.template_exercise, null);
exerciseView.setTag(String.valueOf(i));
TextView remainingSets = (TextView) exerciseView.findViewById(R.id.remaining_sets);
if (savedInstanceState != null) {
remainingSets.setText(values[i]);
} else {
String sets = exercises.get(i).getSets();
remainingSets.setText(sets);
values[i] = sets;
}
exerciseView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), ExerciseActivity.class);
intent.putExtra("remaining_sets",
Integer.valueOf(((TextView) v.findViewById(R.id.remaining_sets)).getText().toString()));
lastItemClicked = v.getTag().toString();
startActivityForResult(intent, START_EXERCISE);
}
});
exercisesContainer.addView(exerciseView);
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putStringArray("values", values);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
View view = ((LinearLayout) getView().findViewById(R.id.exercises)).findViewWithTag(lastItemClicked);
if (requestCode == START_EXERCISE) {
if (resultCode == Activity.RESULT_OK) { // the exercise had been
// finished.
((TextView) view.findViewById(R.id.remaining_sets)).setText("0");
view.setClickable(false);
values[Integer.valueOf(lastItemClicked)] = "0";
} else if (resultCode == Activity.RESULT_CANCELED) {
String remainingSets = data.getStringExtra("remaining_sets");
((TextView) view.findViewById(R.id.remaining_sets)).setText(remainingSets);
values[Integer.valueOf(lastItemClicked)] = remainingSets;
}
}
}
}
My ExerciseActivity:
public class ExerciseActivity extends Activity {
private Chronometer chronometer;
private TextView timer;
private Button startButton;
private Button endButton;
private int remainingSets;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_exercise);
ExerciseEvents.addExerciseListener(new PopupExerciseListener());
chronometer = (Chronometer) findViewById(R.id.exercise_doing_timer);
timer = (TextView) findViewById(R.id.timer);
startButton = (Button) findViewById(R.id.start_exercise);
startButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
ExerciseEvents.onExerciseBegin();
}
});
endButton = (Button) findViewById(R.id.end_exercise);
endButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
ExerciseEvents.onExerciseRest();
}
});
}
#Override
public void onBackPressed() {
Intent intent = new Intent();
intent.putExtra("remaining_sets", String.valueOf(remainingSets));
setResult(RESULT_CANCELED, intent);
super.onBackPressed();
}
public class PopupExerciseListener implements ExerciseListener {
public PopupExerciseListener() {
remainingSets = getIntent().getIntExtra("remaining_sets", 3);
}
#Override
public void onExerciseBegin() {
if (remainingSets > 0) {
chronometer.setVisibility(View.VISIBLE);
timer.setVisibility(View.GONE);
chronometer.setBase(SystemClock.elapsedRealtime());
chronometer.start();
startButton.setVisibility(View.GONE);
endButton.setVisibility(View.VISIBLE);
} else {
ExerciseEvents.onExerciseFinish();
}
}
#Override
public void onExerciseFinish() {
setResult(RESULT_OK);
finish();
}
#Override
public void onExerciseRest() {
chronometer.setVisibility(View.GONE);
endButton.setVisibility(View.GONE);
timer.setVisibility(View.VISIBLE);
long restTime = getIntent().getLongExtra("time_to_rest", 60) * 1000;
new CountDownTimer(restTime, 1000) {
#Override
public void onTick(long millisUntilFinished) {
timer.setText(String.valueOf(millisUntilFinished / 1000));
}
#Override
public void onFinish() {
ExerciseEvents.onExerciseBegin();
}
}.start();
remainingSets--;
}
}
}
And my ExerciseEvents:
public class ExerciseEvents {
private static LinkedList<ExerciseListener> mExerciseListeners = new LinkedList<ExerciseListener>();
public static void addExerciseListener(ExerciseListener listener) {
mExerciseListeners.add(listener);
}
public static void removeExerciseListener(String listener) {
mExerciseListeners.remove(listener);
}
public static void onExerciseBegin() {
for (ExerciseListener l : mExerciseListeners) {
l.onExerciseBegin();
}
}
public static void onExerciseRest() {
for (ExerciseListener l : mExerciseListeners) {
l.onExerciseRest();
}
}
public static void onExerciseFinish() {
for (ExerciseListener l : mExerciseListeners) {
l.onExerciseFinish();
}
}
public static interface ExerciseListener {
public void onExerciseBegin();
public void onExerciseRest();
public void onExerciseFinish();
}
}
Could anyone give me any help?
After you updated your code, I see you have a big memory leak in your code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_exercise);
ExerciseEvents.addExerciseListener(new PopupExerciseListener());
....
}
The call ExerciseEvents.addExerciseListener(new PopupExerciseListener()) adds a new PopupExerciseListener to a static/global list: ExcerciseEvents.mExerciseListeners. Since the class PopupExerciseListener is an inner-class, it implicitly holds a reference to its enclosing ExcerciseActivity. This mean your code is holding on to each instance of ExcerciseActivity forever. Not good.
This may also explain the weird behavior you see. When one of the onExcersizeXXX() methods is called, it will call all ExcerciseListeners in the linked-list, the ones from previous screens and the current one.
Try this in your ExcerciseActivity.java:
....
ExerciseListener mExerciseListener;
....
#Override
protected void onCreate(Bundle savedInstanceState) {
....
....
mExerciseListener = new PopupExerciseListener()
ExerciseEvents.addExerciseListener(mExerciseListener);
....
....
}
#Override
protected void onDestroy() {
ExerciseEvents.removeExerciseListener(mExerciseListener);
super.onDestroy();
}
....
In onDestroy, you deregister your listener, preventing a memory leak and preventing odd multiple callbacks to PopupExerciseListeners that are attached to activities that no longer exist.