I have an app, where I have one Activity (main), and there in the layout, I have a ViewPager2. That ViewPager2's adapter is set with 2 fragments. Let's call one of these as Fragment A.
When Fragment A opens up, users have the option to open another fragment, Fragment B from within itself. (by transaction; I have an empty FrameLayout inside Fragment A). Fragment B provides a dialog box to edit the data shown with a RecyclerView in Fragment A. Fragment A itself also has a few options to change the data in its RecyclerView.
This data is populated from Shared preferences. Now, here's the problem - since the Fragments can be made to communicate only with the Activity with Interfaces, I can save the data in the Activity, but then I have no other option but to reload the entire RecyclerView in Fragment A since Fragment A doesn't know at which position it got the new data.
Please advice. Below are the Fragments and the Activity that I mentioned.
MainActivity.java:
package com.coffeetech.kittycatch;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import android.os.Bundle;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity implements FoodEditorFragment.SendFoodFromEditor{
ViewPager2 viewPager;
TabLayout tab_menu;
SwipeAdapter adapter;
ArrayList<Food> foods;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager=findViewById(R.id.viewpager);
adapter=new SwipeAdapter(this);
viewPager.setAdapter(adapter);
//SETTING TABS
tab_menu = findViewById(R.id.tab_menu);
new TabLayoutMediator(tab_menu, viewPager, true, new TabLayoutMediator.TabConfigurationStrategy() {
#Override
public void onConfigureTab(#NonNull TabLayout.Tab tab, int position) {
switch(position){
case 0:
tab.setText("INVENTORY");
break;
case 1:
tab.setText("SHOPPING LIST");
}
}
}).attach();
}
#Override
public void onBackPressed() {
super.onBackPressed(); //TODO: CONFIGURE THIS
}
#Override
public void sendFood(Food food, int position) { //TODO: CODE TO REPLACE DATA TO SPECIFIC POSITION
loadData();
saveData();
}
#Override
public void sendFood(Food food) { //TODO: CODE TO ADD DATA TO POSITION 0
loadData();
saveData();
}
public void loadData(){
//TODO: LOAD THE LIST HERE
}
public void saveData(){
//TODO: SAVE THE DATA HERE
}
}
The FragmentStateAdapter for the ViewPager2:
package com.coffeetech.kittycatch;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
public class SwipeAdapter extends FragmentStateAdapter {
Fragment fragment;
public SwipeAdapter(FragmentActivity fa){
super(fa);
}
#NonNull
#Override
public Fragment createFragment(int position) {
switch (position){
case 0: //START INVENTORY FRAGMENT
fragment = new InventoryFragment();
break;
case 1: //START SHOPPING LIST FRAGMENT
fragment = new ShoppingListFragment();
break;
}
return fragment;
}
#Override
public int getItemCount() {
return 2;
}
}
Fragment 'A':
package com.coffeetech.kittycatch;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList;
public class InventoryFragment extends Fragment {
//GLOBAL VARIABLES
RecyclerView recyclerView;
FoodAdapter foodAdapter;
ArrayList<Food> foods = new ArrayList<Food>();
FloatingActionButton add_button;
FrameLayout frameLayout;
Food food;
int q; //for use in decrease and functions
public InventoryFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onPause() { //TODO:CODE TO SAVE DATA
saveData();
super.onPause();
}
#Override
public void onResume() { //TODO:CODE TO LOAD DATA
loadData();
super.onResume();
//food constructor takes arguments -> (String name, int type, int quantity, int min_quantity)
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_inventory, container, false);
foodAdapter = new FoodAdapter(foods);
recyclerView=v.findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(foodAdapter);
//setting up the frameLayout
frameLayout = v.findViewById(R.id.food_editor_frame);
//setting up the Add button
add_button=v.findViewById(R.id.add_button);
add_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) { //for new food addition to list
openFoodEditorFragment();
}
});
foodAdapter.setOnFoodcardClickListener(new FoodAdapter.OnFoodcardClickListener() {
#Override
public void deleteFood(int position) { //code that deletes current food
foods.remove(position);
foodAdapter.notifyItemRemoved(position);
}
#Override
public void onEdit(int position, int mode) { //code that runs the edit of each food, mode=1 for edit
openFoodEditorFragment(position,foods.get(position)); //TODO: SEE IF YOU NEED 'mode' AT ALL
}
#Override
public void decrease(int position) {
food = foods.get(position);
q=food.getQuantity();
food.setQuantity(q-1);
foodAdapter.notifyItemChanged(position);
}
#Override
public void increase(int position) {
food = foods.get(position);
q=food.getQuantity();
food.setQuantity(q+1);
foodAdapter.notifyItemChanged(position);
}
#Override
public void setSeekBar(int position,int progress) {
food = foods.get(position);
food.setQuantity(progress);
Toast.makeText(getContext(),"Quantity set to "+progress+"%",Toast.LENGTH_SHORT).show();
foodAdapter.notifyItemChanged(position);
}
});
return v;
}
public void openFoodEditorFragment(int position,Food food){ //code for Editor in Edit Old mode
//creating new Bundle and passing each member data of 'food'
Bundle bundle = new Bundle();
bundle.putString("name",food.getName());
bundle.putInt("type",food.getType());
bundle.putInt("quantity",food.getQuantity());
bundle.putInt("min_quantity",food.getMin_quantity());
bundle.putInt("mode",1);
bundle.putInt("position",position);
//setting up arguments for Fragment
FoodEditorFragment foodEditorFragment = new FoodEditorFragment();
foodEditorFragment.setArguments(bundle);
//creating the Fragment
FragmentTransaction transaction=getFragmentManager().beginTransaction();
transaction.replace(R.id.food_editor_frame,foodEditorFragment);
transaction.commit();
}
public void openFoodEditorFragment(){ //code for Editor in Add New mode
//creating new Bundle and passing each member data of 'food'
Bundle bundle = new Bundle();
bundle.putInt("position",0);
bundle.putInt("mode",0);
//setting up arguments for Fragment
FoodEditorFragment foodEditorFragment = new FoodEditorFragment();
foodEditorFragment.setArguments(bundle);
//creating the Fragment
FragmentTransaction transaction=getFragmentManager().beginTransaction();
transaction.replace(R.id.food_editor_frame,foodEditorFragment);
transaction.commit();
}
protected void setData(int position, Food food){
foods.remove(position);
foods.add(position,food);
foodAdapter.notifyItemChanged(position);
}
protected void setData(Food food){
foods.add(0,food);
foodAdapter.notifyItemInserted(0);
Toast.makeText(getContext(),"Added to the top",Toast.LENGTH_SHORT).show();
}
void loadData(){
//TODO: CODE TO LOAD DATA
}
void saveData(){
//TODO: CODE TO SAVE DATA
}
}
Fragment 'B':
package com.coffeetech.kittycatch;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;
public class FoodEditorFragment extends Fragment {
private TextView name,quantity,min_quantity;
private ImageButton save,cancel;
private RadioGroup radioGroup;
protected int mode,t,position;
protected Food food = new Food();
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
sffe = (SendFoodFromEditor)context;
}
public FoodEditorFragment() {
// Required empty public constructor
}
public interface SendFoodFromEditor{
void sendFood(Food food,int position); //FOR EDITING FOOD
void sendFood(Food food); //FOR NEW FOOD
}
SendFoodFromEditor sffe;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_food_editor, container, false);
name=view.findViewById(R.id.name_editor);
quantity=view.findViewById(R.id.quantity_editor);
min_quantity=view.findViewById(R.id.min_quantity_editor);
save=view.findViewById(R.id.save_button_editor);
cancel=view.findViewById(R.id.cancel_button_editor);
radioGroup=view.findViewById(R.id.radioGroup_editor);
mode=getArguments().getInt("mode");
position=getArguments().getInt("position");
if (mode==1){ //for editing Food
//CODE TO SETUP EDITOR ACCORDING TO INITIAL DETAILS
name.setText(getArguments().getString("name"));
quantity.setText(String.valueOf(getArguments().getInt("quantity")));
min_quantity.setText(String.valueOf(getArguments().getInt("min_quantity")));
t=getArguments().getInt("type");
if(t==0){//for discrete food
radioGroup.check(R.id.discrete_radioButton);
}else{//for cont food
radioGroup.check(R.id.cont_radioButton);
}
}
setButtons();
return view;
}
public void setButtons(){
save.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) { //USE BELOW 'food' TO PASS NEW DATA TO ACTIVITY
try {
if ((!name.getText().toString().isEmpty()) && ((radioGroup.getCheckedRadioButtonId() == R.id.discrete_radioButton) || (radioGroup.getCheckedRadioButtonId() == R.id.cont_radioButton))) {
food.setName(name.getText().toString());
food.setQuantity(Integer.parseInt(quantity.getText().toString()));
food.setMin_quantity(Integer.parseInt(min_quantity.getText().toString()));
if (radioGroup.getCheckedRadioButtonId() == R.id.discrete_radioButton) {
food.setType(0);
} else if (radioGroup.getCheckedRadioButtonId() == R.id.cont_radioButton) {
food.setType(1);
}
//CODE TO SEND THE FOOD TO ACTIVITY
if (mode == 1) {
sffe.sendFood(food, position);
} else {
sffe.sendFood(food);
}
//CLOSE THE FRAGMENT
getFragmentManager().beginTransaction().remove(FoodEditorFragment.this).commit();
} else {
throw new Exception();
}
}catch (Exception e){
Toast.makeText(getContext(),"Please set all details",Toast.LENGTH_SHORT).show();
}
}
});
cancel.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) { //CODE IF USER PRESSES ON CANCEL
//CLOSE THE FRAGMENT
getFragmentManager().beginTransaction().remove(FoodEditorFragment.this).commit();
}
});
}
}
You need to make following changes.
In Fragment A,
Replace
transaction.replace(R.id.food_editor_frame,foodEditorFragment);
With
transaction.add(R.id.food_editor_frame,foodEditorFragment);
Reason: using replace will remove the fragmentA and FragmentA will always be recreated.
In MainActivity
#Override
public void sendFood(Food food, int position){
//find fragment by Id
Fragment fragmentA = (Fragment)
getSupportFragmentManager().findFragmentById(R.id.fragmetnA);
fragment.setData(position,food)
//code for save data here
}
#Override
public void sendFood(Food food) {
Fragment fragmentA = (Fragment)
getSupportFragmentManager().findFragmentById(R.id.fragmetnA);
fragment.setData(food)
//code for save data here
}
Related
I have a fragment (Inventory Fragment) that displays some CardView objects in a RecyclerView. These objects get their data from an Adapter after the data has been received from the ViewModel. Inside the adapter, there are certain functions that can change the Livedata. All of these functions first open another Fragment (Food Editor), and this where the new data is set to the ViewModel.
My problem is that even after this, the RecyclerView does not get the new object. I used notifyDataSetChanged(). What am I doing wrong? Is there an easier way to achieve what I am trying to do (Add, Delete, Modify and such)?
Inventory Fragment:
package com.coffeetech.kittycatch;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList;
import java.util.List;
public class InventoryFragment extends Fragment {
//GLOBAL VARIABLES
RecyclerView recyclerView;
FoodAdapter foodAdapter;
FloatingActionButton add_button;
FrameLayout frameLayout;
FoodViewModel foodViewModel;
//FOOD LIST
private List<Food> foodList;
public InventoryFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_inventory, container, false);
//GETTING THE FOOD VIEW MODEL
foodViewModel = new ViewModelProvider(this,ViewModelProvider.AndroidViewModelFactory.getInstance(getActivity().getApplication())).get(FoodViewModel.class); //TODO:HERE
foodList = foodViewModel.getFoods().getValue();
foodAdapter = new FoodAdapter(foodList);
recyclerView=v.findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(foodAdapter);
//SETTING THE FOOD VIEW MODEL
foodViewModel.getFoods().observe(getActivity(), new Observer<List<Food>>() {
#Override
public void onChanged(List<Food> foods) {
foodAdapter.setFoods(foods);
foodList=foods;
foodAdapter.notifyDataSetChanged(); //TODO: MAKE THIS BETTER
}
});
//setting up the frameLayout
frameLayout = v.findViewById(R.id.food_editor_frame);
//setting up the Add button
add_button=v.findViewById(R.id.add_button);
add_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) { //for new food addition to list
openFoodEditorFragment(-1);
}
});
foodAdapter.setOnFoodcardClickListener(new FoodAdapter.OnFoodcardClickListener() {
#Override
public void deleteFood(int position) { //code that deletes current food
foodViewModel.delete(foodList.get(position));
foodAdapter.notifyItemRemoved(position);
}
#Override
public void onEdit(int position) { //code that runs the edit of each food
openFoodEditorFragment(position);
foodAdapter.notifyItemChanged(position);
}
#Override
public void decrease(int position) {
foodList.get(position).decrease();
foodViewModel.update(foodList.get(position));
foodAdapter.notifyItemChanged(position);
}
#Override
public void increase(int position) {
foodList.get(position).increase();
foodViewModel.update(foodList.get(position));
foodAdapter.notifyItemChanged(position);
}
#Override
public void setSeekBar(int position,int progress) {
foodList.get(position).setQuantity(progress);
foodViewModel.update(foodList.get(position));
foodAdapter.notifyItemChanged(position);
}
});
return v;
}
public void openFoodEditorFragment(int position){ //position = -1 for new, and an integer (position) for edit
FoodEditorFragment foodEditorFragment;
switch(position){
case -1:
foodEditorFragment = new FoodEditorFragment(foodViewModel);
break;
default:
foodEditorFragment = new FoodEditorFragment(foodList.get(position),foodViewModel);
break;
}
FragmentTransaction transaction=getFragmentManager().beginTransaction();
transaction.replace(R.id.food_editor_frame,foodEditorFragment);
transaction.commit();
}
}
RecyclerView's adapter:
package com.coffeetech.kittycatch;
import android.app.Application;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public class FoodAdapter extends RecyclerView.Adapter<FoodAdapter.FoodViewHolder> {
//VARIABLE THAT CONTAINS THE FOOD LIST (array list)
public List<Food> foods;
int size;
private OnFoodcardClickListener onFoodcardClickListener;
//listener interface
public interface OnFoodcardClickListener{
void deleteFood(int position);
void onEdit(int position);
void decrease(int position);
void increase(int position);
void setSeekBar(int position, int progress);
}
public void setOnFoodcardClickListener(OnFoodcardClickListener activity){ //this is called in MainActivity
onFoodcardClickListener=activity;
}
public FoodAdapter(List<Food>foods){
this.foods=foods;
}
public static class FoodViewHolder extends RecyclerView.ViewHolder{
//VARIABLES FOR EACH WIDGET IN FOODCARD
TextView name,quantity; //to modify according to current food
ImageButton decreaseButton,increaseButton; //to hide or show
SeekBar seekbar; //according to current type
ImageButton deleteButton,editButton;
public FoodViewHolder(#NonNull View itemView, final OnFoodcardClickListener listener) {//'itemView' is the card
super(itemView);
name=itemView.findViewById(R.id.name);
quantity=itemView.findViewById(R.id.quantity);
decreaseButton=itemView.findViewById(R.id.decrease_button);
increaseButton=itemView.findViewById(R.id.increase_button);
seekbar=itemView.findViewById(R.id.seekbar);
deleteButton=itemView.findViewById(R.id.delete_button);
editButton=itemView.findViewById(R.id.edit_button);
editButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(listener!=null){
int position=getAdapterPosition();
if (position!= RecyclerView.NO_POSITION){
listener.onEdit(position);
}
}
}
});
deleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(listener!=null){
int position=getAdapterPosition();
if (position!= RecyclerView.NO_POSITION){
listener.deleteFood(position);
}
}
}
});
decreaseButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(listener!=null){
int position=getAdapterPosition();
if (position!= RecyclerView.NO_POSITION){
listener.decrease(position);
}
}
}
});
increaseButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(listener!=null){
int position=getAdapterPosition();
if (position!= RecyclerView.NO_POSITION){
listener.increase(position);
}
}
}
});
seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
if(listener!=null){
int position=getAdapterPosition();
if (position!= RecyclerView.NO_POSITION){
listener.setSeekBar(position,seekBar.getProgress());
}
}
}
});
}
}
#NonNull
#Override
public FoodViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.foodcard, parent, false); //inflating Foodcard
FoodViewHolder vw = new FoodViewHolder(v,onFoodcardClickListener);
return vw;
}
#Override
public void onBindViewHolder(#NonNull FoodViewHolder holder, int position) { //'holder' is the foodcard here
Food currentFood=foods.get(position);
holder.name.setText(currentFood.getName());
holder.quantity.setText(String.valueOf(currentFood.getQuantity()));
holder.seekbar.setProgress(currentFood.getQuantity());
//code to hide or show certain widgets based on food type
if(currentFood.getType()==0){ //for discrete food
holder.increaseButton.setVisibility(View.VISIBLE);
holder.decreaseButton.setVisibility(View.VISIBLE);
holder.quantity.setVisibility(View.VISIBLE);
holder.seekbar.setVisibility(View.GONE);
}else{// for continuous food
holder.increaseButton.setVisibility(View.GONE);
holder.decreaseButton.setVisibility(View.GONE);
holder.quantity.setVisibility(View.GONE);
holder.seekbar.setVisibility(View.VISIBLE);
}
}
#Override
public int getItemCount() {
return size;
}
//FUCNTION TO GET LIVE DATA HERE
public void setFoods (List<Food> foods){
this.foods=foods;
notifyDataSetChanged();
}
}
Food Editor Fragment:
package com.coffeetech.kittycatch;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;
public class FoodEditorFragment extends Fragment {
private TextView name,quantity,min_quantity;
private ImageButton save,cancel;
private RadioGroup radioGroup;
protected int t,mode;
protected Food food;
FoodViewModel foodViewModel;
public FoodEditorFragment() {
// Required empty public constructor
}
public FoodEditorFragment (Food food, FoodViewModel foodViewModel){
this.food=food;
this.foodViewModel=foodViewModel;
mode=1;
}
public FoodEditorFragment (FoodViewModel foodViewModel){
this.foodViewModel=foodViewModel;
this.food = new Food();
mode=0;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_food_editor, container, false);
name=view.findViewById(R.id.name_editor);
quantity=view.findViewById(R.id.quantity_editor);
min_quantity=view.findViewById(R.id.min_quantity_editor);
save=view.findViewById(R.id.save_button_editor);
cancel=view.findViewById(R.id.cancel_button_editor);
radioGroup=view.findViewById(R.id.radioGroup_editor);
if (mode==1){ //for editing Food
//CODE TO SETUP EDITOR ACCORDING TO INITIAL DETAILS
name.setText(food.getName());
quantity.setText(String.valueOf(food.getQuantity()));
min_quantity.setText(String.valueOf(food.getMin_quantity()));
t=food.getType();
if(t==0){//for discrete food
radioGroup.check(R.id.discrete_radioButton);
}else{//for cont food
radioGroup.check(R.id.cont_radioButton);
}
}
setButtons();
return view;
}
public void setButtons(){
save.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) { //USE BELOW 'food' TO PASS NEW DATA TO ACTIVITY
try {
if ((!name.getText().toString().isEmpty()) && ((radioGroup.getCheckedRadioButtonId() == R.id.discrete_radioButton) || (radioGroup.getCheckedRadioButtonId() == R.id.cont_radioButton))) {
food.setName(name.getText().toString());
food.setQuantity(Integer.parseInt(quantity.getText().toString()));
food.setMin_quantity(Integer.parseInt(min_quantity.getText().toString()));
if (radioGroup.getCheckedRadioButtonId() == R.id.discrete_radioButton) {
food.setType(0);
} else if (radioGroup.getCheckedRadioButtonId() == R.id.cont_radioButton) {
food.setType(1);
}
switch (mode){
case 0:
foodViewModel.insert(food);
break;
case 1:
foodViewModel.update(food);
break;
}
//CLOSE THE FRAGMENT
getFragmentManager().beginTransaction().remove(FoodEditorFragment.this).commit();
} else {
throw new Exception();
}
}catch (Exception e){
Toast.makeText(getContext(),"Please set all details",Toast.LENGTH_SHORT).show();
}
}
});
cancel.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) { //CODE IF USER PRESSES ON CANCEL
//CLOSE THE FRAGMENT
getFragmentManager().beginTransaction().remove(FoodEditorFragment.this).commit();
}
});
}
}
ViewModel I am using:
package com.coffeetech.kittycatch;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import java.util.ArrayList;
import java.util.List;
public class FoodViewModel extends AndroidViewModel {
private FoodRepository repository;
private LiveData<List<Food>> foods;
public FoodViewModel(#NonNull Application application) {
super(application);
repository = new FoodRepository(application);
foods=repository.getAll();
}
public void insert(Food food){
repository.insert(food);
}
public void update(Food food){
repository.update(food);
}
public void delete(Food food){
repository.delete(food);
}
public void deleteAll(){
repository.deleteAll();
}
public LiveData<List<Food>> getFoods(){
return foods;
}
public LiveData<List<Food>> getBuying () {return repository.getBuying();}
}
In FoodAdapter's getItemCount() method, You set itemcount static. You have to change it to foods.size()
I have fragment view pager in activity, that creates a number of pages of entered number before. In each fragment, i have recycler view that needs to update each time user moves to the relevant page. on resume() of each fragment I have getter of data from main activity.
What I am experiencing is while I'm going to next page first time it's not updated, but if after some page movements I would back to it, it is updated, with same resume() code.
I tried some delays after updating the data, it didn't help.
So if before updating data I would check every page it would work as I planned, but if the page was not created before (due to pager adapter) but I still use the same code for updating, it does not work.
I would be glad for some help, stacked on it for 2 days already.
Main Activity:
package com.slavafleer.tipcalculator02;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import com.slavafleer.tipcalculator02.recycler.PageHeaderAdapter;
import java.util.ArrayList;
public class ManualModeActivity extends AppCompatActivity implements
PageHeaderAdapter.Callbacks, DinerFragment.Callbacks {
private int mDinersAmount;
private ViewPager mViewPagerDiners;
private PageHeaderAdapter mHeaderAdapter;
private DinersPagerAdapter mDinersPagerAdapter;
private ArrayList<Order> mOrders;
public ArrayList<Order> getOrders() {
return mOrders;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manual_mode);
mOrders = new ArrayList<>();
// Get diners amount from previous activity
Intent intent = getIntent();
mDinersAmount = intent.getIntExtra(Constants.KEY_DINNERS_AMOUNT, 1);
// Initialise PageHeader Recycler
mHeaderAdapter = new PageHeaderAdapter(this, mDinersAmount, this);
final RecyclerView recyclerPageHeader = (RecyclerView) findViewById(R.id.recyclerViewPagerHeader);
final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
recyclerPageHeader.setLayoutManager(linearLayoutManager);
recyclerPageHeader.setAdapter(mHeaderAdapter);
// Initialise ViewPager
mViewPagerDiners = (ViewPager) findViewById(R.id.viewPagerDiners);
FragmentManager fragmentManager = getSupportFragmentManager();
mDinersPagerAdapter = new DinersPagerAdapter(fragmentManager, mDinersAmount);
mViewPagerDiners.setAdapter(mDinersPagerAdapter);
// ViewPager Listener - synchronise with headers recycler
mViewPagerDiners.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
// Gets position for selected page
#Override
public void onPageSelected(int position) {
mHeaderAdapter.selectItem(position);
linearLayoutManager.smoothScrollToPosition(recyclerPageHeader, null,position);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
}
// HeaderPage Adapter Callbacks
// Scroll ViewPager by clicked Header
#Override
public void onItemClick(int position) {
mViewPagerDiners.setCurrentItem(position, true);
}
// DinerFragment.OrderDialog.Callbacks
// Send data to PagerAdapter that would sent to each fragment
#Override
public void onDialogAddClick(Order order) {
Log.d("test", "onDialogAddClick");
mOrders.add(order);
mDinersPagerAdapter.updateOrders();
}
}
PagerAdapter
package com.slavafleer.tipcalculator02;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import java.util.ArrayList;
/**
* PagerAdapter for ManualModeActivity ViewPager
*/
public class DinersPagerAdapter extends FragmentPagerAdapter {
private int mDinersAmount;
private ArrayList<DinerFragment> mDinerFragments;
public DinersPagerAdapter(FragmentManager fm, int dinersAmount) {
super(fm);
mDinersAmount = dinersAmount;
mDinerFragments = new ArrayList<>();
}
#Override
public Fragment getItem(int position) {
// Insert diners amount to fragment
Bundle bundle = new Bundle();
bundle.putInt(Constants.KEY_DINNERS_AMOUNT, mDinersAmount);
bundle.putInt(Constants.KEY_CURRENT_PAGE, position);
DinerFragment dinerFragment = new DinerFragment();
dinerFragment.setArguments(bundle);
mDinerFragments.add(dinerFragment); // save fragments references
return dinerFragment;
}
// Diners amount + All
#Override
public int getCount() {
return mDinersAmount + 1;
}
// Update order list in each fragment of view pager
public void updateOrders() {
for(DinerFragment dinerFragment : mDinerFragments) {
dinerFragment.onResume();
}
}
}
Fragment
package com.slavafleer.tipcalculator02;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.slavafleer.tipcalculator02.recycler.OrdersAdapter;
import java.util.ArrayList;
/**
* Diner Fragment Class
*/
public class DinerFragment extends Fragment implements OrderDialog.Callbacks {
private ArrayList<Order> mOrders = new ArrayList<>();
private RecyclerView mRecyclerViewOrders;
private ImageView mImageViewAddOrderButton;
private int mDinersAmount;
private OrdersAdapter mOrdersAdapter;
private int mDinerId;
private Callbacks mCallbacks;
public DinerFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d("test", "onCreateView");
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_diner, container, false);
mCallbacks = (Callbacks) inflater.getContext();
// Get Diners Amount
final Bundle bundle = getArguments();
if(bundle != null) {
mDinersAmount = bundle.getInt(Constants.KEY_DINNERS_AMOUNT);
mDinerId = bundle.getInt(Constants.KEY_CURRENT_PAGE);
Log.d("test", "onCreateView " + mDinerId);
}
mRecyclerViewOrders = (RecyclerView) view.findViewById(R.id.recyclerViewOrders);
mOrdersAdapter = new OrdersAdapter(getActivity(), mOrders);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
mRecyclerViewOrders.setLayoutManager(linearLayoutManager);
mRecyclerViewOrders.setAdapter(mOrdersAdapter);
// Due to the bug, we could use just listener and not OnClick in Fragment
mImageViewAddOrderButton = (ImageView) view.findViewById(R.id.imageViewAddButton);
mImageViewAddOrderButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ArrayList<Integer> currentPage = new ArrayList<>();
currentPage.add(mDinerId);
OrderDialog orderDialog = new OrderDialog(getActivity(),
mDinersAmount, currentPage, DinerFragment.this);
orderDialog.show();
}
});
return view;
}
#Override
public void onResume() {
super.onResume();
Log.d("test", "onResume " + mDinerId);
ManualModeActivity activity = (ManualModeActivity) getActivity();
mOrders = activity.getOrders();
for(Order order : mOrders) {
Log.d("test", order.getPrice() + "");
}
int size = mOrders.size();
mOrdersAdapter.notifyDataSetChanged();
mRecyclerViewOrders.smoothScrollToPosition(size);
}
// Blank OrderDialog.Callbacks
// used due to Implements for creation OrderDialog
#Override
public void onDialogAddClick(Order order) {
mCallbacks.onDialogAddClick(order);
}
public interface Callbacks {
void onDialogAddClick(Order order);
}
}
Instead of using onResume use the onPageSelected method and used the passed fragment for context to call the updating method in the fragment.
Finally what I did is replacing mOrdersAdapter.notifyDataSetChanged() on recreating the adapter. I understand that is not a very smart solution but visually it does work as I needed.
I would not mark it as answer cause I still want to know why it acts like I wrote before.
Thanks again.
// mOrdersAdapter.notifyDataSetChanged();
mOrdersAdapter = new OrdersAdapter(getActivity(), mOrders);
mRecyclerViewOrders.setAdapter(mOrdersAdapter);
ViewPager adapter creates 2 fragments at the same time. for instance when you're on page 0, it also creates page 1(lifecycle for page 1: OnCreate->OnCreateView->OnResume->OnPause->OnResume).When you swipe to page 1 only method that is being called is SetMenuVisibility. So in order to update data for page 1 is to call setMenuVisibilty inside of Fragment:
#Override
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
if(menuVisible && isResumed()){
settingAdapter();
}
}
So here inside of settingAdapter i reloaded data.
so far,it's the best trick i made toward fragments with recyclerviews inside viewpager. But still there are few milliseconds delay for populating fragment with new data)))
I hope this will help you
My App has CustomPagerAdapter where I created fragments where has a dialog with editText. The main idea to transfer this text to MainActivity. I tried to do eventBus: dialog->fragment->pagerAdapter->activity. I can do while skipping pagerAdapter, but I want to keep all "stations" in order.
I created an interface in the dialog and connected it with fragment by callbacks. Same did between fragment and adapter. But now I need to add Callbacks parameter while creates the Adapter, and I receive this error
And part of my code:
package com.slavafleer.tipcalculator02;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import java.util.ArrayList;
/**
* PagerAdapter for ManualModeActivity ViewPager
*/
public class DinersPagerAdapter extends FragmentPagerAdapter implements
DinerFragment.Callbacks {
private Callbacks mCallbacks;
private int mDinersAmount;
public DinersPagerAdapter(FragmentManager fm, int dinersAmount, Callbacks callbacks) {
super(fm);
mCallbacks = callbacks;
mDinersAmount = dinersAmount;
}
#Override
public Fragment getItem(int position) {
// Insert diners amount to fragment and DinerId
Bundle bundle = new Bundle();
bundle.putInt(Constants.KEY_DINNERS_AMOUNT, mDinersAmount);
bundle.putInt(Constants.KEY_DINER_ID, position);
DinerFragment dinerFragment = new DinerFragment();
dinerFragment.setArguments(bundle);
return dinerFragment;
}
// Diners amount + All
#Override
public int getCount() {
return mDinersAmount + 1;
}
// DinerFragment.OrderDialog.Callbacks
#Override
public void onDialogAddClick(ArrayList<Boolean> selectedIdsList, double orderPrice) {
mCallbacks.onDialogAddClick(selectedIdsList, orderPrice);
}
public interface Callbacks {
void onDialogAddClick(ArrayList<Boolean> selectedIdsList, double orderPrice);
}
}
and
package com.slavafleer.tipcalculator02;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import com.slavafleer.tipcalculator02.recycler.PageHeaderAdapter;
import java.util.ArrayList;
public class ManualModeActivity extends AppCompatActivity implements
PageHeaderAdapter.Callbacks, DinerFragment.Callbacks {
private int mDinersAmount;
private ViewPager mViewPagerDiners;
private PageHeaderAdapter mHeaderAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manual_mode);
// Get diners amount from previous activity
Intent intent = getIntent();
mDinersAmount = intent.getIntExtra(Constants.KEY_DINNERS_AMOUNT, 1);
// Initialise PageHeader Recycler
mHeaderAdapter = new PageHeaderAdapter(this, mDinersAmount, this);
final RecyclerView recyclerPageHeader = (RecyclerView) findViewById(R.id.recyclerViewPagerHeader);
final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
recyclerPageHeader.setLayoutManager(linearLayoutManager);
recyclerPageHeader.setAdapter(mHeaderAdapter);
// Initialise ViewPager
mViewPagerDiners = (ViewPager) findViewById(R.id.viewPagerDiners);
FragmentManager fragmentManager = getSupportFragmentManager();
DinersPagerAdapter dinersPagerAdapter = new DinersPagerAdapter(fragmentManager, mDinersAmount, this);
mViewPagerDiners.setAdapter(dinersPagerAdapter);
// ViewPager Listener - synchronise with headers recycler
mViewPagerDiners.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
// Gets position for selected page
#Override
public void onPageSelected(int position) {
mHeaderAdapter.selectItem(position);
linearLayoutManager.smoothScrollToPosition(recyclerPageHeader, null,position);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
}
// HeaderPage Adapter Callbacks
// Scroll ViewPager by clicked Header
#Override
public void onItemClick(int position) {
mViewPagerDiners.setCurrentItem(position, true);
}
// DinerFragment.OrderDialog.Callbacks
// Receive new order from Dialog.
// Send back to all fragments (via PagerAdapter), where would be added to Recycler
#Override
public void onDialogAddClick(ArrayList<Boolean> selectedIdsList, double orderPrice) {
}
}
Thanks for any help.
Your PagerAdapter is waiting for a DinersPagerAdapter.Callbacks as the 3rd parameter.
However your activity doesn't implement it, therefore this is not recognized.
Change your activity to
public class ManualModeActivity extends AppCompatActivity implements
PageHeaderAdapter.Callbacks, DinerFragment.Callbacks, DinersPagerAdapter.Callbacks {
//Everything else...
//Don't forget to override
#Override
void onDialogAddClick(ArrayList<Boolean> selectedIdsList, double orderPrice) {
//Whatever has to be done here
}
i am looking for a second opinion. I have a class that extends the Fragment class and contains a ViewPager and MaterialTabs. I need to find a workaround on a problem and I would like to remove tabs from ViewPager and add just one (fragment too) and so on.
For instance, i start with two tabs and on an action i want to remove those tabs and add a new one and on a different action to re-add those two tabs.
Is this plain stupid or it has a shot? Thank you.
Below is my class that i will work on:
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v13.app.FragmentStatePagerAdapter;
import android.support.v4.app.ActivityCompat;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.SimpleOnPageChangeListener;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import it.neokree.materialtabs.MaterialTab;
import it.neokree.materialtabs.MaterialTabHost;
import it.neokree.materialtabs.MaterialTabListener;
/**
*
*/
public class PuzzlesTabActivity extends Fragment implements MaterialTabListener {
public static int NUM_TABS = 2;
public static boolean currentlyRunning;
private MaterialTabHost tabHost;
private ViewPager pager;
private PuzzlesTabActivity.ViewPagerAdapter adapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(layout.puzzle_tab_layout, container, false);
if (Utils.isNetworkAvailable(this.getActivity())) {
this.tabHost = (MaterialTabHost) rootView.findViewById(id.myTabHost);
this.pager = (ViewPager) rootView.findViewById(id.myPager);
try {
this.pager.setPadding(0, 5, 0, 0);
// init view pager
this.adapter = new PuzzlesTabActivity.ViewPagerAdapter(this.getActivity().getFragmentManager());
this.pager.setAdapter(this.adapter);
this.pager.setOnPageChangeListener(new SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// when user do a swipe the selected tab change
PuzzlesTabActivity.this.tabHost.setSelectedNavigationItem(position);
}
});
this.pager.setCurrentItem(0);
// add tabs
for (int i = 0; i < this.adapter.getCount(); i++) {
this.tabHost.addTab(this.tabHost.newTab().setText(this.adapter.getPageTitle(i)).setTabListener(this));
}
} catch (Exception e) {
Log.e(Constants.TAG, e.getMessage());
}
} else {
ActivityCompat.startActivity(this.getActivity(), new Intent(this.getActivity(), MainActivity.class), null);
}
return rootView;
}
#Override
public void onTabSelected(MaterialTab tab) {
this.pager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabReselected(MaterialTab tab) {
}
#Override
public void onTabUnselected(MaterialTab tab) {
}
private class ViewPagerAdapter extends FragmentStatePagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
public Fragment getItem(int num) {
switch (num) {
case 0:
PuzzlesFragment aFragment = new PuzzlesFragment();
return aFragment;
case 1:
FavFragment nFragment = new FavFragment();
return nFragment;
default:
return new PuzzlesFragment();
}
}
#Override
public int getCount() {
return PuzzlesTabActivity.NUM_TABS;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return PuzzlesTabActivity.this.getResources().getString(string.new_puzzles);
case 1:
return PuzzlesTabActivity.this.getResources().getString(string.fav_puzzles);
default:
return PuzzlesTabActivity.this.getResources().getString(string.new_puzzles);
}
}
}
public void onPause() {
super.onPause();
SharedPreferences prefs = this.getActivity().getSharedPreferences(Utils.PREFS_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("lastActivity", this.getClass().getName());
editor.commit();
}
#Override
public void onStart() {
super.onStart();
PuzzlesTabActivity.currentlyRunning = true;
}
#Override
public void onStop() {
PuzzlesTabActivity.currentlyRunning = false;
super.onStop();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item);
}
}
Yes, this is fine. ViewPager already does something like this in a normal use case. It works by creating the fragment to the left, the current fragment, and the fragment to the right, as you swipe (so only three exist at a time).
As for adding and removing the tabs, that is fine to do as well. Just make sure to handle swapping for the new tab's fragment in the ViewPager.
I have two fragments, lets call them Fragment A and Fragment B, which are a part of a NavigationDrawer (this is the activity they a bound to). In Fragment A I have a button. When this button is pressed, I would like another item added to the ListView in Fragment B.
What is the best way to do this? Use Intents, SavedPreferences, making something public(?) or something else?
EDIT 5: 20/7/13 This is with srains latest code
This is the NavigationDrawer that I use to start the fragments:
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class Navigation_Drawer extends FragmentActivity {
public DrawerLayout mDrawerLayout; // Creates a DrawerLayout called_.
public ListView mDrawerList;
public ActionBarDrawerToggle mDrawerToggle;
private CharSequence mDrawerTitle;
private CharSequence mTitle;
private String[] mNoterActivities; // This creates a string array called _.
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
// Just setting up the navigation drawer
} // End of onCreate
// Removed the menu
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
selectItem(position);
}
}
private void selectItem(int position) {
FragmentManager fragmentManager = getSupportFragmentManager();
if (position == 0) {
Fragment qnfragment = new QuickNoteFragment();
((FragmentBase) qnfragment).setContext(this);
Bundle args = new Bundle(); // Creates a bundle called args
args.putInt(QuickNoteFragment.ARG_nOTERACTIVITY_NUMBER, position);
qnfragment.setArguments(args);
fragmentManager.beginTransaction()
.replace(R.id.content_frame, qnfragment).commit();
} else if (position == 3) {
Fragment pendViewPager = new PendViewPager(); // This is a ViewPager that includes HistoryFragment
((FragmentBase) pendViewPager).setContext(this);
Bundle args = new Bundle();
pendViewPager.setArguments(args);
fragmentManager.beginTransaction()
.replace(R.id.content_frame, pendViewPager).commit();
}
// Update title etc
}
#Override
protected void onPostCreate(Bundle savedInstanceState) { // Used for the NavDrawer toggle
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
mDrawerToggle.syncState();
}
#Override
public void onConfigurationChanged(Configuration newConfig) { // Used for the NavDrawer toggle
super.onConfigurationChanged(newConfig);
// Pass any configuration change to the drawer toggles
mDrawerToggle.onConfigurationChanged(newConfig);
}
}
This is QuickNoteFragment:
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class QuickNoteFragment extends FragmentBase implements OnClickListener {
public static final String ARG_nOTERACTIVITY_NUMBER = "noter_activity";
Button b_create;
// removed other defining stuff
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.quicknote, container, false);
int i = getArguments().getInt(ARG_nOTERACTIVITY_NUMBER);
String noter_activity = getResources().getStringArray(
R.array.noter_array)[i];
FragmentManager fm = getFragmentManager();
setRetainInstance(true);
b_create = (Button) rootView.findViewById(R.id.qn_b_create);
// Removed other stuff
getActivity().setTitle(noter_activity);
return rootView;
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.qn_b_create:
String data = "String data";
DataModel.getInstance().addItem(data);
break;
}
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// TODO Auto-generated method stub
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
}
}
public interface OnItemAddedHandler { // Srains code
public void onItemAdded(Object data);
}
}
This is HistoryFragment (Remember it is part of a ViewPager):
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.RiThBo.noter.QuickNoteFragment.OnItemAddedHandler;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;
public class HistoryFragment extends FragmentBase implements OnItemAddedHandler {
ListView lv;
List<Map<String, String>> planetsList = new ArrayList<Map<String, String>>();
SimpleAdapter simpleAdpt;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.history, container, false);
initList();
ListView lv = (ListView) view.findViewById(R.id.listView);
simpleAdpt = new SimpleAdapter(getActivity(), planetsList,
android.R.layout.simple_list_item_1, new String[] { "planet" },
new int[] { android.R.id.text1 });
lv.setAdapter(simpleAdpt);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parentAdapter, View view,
int position, long id) {
// We know the View is a TextView so we can cast it
TextView clickedView = (TextView) view;
Toast.makeText(
getActivity(),
"Item with id [" + id + "] - Position [" + position
+ "] - Planet [" + clickedView.getText() + "]",
Toast.LENGTH_SHORT).show();
}
});
registerForContextMenu(lv);
return view;
}
private void initList() {
// We populate the planets
planetsList.add(createPlanet("planet", "Mercury"));
planetsList.add(createPlanet("planet", "Venus"));
planetsList.add(createPlanet("planet", "Mars"));
planetsList.add(createPlanet("planet", "Jupiter"));
planetsList.add(createPlanet("planet", "Saturn"));
planetsList.add(createPlanet("planet", "Uranus"));
planetsList.add(createPlanet("planet", "Neptune"));
}
private HashMap<String, String> createPlanet(String key, String name) {
HashMap<String, String> planet = new HashMap<String, String>();
planet.put(key, name);
return planet;
}
// We want to create a context Menu when the user long click on an item
#Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
AdapterContextMenuInfo aInfo = (AdapterContextMenuInfo) menuInfo;
// We know that each row in the adapter is a Map
HashMap map = (HashMap) simpleAdpt.getItem(aInfo.position);
menu.setHeaderTitle("Options for " + map.get("planet"));
menu.add(1, 1, 1, "Details");
menu.add(1, 2, 2, "Delete");
}
#Override
public void onItemAdded(Object data) {
// to add item
String string = String.valueOf(data);
Toast.makeText(getContext(), "Data: " + string, Toast.LENGTH_SHORT).show();
planetsList.add(createPlanet("planet", string));
}
#Override
public void onStart() {
super.onStart();
DataModel.getInstance().setOnItemAddedHandler(this);
}
}
This is FragmentBase:
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
public class FragmentBase extends Fragment {
private FragmentActivity mActivity; // I changed it to FragmentActivity because Activity was not working, and my `NavDrawer` is a FragmentActivity.
public void setContext(FragmentActivity activity) {
mActivity = mActivity;
}
public FragmentActivity getContext() {
return mActivity;
}
}
This is DataModel:
import android.util.Log;
import com.xxx.xxx.QuickNoteFragment.OnItemAddedHandler;
public class DataModel {
private static DataModel instance;
private static OnItemAddedHandler mOnItemAddHandler;
public static DataModel getInstance() {
if (null == instance) {
instance = new DataModel();
}
return instance;
}
public void setOnItemAddedHandler(OnItemAddedHandler handler) {
mOnItemAddHandler = handler;
}
public void addItem(Object data) {
if (null != mOnItemAddHandler)
mOnItemAddHandler.onItemAdded(data);
else {
Log.i("is context null?", "yes!");
}
}
}
Thank you
I suggest you to use interface and MVC, that will make your code much more maintainable.
First you need an interface:
public interface OnItemAddedHandler {
public void onItemAdded(Object data);
}
Then, you will need a data model:
public class DataModel {
private static DataModel instance;
private static OnItemAddedHandler mOnItemAddHandler;
public static DataModel getInstance() {
if (null == instance) {
instance = new DataModel();
}
return instance;
}
public void setOnItemAddedHandler(OnItemAddedHandler handler) {
mOnItemAddHandler = handler;
}
public void addItem(Object data) {
if (null != mOnItemAddHandler)
mOnItemAddHandler.onItemAdded(data);
}
}
When you click the button, you can add data into the datamodel:
Object data = null;
DataModel.getInstance().addItem(data);
Then, the FragmentB implements the interface OnItemAddedHandler to add item
public class FragmentB implements OnItemAddedHandler {
#Override
public void onItemAdded(Object data) {
// to add item
}
}
also, When the FragmentB start, you should register itself to DataModel:
public class FragmentB implements OnItemAddedHandler {
#Override
public void onItemAdded(Object data) {
// to add item
}
#Override
protected void onStart() {
super.onStart();
DataModel.getInstance().setOnItemAddedHandler(this);
}
}
You also can add DataModel.getInstance().setOnItemAddedHandler(this); to the onCreate method of FragmentB
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DataModel.getInstance().setOnItemAddedHandler(this);
// do other things
}
update
you can send string simply:
String data = "some string";
DataModel.getInstance().addItem(data);
then on FragementB
public class FragmentB implements OnItemAddedHandler {
#Override
public void onItemAdded(Object data) {
// get what you send into method DataModel.getInstance().addItem(data);
String string = String.valueOf(data);
}
}
update
OK. You have add DataModel.getInstance().setOnItemAddedHandler(this) in onCreateView method, so there is no need to add it in onStart() method. You can remove the whole onStart method.
onStart will be called on the fragment start, we do not need to call it in onCreateView. And on onItemAdded will be call when call the method DataModel.getInstance().addItem(data), we do not need to call it in onCreateView neither.
so, you can remove the code below from onCreateView method:
DataModel.getInstance().setOnItemAddedHandler(this);
// remove the methods below
// onItemAdded(getView());
// onStart();
You have another fragment where there is a button, you can add the codes below in the clickhandler function:
String data = "some string";
DataModel.getInstance().addItem(data);
update3
I think the HistoryFragment has been detached after you when to QuickNoteFragment
You can add code to HistoryFragment to check:
#Override
public void onDetach() {
super.onDetach();
Log.i("test", String.format("onDetach! %s", getActivity() == null));
}
update4
I think HistoryFragment and QuickNoteFragment should has an parent class, named FragmentBase:
public class FragmentBase extends Fragment {
private Activity mActivity;
public void setContext(Activity activity) {
mActivity = mActivity;
}
public Activity getContext() {
return mActivity;
}
}
HistoryFragment and QuickNoteFragment extends FragmentBase. Then when you switch between them, you can call setContext to set a Activity, like:
private void selectItem(int position) {
FragmentManager fragmentManager = getSupportFragmentManager();
if (position == 0) {
Fragment qnfragment = new QuickNoteFragment();
qnfragment.setContext(this);
// ...
} else if (position == 1) {
Fragment pagerFragment = new RemViewPager();
pagerFragment.setContext(this);
// ...
}
}
now, we can get a non-null activity in HistoryFragment by calling getContext, so we can change onItemAdded method to:
#Override
public void onItemAdded(Object data) {
// to add item
String string = String.valueOf(data);
Toast.makeText(getContext(), "Data: " + string, Toast.LENGTH_SHORT).show();
planetsList.add(createPlanet("planet", string));
}
I hope this would work.
Some good design principals:
An activity can know everything pubic about any Fragment it contains.
A Fragment should not know anything about the specific Activities that contain it.
A Fragment should NEVER know about other fragments that may or may not be contained in the Parent activity.
A suggested approach (informal design pattern) based on these principles.
Each fragment should declare an interface to be implemented by its parent activity:
public class MyFragment extends Fragment
{
public interface Parent
{
void onMyFragmentSomeAction();
}
private Parent mParent;
public onAttach(Activity activity)
{
mParent = (Parent) activity;
}
// This would actually be in a listener. Simplifying to save typing.
void onSomeButtonClick(View button)
{
mParent.onMyFragmentSomeAction();
}
}
And the activity should implement the appropriate interfaces for all of its contained fragments.
public class MyActivity extends Activity
implements MyFragment.Parent,
YourFragment.Parent,
HisFragment.Parent
{
[usual Activity code]
void onMyFragmentSomeAction()
{
if yourFragment is showing
{
yourFragment.reactToSomeAction();
}
if hisFragment is showing
{
hisFragment.observeThatSomeActionHappened();
}
[etc]
}
The broadcast approach is good, too, but it's pretty heavyweight and it requires the target Fragment to know what broadcasts will be sent by the source Fragment.
Use Broadcast. Send a boradcast from A, and B will receive and handle it.
Add a public method to B, for example, public void addListItem(), which will add data to listview in B. Fragment A try to find the instance of Fragment B using FragmentMananger.findFragmentByTag() and invoke this method.