StepFragmentSample.java:
public class StepFragmentSample extends Fragment implements Step {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.step, container, false);
//initialize your UI
return v;
}
public VerificationError verifyStep() {
//return null if the user can go to the next step, create a new VerificationError instance otherwise
return null;
}
#Override
public void onSelected() {
//update UI when selected
}
#Override
public void onError(#NonNull VerificationError error) {
//handle error inside of the fragment, e.g. show error on EditText
}
}
MyStepAdapter.java:
public static class MyStepperAdapter extends AbstractFragmentStepAdapter {
public MyStepperAdapter(FragmentManager fm, Context context) {
super(fm, context);
}
#Override
public Step createStep(int position) {
final StepFragmentSample step = new StepFragmentSample();
Bundle b = new Bundle();
b.putInt(CURRENT_STEP_POSITION_KEY, position);
step.setArguments(b);
return step;
}
#Override
public int getCount() {
return 3;
}
#NonNull
#Override
public StepViewModel getViewModel(#IntRange(from = 0) int position) {
//Override this method to set Step title for the Tabs, not necessary for other stepper types
return new StepViewModel.Builder(context)
.setTitle(R.string.tab_title) //can be a CharSequence instead
.create();
}
}
MainActivity.java:
public class StepperActivity extends AppCompatActivity {
private StepperLayout mStepperLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mStepperLayout = (StepperLayout) findViewById(R.id.stepperLayout);
mStepperLayout.setAdapter(new MyStepperAdapter(getSupportFragmentManager(), this));
}
I have copied the above code from this link.}
While debugging, the control does not goes to createStep Method In StepFragmentSample.java
while debugging from mainActivity.java, the control goes to MyStepAdapter.java in that first MyStepperAdapter() method then to getCount() method but not to createStep(). why? please help me. and It does not giving any exception. The app shows "unfortunately stopped".
Related
EDIT: after running the same code in the MainActivity the public void handleDialogClose(DialogInterface dialog) worked and everything got updated. so now the main problem is why its not running in other fragment?
The only difference I see is that on MainActivity the code is running in protected void onCreate(Bundle savedInstanceState)
While on Assignment fragment its running in public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
My task list is not getting updated after adding a new task or updating an existing task unless I go from one fragment to another and then come back, but the delete task function does work and removes the task from the list without going to the other fragments.
Note: it did worked when the code was in MainActivity
I tried to log the public void handleDialogClose(DialogInterface dialog)
but it didn't worked, so I assume maybe that's why...
Here is the code from my fragment:
public class AssignmentFragment extends Fragment implements DialogCloseListener {
public static Context context;
private FragmentAssignmentBinding binding;
private FloatingActionButton fab;
private RecyclerView tasksRecyclerView;
private ToDoAdapter tasksAdapter;
private List<ToDoModel> taskList;
private DatabaseHandler db;
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentAssignmentBinding.inflate(inflater, container, false);
View root = binding.getRoot();
AssignmentFragment.context = getContext();
db = new DatabaseHandler(getContext());
db.openDatabase();
taskList = new ArrayList<>();
tasksRecyclerView = root.findViewById(R.id.tasksRecyclerView);
tasksRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
tasksAdapter = new ToDoAdapter(db,this, null);
tasksRecyclerView.setAdapter(tasksAdapter);
taskList = db.getAllTasks();
Collections.reverse(taskList);
tasksAdapter.setTasks(taskList);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new RecyclerItemTouchHelper(tasksAdapter));
itemTouchHelper.attachToRecyclerView(tasksRecyclerView);
fab = root.findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
AddNewTask.newInstance().show(getChildFragmentManager(), AddNewTask.TAG);
}
});
return root;
}
#Override
public void handleDialogClose(DialogInterface dialog){
taskList = db.getAllTasks();
Collections.reverse(taskList);
tasksAdapter.setTasks(taskList);
tasksAdapter.notifyDataSetChanged();
}
#Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
Here is the code for Adding a new task:
Here I did tried to log the public void onDismiss(DialogInterface dialog) but its also not responding.
public class AddNewTask extends BottomSheetDialogFragment {
public static final String TAG = "ActionBottomDialog";
private EditText newTaskText;
private Button newTaskSaveButton;
private DatabaseHandler db;
public static AddNewTask newInstance(){
return new AddNewTask();
}
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setStyle(STYLE_NORMAL, R.style.DialogStyle);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View view =inflater.inflate(R.layout.new_task, container, false);
getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
return view;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
newTaskText = getView().findViewById(R.id.newTaskText);
newTaskSaveButton = getView().findViewById(R.id.newTaskButton);
boolean isUpdate = false;
final Bundle bundle = getArguments();
if(bundle != null){
isUpdate = true;
String task = bundle.getString("task");
newTaskText.setText(task);
if(task.length()>0) {
newTaskSaveButton.setTextColor(ContextCompat.getColor(getContext(), android.R.color.holo_blue_dark));
}
}
db = new DatabaseHandler(getActivity());
db.openDatabase();
newTaskText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence s, int i, int i1, int i2) {
if(s.toString().equals("")){
newTaskSaveButton.setEnabled(false);
newTaskSaveButton.setTextColor(Color.GRAY);
}
else{
newTaskSaveButton.setEnabled(true);
newTaskSaveButton.setTextColor(ContextCompat.getColor(getContext(), android.R.color.holo_blue_dark));
}
}
#Override
public void afterTextChanged(Editable editable) {
}
});
final boolean finalIsUpdate = isUpdate;
newTaskSaveButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String text = newTaskText.getText().toString();
if(finalIsUpdate){
db.updateTask(bundle.getInt("id"), text);
}
else{
ToDoModel task = new ToDoModel();
task.setTask(text);
task.setStatus(0);
db.insertTask(task);
}
dismiss();
}
});
}
#Override
public void onDismiss(DialogInterface dialog){
Activity activity = getActivity();
if(activity instanceof DialogCloseListener){
((DialogCloseListener)activity).handleDialogClose(dialog);
}
}
}
When you add a new task, you should write the following code immediately after;
tasksAdapter.notifyDataSetChanged();
This code refreshes the database.
I am using navigation component in my app I have 2 fragments one fragments list of items and another shows detail of an item when user clicks on an item in fragments 1 it goes to detail fragment and when I switch back to first fragment then all the listing duplicates again.
Below is my code:
CakeFragment.java
public class CakeFragment extends Fragment {
List<AllCakes> allCakeList = new ArrayList<>();
AllCakesAdapter adapter;
BottomNavigationView navView;
FragmentCakeBinding fragmentCakeBinding;
public CakeFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
fragmentCakeBinding = FragmentCakeBinding.inflate(inflater,container,false);
navView = getActivity().findViewById(R.id.navView);
navView.setVisibility(View.GONE);
getAllCakes();
return fragmentCakeBinding.getRoot();
}
private void getAllCakes(){
Retrofit retrofit = RetrofitClient.getInstance();
ApiService apiService = retrofit.create(ApiService.class);
Call<List<AllCakes>> call = apiService.getAllCake();
call.enqueue(new Callback<List<AllCakes>>() {
#Override
public void onResponse(Call<List<AllCakes>> call, Response<List<AllCakes>> response) {
fragmentCakeBinding.cakeProgress.setVisibility(View.INVISIBLE);
fragmentCakeBinding.allCakeRecycler.setHasFixedSize(true);
fragmentCakeBinding.allCakeRecycler.setLayoutManager(new LinearLayoutManager(getActivity()));
allCakeList.addAll(response.body());
adapter = new AllCakesAdapter(getActivity(),allCakeList);
fragmentCakeBinding.allCakeRecycler.setAdapter(adapter);
}
#Override
public void onFailure(Call<List<AllCakes>> call, Throwable t) {
fragmentCakeBinding.cakeProgress.setVisibility(View.INVISIBLE);
TastyToast.makeText(getActivity(),t.getMessage(),TastyToast.LENGTH_SHORT,TastyToast.ERROR).show();
}
});
}
}
AllCakesAdapter.java
public class AllCakesAdapter extends RecyclerView.Adapter<AllCakesAdapter.ViewHolder> {
Context context;
List<AllCakes> allCakeList;
public AllCakesAdapter(Context context, List<AllCakes> allCakeList) {
this.context = context;
this.allCakeList = allCakeList;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.allcakes_row,parent,false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
AllCakes model = allCakeList.get(position);
Glide.with(context).load(model.getImgurl()).into(holder.allCakeImg);
holder.allCakeName.setText(model.getName());
holder.cakeDisPrice.setPaintFlags(holder.cakeDisPrice.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
holder.moreCake.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
NavController navController = Navigation.findNavController((Activity) context,R.id.fragment);
navController.navigate(R.id.cakeDetailFragment);
}
});
}
#Override
public int getItemCount() {
return allCakeList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
ImageView allCakeImg;
Button moreCake;
TextView allCakeName;
public ViewHolder(#NonNull View itemView) {
super(itemView);
allCakeImg = itemView.findViewById(R.id.allCakeImg);
moreCake = itemView.findViewById(R.id.moreCake);
allCakeName = itemView.findViewById(R.id.allCakeName);
}
}
}
CakeDetailFragment.java
public class CakeDetailFragment extends Fragment {
FragmentCakeDetailBinding fragmentCakeDetailBinding;
public CakeDetailFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
fragmentCakeDetailBinding = FragmentCakeDetailBinding.inflate(inflater,container,false);
return fragmentCakeDetailBinding.getRoot();
}
}
Why is this happening?
private void getAllCakes(){
...
allCakeList.addAll(response.body());
adapter = new AllCakesAdapter(getActivity(),allCakeList);
fragmentCakeBinding.allCakeRecycler.setAdapter(adapter);
}
...
you're calling:
allCakeList.addAll(response.body());
every time without clearing your list out.
you have to clear that list:
allCakeList.clear();
allCakeList.addAll(response.body());
this is something you can easily determine yourself by just putting a breakpoint on your allCakeList to see what's inside it, if you haven't ever done this before, you should consider trying it out
Try the following and see if it solves your issue
CakeFragment.java
allCakeList.clear();
allCakeList.addAll(response.body());
//rest of the logic remains same
I'm trying to manage a RyclerView into my fragment. When I open the app, the fragment gets loaded and a list of element is generated too (this by the execution of the loadMovie() method). Now, I added a search button and I'd like to reload the loadMovie() method to refresh the list of elements. My problem is that even if the list changes, when I click on an element it opens me two new activities: the one of the object on which I clicked on and the one of the element that was in the same position of the current one before calling the method. How can I delete all the old elements of the RecyclerView without modify the Adapter? This is the code:
public class PopularFragment extends Fragment {
private EditText mSearchField;
private ImageButton mSearchBtn;
#BindView(R.id.rc_view)
RecyclerView recyclerView;
public PopularFragment() {
}
public static PopularFragment newInstance(String param1, String param2) {
PopularFragment fragment = new PopularFragment();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
onPause();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.popular_fragment, container, false);
recyclerView = view.findViewById(R.id.popular_rc_view);
mSearchField = (EditText) view.findViewById(R.id.search_field);
mSearchBtn = (ImageButton) view.findViewById(R.id.search_btn);
mSearchBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
loadMovie();
}
});
loadMovie();
return view;
}
#Override
public void onPause() {
super.onPause();
}
private void loadMovie() {
ApiService apiService = ApiBuilder.getClient(getContext()).create(ApiService.class);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext(),LinearLayoutManager.VERTICAL, false));
Call<MovieResponse> call = apiService.getPopular(Values.CATEGORY[1], BuildConfig.API_KEY,Values.LANGUAGE,Values.PAGE[0]);
call.enqueue(new Callback<MovieResponse>() {
#Override
public void onResponse(Call<MovieResponse>call, Response<MovieResponse> response) {
final List<MovieModel> movies = response.body().getResults();
recyclerView.setAdapter(new PopularAdapter(movies, R.layout.content_main, getContext()));
recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
GestureDetector gestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
public boolean onSingleTapUp(MotionEvent e){
return true;
}
});
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && gestureDetector.onTouchEvent(e)){
int position = rv.getChildAdapterPosition(child);
//loadActivity
}
return false;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
});
}
#Override
public void onFailure(Call<MovieResponse>call, Throwable t) {
}
});
}
}
Have you tried to define the Adapter class globally?
Try doing the following steps:-
Define the Adapter class globally and pass an empty list of movies.
Add a function in adapter that accepts the list of movies that you can call in place of defining your adapter over an over again.
Call (Adapter class variable).notifyDataSetChanged(); every time after you call the new function that populates your data, that'll refresh your list of data.
I am implementing RecycleView that will display list of skills those skills passed form another fragment using Parcelable the issue that when I delete item from the skills that display on RecycleView and back to the previous fragment the item also deleted from the orginal arraylist
Here is the Parcelable object
public class Skills implements Parcelable {
private ArrayList<String> skills;
public ArrayList<String> getVacancySkills() {
return skills;
}
public void setSkills(ArrayList<String> skills) {
this.skills = skills;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStringList(this.skills);
}
public Skills() {
}
protected Skills(Parcel in) {
this.skills = in.createStringArrayList();
}
public static final Parcelable.Creator<Skills> CREATOR = new Parcelable.Creator<Skills>() {
#Override
public Skills createFromParcel(Parcel source) {
return new Skills(source);
}
#Override
public Skills[] newArray(int size) {
return new Skills[size];
}
};
}
and here is the passing data
#OnClick(R.id.card_add_skill)
public void showAddSkills() {
Bundle data = new Bundle();
Skills skills = new Skills();
skills.setSkills(skillsLit);
data.putParcelable(ExtraKeys.VACANCY_SKILLS, skills);
VacancyEditSkillsFragment vacancyEditSkillsFragment = new VacancyEditSkillsFragment();
FragmentUtils.addFragment(
getActivity().getSupportFragmentManager(),
vacancyEditSkillsFragment
,
true,
App.getContext().getString(R.string.skills), data
);
}
and here is the fragment that recive the Parcable arraylist of strings
public class VacancyEditSkillsFragment extends Fragment {
#BindView(R.id.rc_edit_skill_recycle)
RecyclerView editSkillsRecycle;
private Skills mSkills;
private EditVacancySkillsAdapter mEditVacancySkillsAdapter;
public VacancyEditSkillsFragment() {
// Required empty public constructor
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getArguments() != null) {
mSkills = getArguments().getParcelable(ExtraKeys.VACANCY_SKILLS);
}
mEditVacancySkillsAdapter = new EditVacancySkillsAdapter(mSkills.getVacancySkills());
editSkillsRecycle.setLayoutManager(new LinearLayoutManager(getActivity()));
editSkillsRecycle.setAdapter(mEditVacancySkillsAdapter);
}
public static VacancyEditSkillsFragment newInstance() {
VacancyEditSkillsFragment fragment = new VacancyEditSkillsFragment();
return fragment;
}
#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 view = inflater.inflate(R.layout.fragment_vacancy_edit_skills, container, false);
ButterKnife.bind(this, view);
return view;
}
}
and here is the adapter code
public class EditVacancySkillsAdapter extends RecyclerView.Adapter<EditVacancySkillsAdapter.EditSkillViewHolder> {
private List<String> skills;
public class EditSkillViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
#BindView(R.id.tv_skill_name)
TextView skillTitle;
#BindView(R.id.iv_reomve_skill)
ImageView deleteSkill;
public EditSkillViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
deleteSkill.setOnClickListener(this);
}
#Override
public void onClick(View view) {
if(view.getId()== R.id.iv_reomve_skill)
{
skills.remove(getAdapterPosition());
notifyDataSetChanged();
}
}
}
public EditVacancySkillsAdapter(ArrayList<String> sections) {
this.skills = sections;
if (skills == null) {
skills = new ArrayList<>();
}
}
#Override
public EditSkillViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_edit_vacancy_skill, parent, false);
return new EditSkillViewHolder(itemView);
}
#Override
public void onBindViewHolder(EditSkillViewHolder holder, int position) {
holder.skillTitle.setText(skills.get(position));
}
#Override
public int getItemCount() {
return skills.size();
}
}
This is as expected.
What is parcellable in simple term is:- It allows you to send a Object across different components. You are referencing the same object in both the components. Parcellable has implemented on top the object. So if any change happens to the object anywhere in the component will reflect back to rest of the component which uses the same object.
I hope you understood.
What you should do is send a copy of the object instead of the original object.
How to clone a parcellable object is by this:-
Foo foo1 = new Foo("a", "b", "c");
Parcel p = Parcel.obtain();
p.writeValue(foo1);
p.setDataPosition(0);
Foo foo2 = (Foo)p.readValue(Foo.class.getClassLoader());
p.recycle();
I hope this will help!
I have a FragmentActivity with two Fragments. The two Fragments depend on a shared object. The object is loaded by an AsyncTask. So the problem here is that when the activity creates and shows up the fragments, the object is not loaded yet. Is there any method that pauses the Fragment creation or something like this?
The scenario is like this:
FragmentActivity
public class MainActivity extends FragmentActivity {
private Object sharedObject;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new FooAsyncTask(...).execute();
}
...
public void onAsyncTaskCompleted(result) {
sharedObject = result;
}
// Pager adapter -------------------------------------------------------
private class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch(position) {
case 0:
return FooFragment.newInstance(sharedObject);
case 1:
return BarFragment.newInstance(sharedObject);
default:
throw new IllegalStateException();
}
}
#Override
public String getPageTitle(int position) {
switch(position) {
case 0:
return "Foo";
case 1:
return "Bar";
default:
throw new IllegalStateException();
}
}
#Override
public int getCount() {
return 2;
}
}
}
FooFragment
public class FooFragment extends Fragment {
public FooFragment newInstance(Object sharedObject) {
FooFragment f = new FooFragment();
f.setObject(sharedObject);
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
}
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
// Here I use the object to show data
}
}
BarFragment
public class BarFragment extends Fragment {
public BarFragment newInstance(Object sharedObject) {
BarFragment f = new BarFragment();
f.setObject(sharedObject);
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
}
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
// Here I use the object to show data
}
}
Final thoughts
So, as you can see, I need the same data for the two fragments, and this data needs to be loaded in an AsyncTask. The ideal behavior for me would be:
Load MainActivity
Show ProgressDialog and load the data in the background
Dismiss ProgressDialog
Show Fragments and use the data
You do this wrong. You should NOT pause anything. Instead, when parent activity load what is needed it should tell the fragments about that. In fact I'd make it differently - by using listeners - so my fragment would need to register on creation and the object that loads my data would then broadcast message back once loading task it done.