I have a MainActivity hosted a fragment called FragmentOne
and there is another Activity named DetailPagerActivity hosted another fragment called DetailFragment
i want to use a method from DetailFragment in FragmentOne
.. how can i do this?
i tried to use FragmentManager and so on, but failed
i want to call getDetailReport() from DeatilFragment in onSwiped > FragmentOne
here is my FragmentOne :
public class FragmentOne extends Fragment {
private RecyclerView mDetailRecyclerView;
private DetailAdapter mAdapter;
private boolean mNumberVisible;
private View view;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true); }
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_one_layout,
container, false);
mDetailRecyclerView = (RecyclerView)
view.findViewById(R.id.detail_recycler_view);
..
..
return view;
}
#Override
public void onResume() {
super.onResume();
updateUI();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(SAVED_NUMBER_VISIBLE, mNumberVisible);
}
private class DetailHolder extends RecyclerView.ViewHolder
implements View.OnClickListener, View.OnLongClickListener {
private TextView mTitleTextView;
private Detail mDetail;
private RatingBar mRatingBar;
public DetailHolder(LayoutInflater inflater, ViewGroup parent) {
super(inflater.inflate(R.layout.list_item_detail,
parent, false));
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
mTitleTextView = (TextView) itemView.findViewById(R.id.detail_title);
mRatingBar = (RatingBar) itemView.findViewById(R.id.ratingBar);
}
public void bind(Detail detail) {
mDetail = detail;
mTitleTextView.setText(mDetail.getTitle());
mRatingBar.setRating(mDetail.getRate()); }
#Override
public void onClick(View view) {
Intent intent = DetailPagerActivity.newIntent(getActivity(),
mDetail.getId());
startActivity(intent); }
return true;
}
}
private class DetailAdapter extends RecyclerView.Adapter<DetailHolder> {
private List<Detail> mDetails;
public DetailAdapter(List<Detail> details) {
mDetails = details;
}
#Override
public DetailHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater =
LayoutInflater.from(getActivity());
return new DetailHolder(layoutInflater, parent);
}
#Override
public void onBindViewHolder(DetailHolder holder, int position) {
Detail detail = mDetails.get(position);
holder.bind(detail);
}
#Override
public int getItemCount() {
return mDetails.size();
}
public void setDetails(List<Detail> details) {
mDetails = details;
////////////////////////////////////////////////////////////////////////////////////////////////////////
ItemTouchHelper.SimpleCallback simpleCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
private Detail mDetail;
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(final RecyclerView.ViewHolder viewHolder, int direction) {
final int position = viewHolder.getAdapterPosition(); //get position which is swipe
if (direction == ItemTouchHelper.LEFT) { //if swipe left
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); //alert for confirm to delete
builder.setMessage("Are you sure to delete?"); //set message
builder.setPositiveButton("REMOVE", new DialogInterface.OnClickListener() { //when click on DELETE
#Override
public void onClick(DialogInterface dialog, int which) {
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("text/plain");
i.putExtra(Intent.EXTRA_TEXT, getDetailReport());
i.putExtra(Intent.EXTRA_SUBJECT,
getString(R.string.detail_report_subject));
i = Intent.createChooser(i, getString(R.string.send_report));
startActivity(i);
//////////////////////////////////////////////////////
// mAdapter.notifyItemRemoved(position); //item removed from recylcerview
// DetailLab.get(getActivity()).deleteDetail(mDetail);
// updateUI();
// updateNumbers();
dialog.dismiss();
// return;
}
}).setNegativeButton("CANCEL", new DialogInterface.OnClickListener() { //not removing items if cancel is done
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builder.show(); //show alert dialog
}
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleCallback);
itemTouchHelper.attachToRecyclerView(mDetailRecyclerView); //set swipe to recylcerview
}
}
}
and this is my DetailFargment:
public class DetailFragment extends Fragment {
private static final String ARG_DETAIL_ID = "detail_id";
private static final String DIALOG_DATE = "DialogDate";
private static final String DIALOG_PHOTO = "DialogPhoto";
private static final int REQUEST_DATE = 0;
private static final int REQUEST_PHOTO = 2;
..
..
public static DetailFragment newInstance (UUID detailId) {
Bundle args = new Bundle();
args.putSerializable(ARG_DETAIL_ID, detailId);
DetailFragment fragment = new DetailFragment();
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
UUID detailId = (UUID) getArguments().getSerializable(ARG_DETAIL_ID);
mDetail = DetailLab.get(getActivity()).getDetail(detailId);
mPhotoFile = DetailLab.get(getActivity()).getPhotoFile(mDetail);
}
#Override
public void onPause() {
super.onPause();
DetailLab.get(getActivity()).updateDetail(mDetail);
}
#Override
public View onCreateView (LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_detail,
container, false);
..
..
/////////////////////////// Report Button
mReportButton = (Button) v.findViewById(R.id.detail_report);
mReportButton.setOnClickListener(new View.OnClickListener() {
public void onClick (View v) {
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("text/plain");
i.putExtra(Intent.EXTRA_TEXT, getDetailReport());
i.putExtra(Intent.EXTRA_SUBJECT,
getString(R.string.detail_report_subject));
i = Intent.createChooser(i, getString(R.string.send_report));
startActivity(i);
}
});
..
..
return v;
}
#Override
public void onResume() {
super.onResume();
}
//////////////////////////////////// REPORT
private String getDetailReport() {
String dateFormat = "EEE, MMM dd";
String dateString = DateFormat.format(
dateFormat, mDetail.getDate()).toString();
String rateString = null;
String report = getString(R.string.detail_report,
mDetail.getTitle(), dateString,
rateString, rateString
);
return report;
}
..
..}
You should move the getDetailsReport() out of the fragment and into some sort of data/utility class - then you can call it from both places. Data should be separate from the UI especially if you need to use it in more than one Activity/Fragment.
If you want to communicate between unrelated activities/fragments, the easiest way is to use a Broadcast / BroadcastReceiver. This is usually used to notify the other activity/fragment that something needs to be updated - as opposed to trying to query the other activity/fragment for data. Usually the fragment/activity will update if it receives a broadcast or be in a good state if it is initialized from scratch.
The issue you will run into when making activities/fragment rely on other activities/fragment is that the one not being shown can be cleaned up - so you can't rely on the action actually happening.
Related
I am implementing recycler view using MVVM architecture. The data is getting populated in recycler view for the first time when fragment is loaded but after coming back to the fragment after back press it does not load list after resuming fragment.
Here, is my implementation for Fragment HomeFragment.java
public class HomeFragment extends Fragment {
private HomeViewModel homeViewModel;
private RecyclerView rv;
private ProgressBar pb;
private DataAdapter adapter;
private List<DataModel> modelList;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_home, container, false);
rv = v.findViewById(R.id.rv_home);
pb = v.findViewById(R.id.home_pb);
return v;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
modelList = new ArrayList<>();
Log.d("Home Frag", "onViewCreated: called again after back");
rv.setLayoutManager(new GridLayoutManager(getActivity(), 2));
rv.setHasFixedSize(true);
pb.setVisibility(View.VISIBLE);
homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class);
homeViewModel.getUserMutableLiveData().observe(Objects.requireNonNull(getActivity()), (userListUpdateObserver));
pb.setVisibility(View.GONE);
view.setFocusableInTouchMode(true);
view.requestFocus();
view.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
Log.i("HomeFragment", "keyCode: " + keyCode);
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
Log.i("HomeFragment", "keyCode: " + keyCode);
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("Logout");
builder.setMessage("Are you sure you want to Logout?");
// add the buttons
builder.setPositiveButton("Yes",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
getActivity().finish();
}
});
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
// create and show the alert dialog
AlertDialog dialog = builder.create();
dialog.show();
return true;
}
return false;
}
});
}
Observer<List<Category>> userListUpdateObserver = new Observer<List<Category>>() {
#Override
public void onChanged(final List<Category> userArrayList) {
for (int i = 0; i < userArrayList.size(); i++) {
modelList.add(new DataModel(userArrayList.get(i).getTitle(), userArrayList.get(i).getImage(), userArrayList.get(i).getId(), 0));
}
adapter = new DataAdapter(getContext(), modelList, new DataAdapter.RecyclerViewClickListener() {
#Override
public void onClick(View view, int position) {
homeViewModel.selectedId(modelList.get(position).getId());
homeViewModel.selectedString(modelList.get(position).getText());
SubCategoryFragment subCategoryFragment = new SubCategoryFragment();
ManageFragments.replaceFragment((FragmentActivity) getContext(), subCategoryFragment);
}
});
rv.setLayoutManager(new GridLayoutManager(getActivity(), 2));
rv.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
};
}
Here is my ViewModel class HomeViewModel.java
public class HomeViewModel extends ViewModel {
private MutableLiveData<List<Category>> userLiveData;
public static final MutableLiveData<Integer> selectedItemId = new MutableLiveData<Integer>();
private ApiInterface apiInterface;
public static final MutableLiveData<String> selectedString = new MutableLiveData<String>();
public HomeViewModel() {
userLiveData = new MutableLiveData<List<Category>>();
init();
}
public void selectedId(Integer id) {
selectedItemId.setValue(id);
}
public void selectedString(String name) {
selectedString.setValue(name);
}
MutableLiveData<List<Category>> getUserMutableLiveData() {
return userLiveData;
}
private void init() {
populateList();
}
private void populateList() {
apiInterface = ApiRequest.createService(ApiInterface.class);
Call<MainCategoryModel> call = apiInterface.getCategory();
call.enqueue(new Callback<MainCategoryModel>() {
#Override
public void onResponse(Call<MainCategoryModel> call, Response<MainCategoryModel> response) {
userLiveData.setValue(response.body().getData().getCategory());
}
#Override
public void onFailure(Call<MainCategoryModel> call, Throwable t) {
Log.d("TAG", "onFailure: " + t.getMessage());
}
});
}
}
Thank you.
The problem is with this line:
homeViewModel.getUserMutableLiveData().observe(Objects.requireNonNull(getActivity()), (userListUpdateObserver));
You're using the activity as the LifecycleOwner. That is always the wrong LifecycleOwner to use from within onViewCreated(). Instead, you need to use getViewLifecycleOwner() - the LifecycleOwner associated with the Fragment's view.
homeViewModel.getUserMutableLiveData().observe(getViewLifecycleOwner(),
userListUpdateObserver);
What is happening here is, on HomeFragment creation onViewCreated is getting called and so its content. On Recycle view, there is no adapter attached in fragment life cycle. You are attaching adapter to your Recycle View in an observer which will be called but asynchronously. Hence you are getting No Adapter Attached: skipping layout error.
You only need the observer to update the changes to your adapter's list.
To fix it you need to add adapter on Recycle View in a fragment life cycle such as onViewCreated method. Please find the changes in adapter code as follows -
public class CategoryAdapter extends RecyclerView.Adapter<CategoryAdapter.CustomViewHolder> {
private Context context;
private List<Category> categoryList;
private CategoryClickListener categoryClickListener;
public CategoryAdapter(Context context) {
this.context = context;
}
public interface CategoryClickListener {
//modify the method according to your requirement
void onCategoryClick(String id);
}
public void setUpCategoryListener(CategoryClickListener categoryClickListener ) {
this.categoryClickListener = categoryClickListener ;
}
public CustomViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new CustomViewHolder(LayoutInflater.from(context).inflate(R.layout.item_layout, parent, false));
}
#Override
public void onBindViewHolder(#NonNull CustomViewHolder holder, int position) {
// setup binding of views
}
public void setCategoryList(List<Category> categoryList) {
this.categoryList = categoryList;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
if (categoryList!= null)
return categoryList.size();
else
return 0;
}
public class CustomViewHolder extends RecyclerView.ViewHolder {
//item variable intializer
public CustomViewHolder(#NonNull View itemView) {
super(itemView);
// find views here from item
//putting click listener to register a click on item
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
categoryClickListener.onCategoryClick(categoryList.get(getAdapterPosition()).getId());
}
});
}
}
}
Now you can create adapter as -
//make adapter as global variable of class;
CategoryAdapter categoryAdapter;
//in onViewCreated()
rv.setLayoutManager(new GridLayoutManager(getActivity(), 2));
CategoryAdapter categoryAdapter = new CategoryAdapter(getContext());
rv.setAdapter(adapter);
categoryAdapter.setUpCategoryListener(new CategoryAdapter.CategoryClickListener(){
#Override
public void onCategoryClick(String id) {
//here you can get the selected item id or any value you require
// your fragment transaction as well
}
});
and under an observer, you can simply use setCategoryList() method of CategoryAdapter-
Observer<List<Category>> userListUpdateObserver = new Observer<List<Category>>() {
#Override
public void onChanged(final List<Category> userArrayList) {
categoryAdapter.setCategoryList(userArrayList);
}
};
No need to call notifyDataSetChanged() because that has been taken care undersetCategoryList() already.
Even we have take care of NullPointerException on userArrayList for adapter under getItemCount()
Happy Coding !
Hello I am having a hard time to implement this,I havent found any example to do it.I have gone up to this point myself but I am stuck!Any help would be appreciated
public class JobsFragment extends Fragment{
private List<JobsLists> jobsLists;
private RecyclerView recycle;
private FirebaseFirestore fb;
private FirestoreRecyclerAdapter<JobsLists, JobsViewHolder> adapter;
private static final String TAG ="" ;
public JobsFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View jobsView = inflater.inflate(R.layout.fragment_jobs, container, false);
recycle = jobsView.findViewById(R.id.recycle);
recycle.setLayoutManager(new LinearLayoutManager(this.getActivity()));
fb = FirebaseFirestore.getInstance();
Query query =
recycle.setItemAnimator(new DefaultItemAnimator());
FirestoreRecyclerOptions<JobsLists> options = new FirestoreRecyclerOptions.Builder<JobsLists>()
.setQuery(query, JobsLists.class)
.build();
adapter = new FirestoreRecyclerAdapter<JobsLists, JobsViewHolder>(options) {
#Override
protected void onBindViewHolder(#NonNull JobsViewHolder holder, final int position,#NonNull JobsLists jobsmodel) {
jobsmodel = jobsLists.get(position);
holder.setTitle(jobsmodel.getTitle());
holder.cv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = v.getContext();
// Intent intent = new Intent(context, JobView.class);
// intent.putExtra("title", .get(position));
// intent.putExtra("description", .get(position));
// context.startActivity(intent);
Toast.makeText(context,"clicked="+ jobsmodel.getTitle(position)),Toast.LENGTH_SHORT).show();
}
});
}
#NonNull
#Override
public JobsViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.custom_card, parent, false);
return new JobsViewHolder(view);
}
};
recycle.setAdapter(adapter);
return jobsView;
}
private class JobsViewHolder extends RecyclerView.ViewHolder {
private View view;
private CardView cv;
JobsViewHolder(final View itemView) {
super(itemView);
view = itemView;
cv = itemView.findViewById(R.id.job_post);
}
private TextView ti;
public void setTitle(String name) {
ti = view.findViewById(R.id.title);
ti.setText(name);
}
}
#Override
public void onStart() {
super.onStart();
adapter.startListening();
}
#Override
public void onStop() {
super.onStop();
if (adapter != null) {
adapter.stopListening();
}
} }
And I cant really pass params to the fragment constructor as many examples suggest!
I have seen examples where they implement OnclickListener but I don't know why it doesnt work in my class.Please give me some directions ,I am trying to learn!
Replace Toast.makeText(context,"clicked="+jobsmodel.getTitle(position)),Toast.LENGTH_SHORT).show() with
Toast.makeText(context,"clicked="+jobsmodel.getTitle(),Toast.LENGTH_SHORT).show();
I always implement the onclickListener under the constructor of the holder class , so, try to implement this under JobsViewHolder(final View itemView)
cv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = v.getContext();
Toast.makeText(itemView.getContext(),"clicked="+jobsmodel.getTitle(position)),Toast.LENGTH_SHORT).show();
}
});
I'm trying using Swip To Dismiss RecyclerView, i used this tutorial
but when i'm trying to delete item in my FragmentOne by swipeToDelete method, it shows this Error:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.UUID com.drgnme.listhamrah.Detail.getId()' on a null object reference
at com.drgnme.listhamrah.DetailLab.deleteDetail(DetailLab.java:82)
at com.drgnme.listhamrah.FragmentOne$DetailAdapter.swipeToDelete(FragmentOne.java:396)
at com.drgnme.listhamrah.ItemSwipeHelper.onSwiped(ItemSwipeHelper.java:43)
at android.support.v7.widget.helper.ItemTouchHelper$4.run(ItemTouchHelper.java:681)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5451)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
i used this in another place in this Fragment for Delete item and it's work just fine, don't know what's the problem here..
this :
DetailLab.get(getActivity()).deleteDetail(mDetail);
updateUI();
updateNumbers();
and here is my FragmentOne:
public class FragmentOne extends Fragment {
private static final String
SAVED_NUMBER_VISIBLE = "number";
private RecyclerView mDetailRecyclerView;
private DetailAdapter mAdapter;
private boolean mNumberVisible;
private View view;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_one_layout,
container, false);
mDetailRecyclerView = (RecyclerView) view.findViewById(R.id.detail_recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
layoutManager.setReverseLayout(true); //This will reverse the data order but not scroll the RecyclerView to the last item
layoutManager.setStackFromEnd(true); //For keeping data order same and simply scrolling the RecyclerView to the last item
mDetailRecyclerView.setLayoutManager(layoutManager);
if (savedInstanceState != null) {
mNumberVisible =
savedInstanceState.getBoolean(SAVED_NUMBER_VISIBLE);
}
updateUI();
return view;
}
#Override
public void onResume() {
super.onResume();
updateUI();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(SAVED_NUMBER_VISIBLE, mNumberVisible);
}
..
..
protected void updateUI() {
DetailLab detailLab = DetailLab.get(getActivity());
List<Detail> details = detailLab.getDetails();
if (details.size() == 0) {
LinearLayout linearLayout = (LinearLayout) view.findViewById(R.id.empty_view);
linearLayout.setVisibility(View.VISIBLE);
} else {
LinearLayout linearLayout = (LinearLayout) view.findViewById(R.id.empty_view);
linearLayout.setVisibility(View.GONE);
}
if (mAdapter == null) {
mAdapter = new DetailAdapter(details);
mDetailRecyclerView.setAdapter(mAdapter);
} else {
mAdapter.setDetails(details);
mAdapter.notifyDataSetChanged();
}
updateNumbers();
}
private class DetailHolder extends RecyclerView.ViewHolder
implements View.OnClickListener, View.OnLongClickListener {
private TextView mTitleTextView;
// private TextView mDateTextView;
private Detail mDetail;
private RatingBar mRatingBar;
public DetailHolder(LayoutInflater inflater, ViewGroup parent) {
super(inflater.inflate(R.layout.list_item_detail,
parent, false));
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
mTitleTextView = (TextView) itemView.findViewById(R.id.detail_title);
mRatingBar = (RatingBar) itemView.findViewById(R.id.ratingBar);
}
public void bind(Detail detail) {
mDetail = detail;
mTitleTextView.setText(mDetail.getTitle());
mRatingBar.setRating(mDetail.getRate());
}
#Override
public void onClick(View view) {
Intent intent = DetailPagerActivity.newIntent(getActivity(),
mDetail.getId());
startActivity(intent);
}
#Override
public boolean onLongClick(View v) {
AlertDialog.Builder alert = new AlertDialog.Builder(
getActivity());
alert.setMessage(R.string.alert);
alert.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
DetailLab.get(getActivity()).deleteDetail(mDetail);
updateUI();
updateNumbers();
dialog.dismiss();
}
});
alert.setNegativeButton("No", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
alert.show();
return true;
}
}
private class DetailAdapter extends RecyclerView.Adapter<DetailHolder> implements ItemTouchHelperAdapter {
private List<Detail> mDetails;
private Detail mDetail;
public DetailAdapter(List<Detail> details) {
mDetails = details;
}
#Override
public DetailHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater =
LayoutInflater.from(getActivity());
return new DetailHolder(layoutInflater, parent);
}
#Override
public void onBindViewHolder(DetailHolder holder, int position) {
Detail detail = mDetails.get(position);
holder.bind(detail);
}
#Override
public int getItemCount() {
return mDetails.size();
}
public void setDetails(final List<Detail> details) {
mDetails = details;
ItemTouchHelper.Callback itemTouchCallBack=new ItemSwipeHelper(Direction.RIGHT, mAdapter);
ItemTouchHelper swipeToDismissTouchHelper =new ItemTouchHelper(itemTouchCallBack);
swipeToDismissTouchHelper.attachToRecyclerView(mDetailRecyclerView);
}
#Override
public void swipeToDelete(int position) {
DetailLab.get(getActivity()).deleteDetail(mDetail);
updateUI();
updateNumbers();
}
}
}
The problem wasn't about the Library that i used, i should have give the item's position for delete ,
so i just added this line in the first of my swipeToDelete method and fix the problem:
mDetail = mDetails.get(position);
How to popup a new fragment and reuse it (depending on the item selected ) When an item from the ListView is clicked. I already tried to use OnItemClickListener() instead of OnClickListene
public void onItemClick(AdapterView parent, View view, int position, long id)
but it is not working either, any help would be appreciated
please refer to the screenshot attached
Thanks
-- Fragment ListView Class--
public class WorkOutFragmentForearm extends Fragment {
private String[] exercisesForearmNames; private String[] exerciseForearmType;
private String[] exerciseForeamNumber;
private int[] image = { R.drawable.forearm,
};
private ArrayList<WorkOutListNameListExercises> mForearmsArray;
private RecyclerView mForearmRecyclerView; private ForearmAdapter mAdapter;
#Override public void onCreate(Bundle savedInstanceState) {
Resources res = getResources();
// Initialize array from info form XML strings exercisesForearmNames =
res.getStringArray(R.array.exercisesForearmNames); exerciseForearmType =
res.getStringArray(R.array.exerciseForearmType); exerciseForeamNumber =
res.getStringArray(R.array.exerciseForeamNumber);
mForearmsArray = new ArrayList<>();
for (int i = 0; i < exercisesForearmNames.length; i++) {
WorkOutListNameListExercises s = new WorkOutListNameListExercises();
s.setName(exercisesForearmNames[i]);
s.setExerciseType(exerciseForearmType[i]);
s.setExerciseNumber(exerciseForeamNumber[i]); s.setImageId(image[i]);
mForearmsArray.add(s); }
super.onCreate(savedInstanceState);
}// end Main
#Override public View onCreateView(LayoutInflater inflater, ViewGroup
container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.workout_recycler_forearm, container,
false); mForearmRecyclerView = (RecyclerView)
view.findViewById(R.id.recycler_view);
mForearmRecyclerView.addItemDecoration(new
SimpleDividerItemDecoration(getResources()));
mForearmRecyclerView.setLayoutManager(new
LinearLayoutManager(getActivity())); updateUI();
// -------------------------------------------------------------------------
---------- // When Back Button is clicked
view.setFocusableInTouchMode(true); view.requestFocus();
view.setOnKeyListener(new View.OnKeyListener() { #Override public boolean
onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
// Display Drawer when back button is clicked if drawer not // display
not go back without FragmentTransaction Code
MainActivity.mDrawerLayout.openDrawer(Gravity.LEFT);
return true; } else {
return false; } } });
return view;
}// end Main
private void updateUI() { mAdapter = new ForearmAdapter(mForearmsArray);
mForearmRecyclerView.setAdapter(mAdapter); }
// Inner Class private class ForearmHolder extends RecyclerView.ViewHolder {
private WorkOutListNameListExercises mForearm; public ImageView mImageView;
public TextView mNameTextView; public TextView mTypeNumberTextView;
public ForearmHolder( View itemView){ super(itemView);
mImageView = (ImageView) itemView.findViewById(R.id.imageView);
mNameTextView = (TextView)
itemView.findViewById(R.id.textview_name); mTypeNumberTextView =
(TextView) itemView.findViewById(R.id.textview_type_number);
itemView.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View view) {
Bundle bundle = new Bundle();
if (mForearm.getName().equals(exercisesForearmNames[0])) {
FragmentTransaction fragmentTransaction =
getActivity().getSupportFragmentManager().beginTransaction(); Fragment
fragmentItem1 = new FragmentItem1(); fragmentItem1.setArguments(bundle);
fragmentTransaction.add(R.id.my_frame_list, fragmentItem1);
fragmentTransaction.addToBackStack(null); fragmentTransaction.commit();
Toast.makeText(getActivity(),mForearm.getName() + "Item 1",
Toast.LENGTH_SHORT).show(); }
});//end View.OnClickListener
}// end Method
public void bindData(WorkOutListNameListExercises s) { mForearm = s;
mImageView.setImageResource(s.getImageId());
mNameTextView.setText(s.getName());
mTypeNumberTextView.setText(s.getExerciseType() + ":" +
s.getExerciseNumber());
}
}// end Inner Class
// Inner Class private class ForearmAdapter extends
RecyclerView.Adapter<ForearmHolder>
{ private ArrayList<WorkOutListNameListExercises> mForearmsArray;
public ForearmAdapter(ArrayList<WorkOutListNameListExercises>
WorkOutListNameListExercises) { mForearmsArray =
WorkOutListNameListExercises;
}
#Override public ForearmHolder onCreateViewHolder(ViewGroup parent, int
viewType) { LayoutInflater layoutInflater =
LayoutInflater.from(getActivity()); View view =
layoutInflater.inflate(R.layout.workout_items_list_info, parent, false);
return new ForearmHolder(view);
}
#Override public void onBindViewHolder(ForearmHolder holder, int position) {
WorkOutListNameListExercises s = mForearmsArray.get(position);
holder.bindData(s);
}
#Override public int getItemCount() { return mForearmsArray.size();
} }// end Inner Class
}// end Class
WorkOutListNameListExercises.java
public class WorkOutListNameListExercises { private String ExreciseName; private
String ExerciseType; private String ExerciseNumber; private int ExerciseImageId;
public String getName() { return ExreciseName; }
public void setName(String ExreciseName) { this.ExreciseName = ExreciseName; }
public String getExerciseType() { return ExerciseType; }
public void setExerciseType(String ExerciseType) { this.ExerciseType =
ExerciseType; }
public String getExerciseNumber() { return ExerciseNumber; }
public void setExerciseNumber(String ExerciseNumber) { this.ExerciseNumber =
ExerciseNumber; }
public int getImageId() { return ExerciseImageId; }
public void setImageId(int ExerciseImageId) { this.ExerciseImageId =
ExerciseImageId; } }
Okay! So you are using a recyclerview and on the itemview you have also added an onClickListener. Also you are creating bundle there. Well you are 99% already there!
You haven't added anything into the bundle. To add some values into it do
bundle.putString("key1","value1");
You can pass an Array as well as an ArrayList using putStringArray() and putStringArrayList()
We don't need to check whether its the first, second or the third item the user is clicking on. Do this in the bindData() which is already providing us the position of the item, using this position we will pass the appropriate values to the fragment. So our code will look like this.
public void bindData(WorkOutListNameListExercises s) {
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Bundle bundle = new Bundle();
bundle.putString("forearmName",s.getName());
bundle.putString("forearmType",s.getType());
bundle.putString("forearmNumber",s.getNumber());
FragmentTransaction fragmentTransaction = getActivity().getSupportFragmentManager().beginTransaction();
Fragment fragmentItem1 = new FragmentItem1();
fragmentItem1.setArguments(bundle);
fragmentTransaction.add(R.id.my_frame_list, fragmentItem1);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
//Your Toast message!
}
}
}
Now on the FragmentItem1 to reuse it. Do this in the onCreate
private String exercisesForearmName, exerciseForearmType, exerciseForeamNumber;
#Override
public void onCreate(Bundle savedInstanceState) {
Bundle bundle=this.getArguments();
exercisesForearmNames=bundle.getString("forearmName");
exerciseForearmType=bundle.getString("forearmType");
exerciseForearmNumber=bundle.getString("forearmNumber");
}
now just assign the values to the view which you intend to in the onCreateView()
Done. Hope this helps.
I have a VerticalGridView that is using a RecyclerView.Adapter to populate the elements. I have discovered that the onBindViewHolder() method does not get called if the potential element is off of the viewport. Unfortunately, this is causing a NullPointerException from a different method because I am catching a TextView reference in the onBindViewHolder() method and passing it to an outside variable for later manipulation.
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final ViewHolder viewHolder = (ViewHolder) holder;
viewHolder.txtCategoryName.setText(categories.get(position).getStrCategory());
categories.get(position).setTxtViewReference(viewHolder.txtCategoryDefectTotal);
viewHolder.categoryBoxRoot.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
for(CategoryListItem catItem : categories){
if(catItem.getStrCategory().equals(viewHolder.txtCategoryName.getText())){
int index = Defects.getInstance().getCategories().indexOf(catItem) + 1;
MainInterface.grids.get(index).bringToFront();
MainInterface.grids.get(index).setVisibility(View.VISIBLE);
for(VerticalGridView grid : MainInterface.grids){
int gridIndex = MainInterface.grids.indexOf(grid);
if(gridIndex != index){
grid.setVisibility(View.INVISIBLE);
}
}
break;
}
}
}
});
From what I understand, the reference to the TextView gets created when the Viewholder object is instantiated.
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView txtCategoryName;
public TextView txtCategoryDefectTotal;
public View categoryBoxRoot;
public ViewHolder(View itemView) {
super(itemView);
txtCategoryName = (TextView) itemView.findViewById(R.id.textViewCategoryName);
txtCategoryDefectTotal = (TextView) itemView.findViewById(R.id.textViewCategoryTotalDefects);
categoryBoxRoot = itemView.findViewById(R.id.root_category_box);
}
}
Is there a way to force onBindViewHolder() to be called on all elements at least one time when the Adapter is instantiated?
I attempted the suggestions here without any success.
I understand that forcing onBindViewHolder() on all elements would work against the whole purpose of the RecycleView.Adapter. Thus, I am open to any other suggestions on how I can catch that TextView reference.
As a temporary fix to this problem, I am able to use a try catch block around the method that generates the NullPointerException. However, I am concerned that the lack of the reference means I could introduce errors in the future.
The easiest solution for this problem is to scroll down to the bottom of the grid and then back up to the top. This cannot be done in the onCreate() method though because the grid is not technically visible at that time. Instead, it should be called in the onResume() method of the activity.
#Override
public void onResume(){
super.onResume();
VerticalGridView defectGrid = grids.get(0);
RecyclerView.Adapter adapter = defectGrid.getAdapter();
defectGrid.smoothScrollToPosition(adapter.getItemCount()-1);
defectGrid.smoothScrollToPosition(0);
}
This was a good attempt at a solution but unfortunately it still does not work. While it does get the reference to make the method work, it does NOT necessarily have the right reference. I found that the reference can end up pointing at a different TextView as the RecycleView.Adapter reuses views to display them in different areas.
SOLUTION:
Mark Keen was right when he said to use notifyDataSetChanged(). I got it to work by fixing my onBindViewHolder() method to work properly.
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final ViewHolder viewHolder = (ViewHolder) holder;
viewHolder.txtCategoryName.setText(categories.get(position).getStrCategory());
viewHolder.txtCategoryDefectTotal.setText(String.valueOf(categories.get(position).getTotalDefectsInCategory()));
}
I also changed my data object so it holds an int value instead of a reference to the TextView since the above proved that reference was invalid. Finally, I added a call to my Adapter when I pressed my custom back button.
grids.get(0).getAdapter().notifyDataSetChanged();
Thank you everyone who contributed!
Just reset the Adapter like this
RecyclerView.Adapter adapter = recList.getAdapter();
recList.setAdapter(null);
recList.setAdapter(adapter);
recList should be you RecyclerView.
wrap your RecyclerView in a NestedScrollView that will force all the items to be bound in advance.
<androidx.core.widget.NestedScrollView
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvGrid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false" />
</androidx.core.widget.NestedScrollView>
Understand Flow
public class ThemeGridAdapter extends RecyclerView.Adapter<ThemeGridAdapter.ViewHolder> {
private OnItemClickedListener listener;
private List<Theme> mDataset;
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView title;
public TextView subtitle;
// public SimpleDraweeView image;
public ImageView img;
public ViewGroup container;
public ViewHolder(RelativeLayout v) {
super(v);
container = v;
title = (TextView) v.findViewById(R.id.theme_name_text);
subtitle = (TextView) v.findViewById(R.id.theme_name_type);
// image = (SimpleDraweeView) v.findViewById(R.id.cell_img);
img = (ImageView) v.findViewById(R.id.cell_img);
}
}
public ThemeGridAdapter(List<Theme> dataset) {
mDataset = dataset;
}
#Override
public ThemeGridAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
RelativeLayout v = (RelativeLayout) LayoutInflater.from(parent.getContext())
.inflate(R.layout.cell, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
holder.title.setText(mDataset.get(position).getTitle());
holder.subtitle.setText(mDataset.get(position).getAuthor());
// Uri uri = Uri.parse(mDataset.get(position).getUrl());
holder.img.setImageResource(mDataset.get(position).getImage());
// holder.image.setImageURI(uri);
holder.container.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null)
listener.onItemClicked(holder.getAdapterPosition());
}
});
}
#Override
public int getItemCount() {
return mDataset.size();
}
public void setListener(OnItemClickedListener listener) {
this.listener = listener;
}
}
Check the style of binding view
public class CrimeListFragment extends Fragment {
private static final String SAVED_SUBTITLE_VISIBLE = "subtitle";
private static final int REQUEST_CRIME = 1;
private RecyclerView mRecyclerView;
private CrimeAdapter mCrimeAdapter;
private boolean mSubtitleVisible;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_crime_list, container, false);
mRecyclerView = (RecyclerView) view.findViewById(R.id.crime_recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
if (savedInstanceState != null){
mSubtitleVisible = savedInstanceState.getBoolean(SAVED_SUBTITLE_VISIBLE);
}
updateUI();
return view;
}
private void updateUI() {
CrimeLab crimeLab = CrimeLab.get(getActivity());
List<Crime> crimes = crimeLab.getCrimes();
if (mCrimeAdapter == null) {
mCrimeAdapter = new CrimeAdapter(crimes);
mRecyclerView.setAdapter(mCrimeAdapter);
} else {
mCrimeAdapter.setCrimes(crimes);
mCrimeAdapter.notifyDataSetChanged();
}
updateSubtitle();
}
private class CrimeHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView mTitleTextView;
private TextView mDateTextView;
private CheckBox mSolvedCheckBox;
private Crime mCrime;
public CrimeHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
mTitleTextView = (TextView) itemView.findViewById(R.id.list_item_crime_title_text_view);
mDateTextView = (TextView) itemView.findViewById(R.id.list_item_crime_date_text_view);
mSolvedCheckBox = (CheckBox) itemView.findViewById(R.id.list_item_crime_solved_checkbox);
}
public void bindCrime(Crime crime) {
mCrime = crime;
mTitleTextView.setText(mCrime.getTitile());
mDateTextView.setText(mCrime.getDate().toString());
mSolvedCheckBox.setChecked(mCrime.isSolved());
}
#Override
public void onClick(View v) {
/*Toast.makeText(getActivity(),mCrime.getTitile() + "clicked!", Toast.LENGTH_SHORT).show();*/
/*Intent for calling activity from fragment*/
/* Intent intent = new Intent(getActivity(), CrimeActivity.class);*/
/*Intent intent = CrimeActivity.newIntent(getActivity(),mCrime.getId());*/
Intent intent = CrimePagerActivity.newIntent(getActivity(), mCrime.getId());
/*startActivityForResult(intent,REQUEST_CRIME);*/
startActivity(intent);
}
}
private class CrimeAdapter extends RecyclerView.Adapter<CrimeHolder> {
private List<Crime> mCrimes;
public CrimeAdapter(List<Crime> crimes) {
mCrimes = crimes;
}
#Override
public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
View view = layoutInflater.inflate(R.layout.list_item_crime, parent, false);
return new CrimeHolder(view);
}
#Override
public void onBindViewHolder(CrimeHolder holder, int position) {
Crime crime = mCrimes.get(position);
/*holder.mTextView.setText(crime.getTitile());*/
holder.bindCrime(crime);
}
#Override
public int getItemCount() {
return mCrimes.size();
}
public void setCrimes(List<Crime> crimes) {
mCrimes = crimes;
}
}
#Override
public void onResume() {
super.onResume();
updateUI();
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CRIME) {
}
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_crime_list, menu);
MenuItem subtitleItem = menu.findItem(R.id.menu_item_show_subtitle);
if (mSubtitleVisible){
subtitleItem.setTitle(R.string.hide_subtitle);
}else {
subtitleItem.setTitle(R.string.show_subtitle);
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_crime:
Crime crime = new Crime();
CrimeLab.get(getActivity()).addCrime(crime);
Intent intent = CrimePagerActivity.newIntent(getActivity(), crime.getId());
startActivity(intent);
return true;
case R.id.menu_item_show_subtitle:
mSubtitleVisible = !mSubtitleVisible;
getActivity().invalidateOptionsMenu();
updateSubtitle();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void updateSubtitle(){
CrimeLab crimeLab = CrimeLab.get(getActivity());
int crimeCount = crimeLab.getCrimes().size();
String subtitle = getString(R.string.subtitle_format,crimeCount);
if (!mSubtitleVisible){
subtitle = null;
}
AppCompatActivity appCompatActivity = (AppCompatActivity)getActivity();
appCompatActivity.getSupportActionBar().setSubtitle(subtitle);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(SAVED_SUBTITLE_VISIBLE,mSubtitleVisible);
}
}