Can single Activity be used for multiple actions? - java

The cheat code app will have a list of games. And when each game is tapped, the cheat codes for that game will be displayed. My question is, do I have to create an activity for each game listed or is there a way to make it work in one activity.

No you don’t need to make individual activity for each game listed rather you can go for a RecyclerView and fragments combination to make it work in one activity.
here i have added a recyclerView for the game list and the when items of the game list will be clicked then the recylerview will be hidden and the frameLayout id(content) will be populated with the fragment and when the back btn is pressed while inside the fragment reverse will happen( hiding the fragment and showing the recyclerView).
though this whole approach can be far more simplified if all the game have similar type of data to show then you can,
in that case you can pass data to the fragment and make it work with only one fragment.
hope this answer the question.
**
XML code
**
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
**
Adapter code
**
public abstract class SettingsAdapter extends RecyclerView.Adapter<SettingsAdapter.MyViewHolder> {
private static final String TAG = "SettingsAdapter";
private LayoutInflater inflater;
private Context context;
private List<SettingData> data;
public SettingsAdapter(Context context , List<SettingData> data){
inflater = LayoutInflater.from(context);
this.data = data;
this.context = context;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.custom_settings_row, parent, false);
SettingsAdapter.MyViewHolder holder = new SettingsAdapter.MyViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
SettingData current = data.get(position);
holder.settinsName.setText(current.settingName);
holder.hostRelativeLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
OnButtonClicked(v, position);
}
});
}
#Override
public int getItemCount() {
return data.size();
}
protected abstract void OnButtonClicked(View v ,int position);
class MyViewHolder extends RecyclerView.ViewHolder {
private final TextView settinsName;
private final RelativeLayout hostRelativeLayout;
public MyViewHolder(View itemView) {
super(itemView);
settinsName = itemView.findViewById(R.id.settings_name);
hostRelativeLayout = itemView.findViewById(R.id.hostRelativeLayout);
}
}
}
**
android activity code inside onCreate
**
SettingsAdapter settingsAdapter = new SettingsAdapter(this, getFinalData()) {
#SuppressLint("RestrictedApi")
#Override
protected void OnButtonClicked(View v, int position) {
switch (position) {
case 0:
settingsContent.setVisibility(View.GONE);
fab.show();
configAppbarTittle(StudentSettingsActivity.this, settingNameArr[position]);
appBarLayout.setExpanded(false);
getSupportFragmentManager().beginTransaction().replace(R.id.content, new SavedPlacesFragment()).commit();
break;
case 1:
//similar
break;
case 2:
//similar
break;
case 3:
//similar
break;
default:
break;
}
}
};
settingsRecycleView.setAdapter(settingsAdapter);
settingsRecycleView.setLayoutManager(new LinearLayoutManager(this));
**
fragment code inside activity
**
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class SavedPlacesFragment extends Fragment {
private StudentSettingsActivity myMainActivity;
private RecyclerView savedPlacesRecycler;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
myMainActivity = (StudentSettingsActivity) getActivity();
}
#SuppressWarnings("unchecked")
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
//myMainActivity = (StudentSettingsActivity) getActivity();
View rootView = inflater.inflate(R.layout.stu_setting_saved_places_fragment, container, false);
return rootView;
}
private void flush(String msg) {
Toast.makeText(myMainActivity, msg, Toast.LENGTH_SHORT).show();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
myMainActivity.fab.hide();
startActivity(new Intent(getActivity(), StudentSettingsActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
}
Please understand the fact that i have copy pasted from my production
code here. Just to demonstrate the way. If you copy paste this code it
won't work. You have to read through the code to understand the way
it's been implemented and then write it in your own way

...You say that the cheat code app have list of games
You create one activity to show the list
....And if u tap on list to get new game
So you have to create fragment or activity to show the game by click on list

You can create a single Activity and have it hold multiple fragments.
Your Activity ( Lets say MainActivity ) will be responsible for displaying fragments.
At a high level, you need 2 fragments:
GameListFragment
for displaying Game List and game item click. You will need a recycler view to display list of games on this fragment.
GameCheatCodeDetailsFragment
for displaying Cheat Code.
Refer to SmartShows to get some code reference.

Related

How to add an Item Click Listener in `RecyclerView.Adapter' using CardView Item

How can I add an Item Click Listener for my `RecyclerView.Adapter'
when the user clicks on the Card View item, Data sent to the PostContent Fragment?
Also, is it possible to send the data from this adapter to the new fragment using intent?
Please note my code:
public class PostDataAdapter extends RecyclerView.Adapter<PostDataAdapter.MyViewHolder> {
private List<PostData> PostDataList ;
public static class MyViewHolder extends RecyclerView.ViewHolder {
public TextView vPostContent, vPostDate, vPostAuthor, vPostTitr,VPostLikes,VPostViews;
public ImageView vPostPhoto;
public MyViewHolder(View v) {
super(v);
vPostContent = v.findViewById(R.id.PostContentTv);
vPostDate = v.findViewById(R.id.PostDateTv);
vPostAuthor = v.findViewById(R.id.PostAuthorTv);
vPostTitr = v.findViewById(R.id.PostTitrTv);
vPostPhoto = v.findViewById(R.id.PostPhoto);
VPostLikes=v.findViewById(R.id.PostLikeTv);
VPostViews=v.findViewById(R.id.PostViewTv);
}
}
public PostDataAdapter(List<PostData> postDataList) {
PostDataList = postDataList;
}
#Override
public PostDataAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_posts, parent, false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.vPostDate.setText(PostDataList.get(position).getPostDate());
holder.vPostTitr.setText(PostDataList.get(position).getPostTitr());
holder.vPostContent.setText(PostDataList.get(position).getPostContent());
holder.vPostAuthor.setText(PostDataList.get(position).getPostAuthor());
holder.VPostViews.setText(PostDataList.get(position).getPostViews());
holder.VPostLikes.setText(PostDataList.get(position).getPostLikes());
new DownloadImageTask(holder.vPostPhoto).execute(PostDataList.get(position).getImgpost());
}
#Override
public int getItemCount() {
return PostDataList.size();
}
}
To add a ItemCLickListener for RecyclerView, you need to implement a custom Interface which the Fragment will implement. When the list item is clicked, then the callback function of the interface is called.
CustomItemClickListener.java:
public CustomItemClickListener {
void onItemClick(Object data);
}
Just add these to the PostDataAdapter:
PostDataAdapter.java:
private CustomItemClickListner clickListener;
public PostDataAdapter(CustomItemClickListner listener, List<PostData> postDataList) {
PostDataList = postDataList;
clickListener = listener
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.vPostCardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Modify the parameters of the function according to what you want to send to the fragment
// As soon as this is called, the `onItemClick` function implemented in the Fragment gets called.
clickListener.onItemClick(Object data);
}
});
}
Fragment.java:
CustomFragment extends Fragment implements CustomItemClickListener {
public CustomFragment() {
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view;
PostDataAdapter adapter = new PostDataAdapter(this, new ArrayList<PostData>)
return view;
}
#Override
public void onItemClick(Object data) {
// Handle the data sent by the adapter on item click
}
}
Yu cand send data from Adapter to a Fragment with Intent:
Fragment fragment = new tasks();
FragmentManager fragmentManager = context.getSupportFragmentManager(); // this is the context of the Activity
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Bundle bundle=new Bundle();
bundle.putString("name", "Osmar Cancino"); //key and value
//set Fragmentclass Arguments
fragment.setArguments(bundle);
fragmentTransaction.replace(R.id.content_frame, fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Although my suggestion is to manage the flow of screens from the parent activity, and manage the data through a Callback, even with a custom Interface
There are two ways you can do this.
Write recyclerview.onitem touchlistener(...). Then consume that event in your fragment. As you will get item position inside touchlistener callback, you can take out data from your list directly from the list you passed to your adapter (Assuming you have list reference outside in your fragment.)
Oobserver pattern.
Define a functional interface (one callback method with required parameters of the data you want to pass) implement inside your fragment. Send its reference with the constructor of adapter. Then Store reference in a interface type variable inside adapter. Write click listener on card. And on the card click, invoke method using interface type variable.
Intents can be used to send data to new activities but not fragments You'd have to use the Fragment Manager and attach a bundle to it to send data. You can refer to the documentation here on how to do so:
https://developer.android.com/training/basics/fragments/communicating#Deliver
To handle click on cards, you can create a listener when you create PostDataAdapter. Refer to the following link for a simple example:
https://stackoverflow.com/a/40584425/4260853
for adding click item for a Cardview, you can find the Cardview in MyViewHolder class by id and in onBindViewHolder set a click listerner for it like the following
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.vPostCardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//write your codes here
}
});
}
if you have an intent that you want to send it's data to a fragment, you can get the intent data and send them with bundle to your fragment. for example do something like the following.
Bundle bundle = new Bundle();
bundle.putString("your_key",intent.getStringExtra("your_item_key_in_intent"));
and after that send bundle to your fragment with
fragment.setArguments(bundle);

Fragments not visible when switch tabs using ViewPager and TabLayout

I have a HostActivity that uses ViewPager and TabLayout to switch between multiple Fragments. When I switch between the tabs, the Fragments instance does get the updated data. I also see the updated data in onCreateView of the Fragment instance, but the TextView.setText does not get updated. When I check the visibility of Fragment, it always shows Invisible. How do I make the fragment visible when I switch tabs so that the view gets updated with new data? Is there something missing in the Fragment/Activity Lifecycle? I am implementing ViewPager for the first time so it will be helpful to know if I am missing something.
Fragment Class:
public class StepFragment extends Fragment { #Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (getArguments() != null) {
step = getArguments().getParcelable(SELECTED_STEP);
mDescription = step.getDescription();
}
View view = inflater.inflate(R.layout.step_fragment, container, false);
ButterKnife.bind(this,view);
Log.e(TAG, "onCreateView: "+mDescription); **// THIS GETS UPDATED DATA**
tvStepDescription.setText(mDescription);
}
return view;
}
}
Here is my Host Activity:
public class StepActivity extends AppCompatActivity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_step);
fragmentSelectAdapter = new StepFragmentSelectAdapter(getSupportFragmentManager(),this,steps,recipe);
mViewPager.setAdapter(fragmentSelectAdapter);
mTabLayout.setupWithViewPager(mViewPager);
stepFragment = (StepFragment) getSupportFragmentManager().findFragmentById(R.id.step_container);
if(stepFragment == null) {
stepFragment = StepFragment.newInstance(step, recipe);
stepFragment.setArguments(bundle);
getSupportFragmentManager().beginTransaction()
.add(R.id.step_container, stepFragment)
.commit();
} else {
stepFragment = StepFragment.newInstance(step, recipe);
stepFragment.setArguments(bundle);
getSupportFragmentManager().beginTransaction()
.replace(R.id.step_container, stepFragment)
.commit();
}
}
}
Here is my FragmentPagerAdapter, which seems to be getting the correct data as per the tab position in getItem method:
public class StepFragmentSelectAdapter extends FragmentPagerAdapter {
...
#Override
public Fragment getItem(int position) {
**// THIS GETS UPDATED DATA**
Log.e(TAG, "getItem: \nDecr: "+steps.get(position).getDescription()+"\nVideo: "+steps.get(position).getVideoURL()+"\nImage: "+steps.get(position).getThumbnailURL());
return StepFragment.newInstance(steps.get(position),recipe);
}
#Override
public int getCount() {
if (steps == null){
return 0;
}
return steps.size();
}
...
}
As far as I could understand about the problem that you are having there, I think you should implement an onResume function in your StepFragment which will get the updated data from some source and will display this in the TextView. However, I can think of a potential problem in your StepFragmentSelectAdapter. You are creating a new instance each time you are switching the tabs.
You should have the Fragment instances created before and if you are about to pass the data among fragments, you might consider having a BroadcasReceiver or listener function by implementing an interface.
So the PagerAdapter should look something like this.
public class StepFragmentSelectAdapter extends FragmentPagerAdapter {
public ArrayList<StepFragment> stepFragments;
public StepFragmentSelectAdapter(ArrayList<Step> steps) {
stepFragments = new ArrayList<>();
for(int i = 0; i < steps.size(); i++) {
stepFragments.add(StepFragment.newInstance(steps.get(position),recipe));
}
}
...
#Override
public Fragment getItem(int position) {
**// THIS GETS UPDATED DATA**
Log.e(TAG, "getItem: \nDecr: "+steps.get(position).getDescription()+"\nVideo: "+steps.get(position).getVideoURL()+"\nImage: "+steps.get(position).getThumbnailURL());
return stepFragments.get(position);
}
#Override
public int getCount() {
if (steps == null){
return 0;
}
return steps.size();
}
...
}
Thanks for the hint. I got around this problem by replacing ActionBar with a custom ToolBar with back ImageButton and using click listener to get back to the calling activity.
backButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getActivity().onBackPressed();
}
});

How to call bindViewHolder when RecyclerView data changes [duplicate]

I have a RecyclerView with an TextView text box and a cross button ImageView. I have a button outside of the recyclerview that makes the cross button ImageView visible / gone.
I'm looking to remove an item from the recylerview, when that items cross button ImageView is pressed.
My adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> implements View.OnClickListener, View.OnLongClickListener {
private ArrayList<String> mDataset;
private static Context sContext;
public MyAdapter(Context context, ArrayList<String> myDataset) {
mDataset = myDataset;
sContext = context;
}
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_text_view, parent, false);
ViewHolder holder = new ViewHolder(v);
holder.mNameTextView.setOnClickListener(MyAdapter.this);
holder.mNameTextView.setOnLongClickListener(MyAdapter.this);
holder.mNameTextView.setTag(holder);
return holder;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.mNameTextView.setText(mDataset.get(position));
}
#Override
public int getItemCount() {
return mDataset.size();
}
#Override
public void onClick(View view) {
ViewHolder holder = (ViewHolder) view.getTag();
if (view.getId() == holder.mNameTextView.getId()) {
Toast.makeText(sContext, holder.mNameTextView.getText(), Toast.LENGTH_SHORT).show();
}
}
#Override
public boolean onLongClick(View view) {
ViewHolder holder = (ViewHolder) view.getTag();
if (view.getId() == holder.mNameTextView.getId()) {
mDataset.remove(holder.getPosition());
notifyDataSetChanged();
Toast.makeText(sContext, "Item " + holder.mNameTextView.getText() + " has been removed from list",
Toast.LENGTH_SHORT).show();
}
return false;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView mNumberRowTextView;
public TextView mNameTextView;
public ViewHolder(View v) {
super(v);
mNameTextView = (TextView) v.findViewById(R.id.nameTextView);
}
}
}
My layout is:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:id="#+id/layout">
<TextView
android:id="#+id/nameTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:padding="5dp"
android:background="#drawable/greyline"/>
<ImageView
android:id="#+id/crossButton"
android:layout_width="16dp"
android:layout_height="16dp"
android:visibility="gone"
android:layout_marginLeft="50dp"
android:src="#drawable/cross" />
</LinearLayout>
How can I get something like an onClick working for my crossButton ImageView? Is there a better way? Maybe changing the whole item onclick into a remove the item? The recyclerview shows a list of locations that need to be edited. Any technical advice or comments / suggestions on best implementation would be hugely appreciated.
I have done something similar.
In your MyAdapter:
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public CardView mCardView;
public TextView mTextViewTitle;
public TextView mTextViewContent;
public ImageView mImageViewContentPic;
public ImageView imgViewRemoveIcon;
public ViewHolder(View v) {
super(v);
mCardView = (CardView) v.findViewById(R.id.card_view);
mTextViewTitle = (TextView) v.findViewById(R.id.item_title);
mTextViewContent = (TextView) v.findViewById(R.id.item_content);
mImageViewContentPic = (ImageView) v.findViewById(R.id.item_content_pic);
//......
imgViewRemoveIcon = (ImageView) v.findViewById(R.id.remove_icon);
mTextViewContent.setOnClickListener(this);
imgViewRemoveIcon.setOnClickListener(this);
v.setOnClickListener(this);
mTextViewContent.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
if (mItemClickListener != null) {
mItemClickListener.onItemClick(view, getPosition());
}
return false;
}
});
}
#Override
public void onClick(View v) {
//Log.d("View: ", v.toString());
//Toast.makeText(v.getContext(), mTextViewTitle.getText() + " position = " + getPosition(), Toast.LENGTH_SHORT).show();
if(v.equals(imgViewRemoveIcon)){
removeAt(getPosition());
}else if (mItemClickListener != null) {
mItemClickListener.onItemClick(v, getPosition());
}
}
}
public void setOnItemClickListener(final OnItemClickListener mItemClickListener) {
this.mItemClickListener = mItemClickListener;
}
public void removeAt(int position) {
mDataset.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, mDataSet.size());
}
Edit:
getPosition() is deprecated now, use getAdapterPosition() instead.
first of all, item should be removed from the list!
mDataSet.remove(getAdapterPosition());
then:
notifyItemRemoved(getAdapterPosition());
notifyItemRangeChanged(getAdapterPosition(), mDataSet.size()-getAdapterPosition());
if still item not removed use this magic method :)
private void deleteItem(int position) {
mDataSet.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, mDataSet.size());
holder.itemView.setVisibility(View.GONE);
}
Kotlin version
private fun deleteItem(position: Int) {
mDataSet.removeAt(position)
notifyItemRemoved(position)
notifyItemRangeChanged(position, mDataSet.size)
holder.itemView.visibility = View.GONE
}
The Problem
RecyclerView was built to display data in an efficient and responsive manner.
Usually you have a dataset which is passed to your adapter and is looped through to display your data.
Here your dataset is:
private ArrayList<String> mDataset;
The point is that RecyclerView is not connected to your dataset, and therefore is unaware of your dataset changes.
It just reads data once and displays it through your ViewHolder, but a change to your dataset will not propagate to your UI.
This means that whenever you make a deletion/addition on your data list, those changes won't be reflected to your RecyclerView directly. (i.e. you remove the item at index 5, but the 6th element remains in your recycler view).
A (old school) solution
RecyclerView exposes some methods for you to communicate your dataset changes, reflecting those changes directly on your list items.
The standard Android APIs allow you to bind the process of data removal (for the purpose of the question) with the process of View removal.
The methods we are talking about are:
notifyItemChanged(index: Int)
notifyItemInserted(index: Int)
notifyItemRemoved(index: Int)
notifyItemRangeChanged(startPosition: Int, itemCount: Int)
notifyItemRangeInserted(startPosition: Int, itemCount: Int)
notifyItemRangeRemoved(startPosition: Int, itemCount: Int)
A Complete (old school) Solution
If you don't properly specify what happens on each addition, change or removal of items, RecyclerView list items are animated unresponsively because of a lack of information about how to move the different views around the list.
The following code will allow RecyclerView to precisely play the animation with regards to the view that is being removed (And as a side note, it fixes any IndexOutOfBoundExceptions, marked by the stacktrace as "data inconsistency").
void remove(position: Int) {
dataset.removeAt(position)
notifyItemChanged(position)
notifyItemRangeRemoved(position, 1)
}
Under the hood, if we look into RecyclerView we can find documentation explaining that the second parameter we pass to notifyItemRangeRemoved is the number of items that are removed from the dataset, not the total number of items (As wrongly reported in some others information sources).
/**
* Notify any registered observers that the <code>itemCount</code> items previously
* located at <code>positionStart</code> have been removed from the data set. The items
* previously located at and after <code>positionStart + itemCount</code> may now be found
* at <code>oldPosition - itemCount</code>.
*
* <p>This is a structural change event. Representations of other existing items in the data
* set are still considered up to date and will not be rebound, though their positions
* may be altered.</p>
*
* #param positionStart Previous position of the first item that was removed
* #param itemCount Number of items removed from the data set
*/
public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
mObservable.notifyItemRangeRemoved(positionStart, itemCount);
}
Open source solutions
You can let a library like FastAdapter, Epoxy or Groupie take care of the business, and even use an observable recycler view with data binding.
New ListAdapter
Google recently introduced a new way of writing the recycler view adapter, which works really well and supports reactive data.
It is a new approach and requires a bit of refactoring, but it is 100% worth switching to it, as it makes everything smoother.
here is the documentation, and here a medium article explaining it
Here are some visual supplemental examples. See my fuller answer for examples of adding and removing a range.
Add single item
Add "Pig" at index 2.
String item = "Pig";
int insertIndex = 2;
data.add(insertIndex, item);
adapter.notifyItemInserted(insertIndex);
Remove single item
Remove "Pig" from the list.
int removeIndex = 2;
data.remove(removeIndex);
adapter.notifyItemRemoved(removeIndex);
Possibly a duplicate answer but quite useful for me. You can implement the method given below in RecyclerView.Adapter<RecyclerView.ViewHolder>
and can use this method as per your requirements, I hope it will work for you
public void removeItem(#NonNull Object object) {
mDataSetList.remove(object);
notifyDataSetChanged();
}
I tried all the above answers, but inserting or removing items to recyclerview causes problem with the position in the dataSet. Ended up using delete(getAdapterPosition()); inside the viewHolder which worked great at finding the position of items.
The problem I had was I was removing an item from the list that was no longer associated with the adapter to make sure you are modifying the correct adapter you can implement a method like this in your adapter:
public void removeItemAtPosition(int position) {
items.remove(position);
}
And call it in your fragment or activity like this:
adapter.removeItemAtPosition(position);
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private Context context;
private List<cardview_widgets> list;
public MyAdapter(Context context, List<cardview_widgets> list) {
this.context = context;
this.list = list;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(this.context).inflate(R.layout.fragment1_one_item,
viewGroup, false);
return new MyViewHolder(view);
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
TextView txtValue;
TextView txtCategory;
ImageView imgInorEx;
ImageView imgCategory;
TextView txtDate;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
txtValue= itemView.findViewById(R.id.id_values);
txtCategory= itemView.findViewById(R.id.id_category);
imgInorEx= itemView.findViewById(R.id.id_inorex);
imgCategory= itemView.findViewById(R.id.id_imgcategory);
txtDate= itemView.findViewById(R.id.id_date);
}
}
#NonNull
#Override
public void onBindViewHolder(#NonNull final MyViewHolder myViewHolder, int i) {
myViewHolder.txtValue.setText(String.valueOf(list.get(i).getValuee()));
myViewHolder.txtCategory.setText(list.get(i).getCategory());
myViewHolder.imgInorEx.setBackgroundColor(list.get(i).getImg_inorex());
myViewHolder.imgCategory.setImageResource(list.get(i).getImg_category());
myViewHolder.txtDate.setText(list.get(i).getDate());
myViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
list.remove(myViewHolder.getAdapterPosition());
notifyDataSetChanged();
return false;
}
});
}
#Override
public int getItemCount() {
return list.size();
}}
i hope this help you.
if you want to remove item you should do this:
first remove item:
phones.remove(position);
in next step you should notify your recycler adapter that you remove an item by this code:
notifyItemRemoved(position);
notifyItemRangeChanged(position, phones.size());
but if you change an item do this:
first change a parameter of your object like this:
Service s = services.get(position);
s.done = "Cancel service";
services.set(position,s);
or new it like this :
Service s = new Service();
services.set(position,s);
then notify your recycler adapter that you modify an item by this code:
notifyItemChanged(position);
notifyItemRangeChanged(position, services.size());
hope helps you.
String str = arrayList.get(position);
arrayList.remove(str);
MyAdapter.this.notifyDataSetChanged();
To Method onBindViewHolder Write This Code
holder.remove.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Cursor del=dbAdapter.ExecuteQ("delete from TblItem where Id="+values.get(position).getId());
values.remove(position);
notifyDataSetChanged();
}
});
Incase Anyone wants to implement something like this in Main class instead of Adapter class, you can use:
public void removeAt(int position) {
peopleListUser.remove(position);
friendsListRecycler.getAdapter().notifyItemRemoved(position);
friendsListRecycler.getAdapter().notifyItemRangeChanged(position, peopleListUser.size());
}
where friendsListRecycler is the Adapter name
you must to remove this item from arrayList of data
myDataset.remove(holder.getAdapterPosition());
notifyItemRemoved(holder.getAdapterPosition());
notifyItemRangeChanged(holder.getAdapterPosition(), getItemCount());
//////// set the position
holder.cancel.setTag(position);
///// click to remove an item from recycler view and an array list
holder.cancel.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int positionToRemove = (int)view.getTag(); //get the position of the view to delete stored in the tag
mDataset.remove(positionToRemove);
notifyDataSetChanged();
}
});
make interface into custom adapter class and handling click event on recycler view..
onItemClickListner onItemClickListner;
public void setOnItemClickListner(CommentsAdapter.onItemClickListner onItemClickListner) {
this.onItemClickListner = onItemClickListner;
}
public interface onItemClickListner {
void onClick(Contact contact);//pass your object types.
}
#Override
public void onBindViewHolder(ItemViewHolder holder, int position) {
// below code handle click event on recycler view item.
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onItemClickListner.onClick(mContectList.get(position));
}
});
}
after define adapter and bind into recycler view called below code..
adapter.setOnItemClickListner(new CommentsAdapter.onItemClickListner() {
#Override
public void onClick(Contact contact) {
contectList.remove(contectList.get(contectList.indexOf(contact)));
adapter.notifyDataSetChanged();
}
});
}
In case you are wondering like I did where can we get the adapter position in the method getadapterposition(); its in viewholder object.so you have to put your code like this
mdataset.remove(holder.getadapterposition());
In the activity:
mAdapter.updateAt(pos, text, completed);
mAdapter.removeAt(pos);
In the your adapter:
void removeAt(int position) {
list.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, list.size());
}
void updateAt(int position, String text, Boolean completed) {
TodoEntity todoEntity = list.get(position);
todoEntity.setText(text);
todoEntity.setCompleted(completed);
notifyItemChanged(position);
}
in 2022, after trying everything the whole internet given below is the answer
In MyViewHolder class
private myAdapter adapter;
inside MyViewHolder function initalise adapter
adapter = myAdapter.this
inside onclick
int position = getAdapterPosition()
list.remove(position);
adapter.notifyItemRemoved(position);

Change fragment visibility in runtime when the fragment was created using FragmentPagerAdapter

I'm trying to create my first Android app that looks like following: there is main activity with multiple fragments initialized by FragmentPagerAdapter. There is another activity (SettingsActivity) where I want to list all the fragment names and allow hiding some of them. To hide them I want to use the following:
FragmentManager fm=getFragmentManager();
Fragment myFragment=fm.findFragmentByTag("tag");
fm.beginTransaction().hide(myFragment).commit();
The problem is that I don't know fragment id or tag, not sure if they exist. How I can get them? Should I switch to XML definition to make it possible?
Adapter:
public class TabsPagerAdapter extends FragmentPagerAdapter {
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
switch (index){
case 0:
return new CoverFragment();
case 1:
return new NumbersConverterFragment();
case 2:
return new TempConverterFragment();
case 3:
return new LengthConverterFragment();
case 4:
return new AreaConverterFragment();
case 5:
return new VolumeConverterFragment();
case 6:
return new WeightConverterFragment();
case 7:
return new SpeedConverterFragment();
}
return null;
}
#Override
public int getCount() {
return 8;
}
Main activity:
public class MainActivity extends FragmentActivity implements ActionBar.TabListener {
private ViewPager viewPager;
private TabsPagerAdapter tabsPagerAdapter;
private ActionBar actionBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
String[] tabs={getString(R.string.title_section0), getString(R.string.title_section1),getString(R.string.title_section2)};
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager=(ViewPager) findViewById(R.id.pager);
actionBar=getActionBar();
tabsPagerAdapter=new TabsPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(tabsPagerAdapter);
actionBar.setHomeButtonEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
for(String tab : tabs){
actionBar.addTab(actionBar.newTab().setText(tab).setTabListener(this));
}
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// on changing the page
// make respected tab selected
actionBar.setSelectedNavigationItem(position);
}
...
});
}
Fragment layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#fbfdfb"
>
<TextView android:text="#string/celsius_" android:id="#+id/textView1"
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
<EditText android:text="" android:id="#+id/txtCelsius" android:layout_width="match_parent"
android:layout_height="wrap_content"></EditText>
<TextView android:text="#string/fahrenheit_" android:id="#+id/textView1"
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
<EditText android:text="" android:id="#+id/txtFahrenheit" android:layout_width="match_parent"
android:layout_height="wrap_content"></EditText>
<TextView android:text="#string/kelvin_" android:id="#+id/textView1"
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
<EditText android:text="" android:id="#+id/txtKelvin" android:layout_width="match_parent"
android:layout_height="wrap_content"></EditText>
</LinearLayout>
Fragment class:
public class TempConverterFragment extends Fragment {
EditText txtCelsius, txtFahrenheit, txtKelvin;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.temp_converter_fragment, container, false);
txtCelsius = (EditText) rootView.findViewById(R.id.txtCelsius);
txtFahrenheit = (EditText) rootView.findViewById(R.id.txtFahrenheit);
txtKelvin = (EditText) rootView.findViewById(R.id.txtKelvin);
...
}
...
}
Thanks in advance.
If SettingsActivity is not the Activity holding the FragmentPagerAdapter, then you would have to re-create all the fragments. The nature of a fragment is to be closely tied to it's activity.
If SettingsActivity is the Activity holding the FragmentPagerAdapter, then As I recall, FragmentPagerAdapter will initialize all the 8 fragments as soon as possible to have them ready when you swipe, unlike FragmentStatePagerAdapter. This means that you should (I think) be able to create each fragment in the constructor TabsPagerAdapter and keeping a reference to them, which you could access using getter methods on the TabsPagerAdapter.
Here is an example of how to get easy access to your pageradapter fragments:
public class DisplayPagerAdapter extends FragmentStatePagerAdapter {
private static final String TAG = "DisplayPagerAdapter";
SparseArray<DisplayFragment> registeredFragments = new SparseArray<DisplayFragment>();
#Inject DisplayCoreModule display;
public DisplayPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return (display != null && display.getPagesCount() > 0) ? display.getPagesCount() : 1;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
Log.d(TAG, "getItem " + position);
return DisplayFragment.newInstance(position);
}
#Override
public CharSequence getPageTitle(int position) {
if (display != null && display.getPagesCount() > 0) {
return "Side " + (position+1);
} else {
return super.getPageTitle(position);
}
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Log.d(TAG, "instantiateItem " + position);
DisplayFragment fragment = (DisplayFragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
Log.d(TAG, "destroyItem " + position);
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
public SparseArray<DisplayFragment> getRegisteredFragments() {
return registeredFragments;
}
}
Now if you implement this usage of registeredFragments , you can call tabsPagerAdapter.getRegisteredFragment(2) to get your TempConverterFragment.
SparseArray<DisplayFragment> should be SparseArray<Fragment> in your case
Now this does not solve the your SettingsActivity problem. But if I understand you correctly, then adding the fragments your want directly in the layout XML of SettingsActivity would make sense. Then it would be easy to temporarily hide the fragments or whatever using:
FragmentManager fm=getFragmentManager();
Fragment myFragment=fm.findFragmentById(R.id.frag_tempconverter)
fm.beginTransaction().hide(myFragment).commit();
Notice the use of findFragmentById. The tag is usually used for dynamically added fragments (atleast in my mind). The findFragmentById will surely return a fragment if it is defined in the XML layout but just to be clear, it will be a new instance of the fragment.
To address your questions:
What if I move the fragments to the main activity XML? Won't it make things simpler
Do not think so, the updated answer shows how to easily access the fragments (from within your main activity).
Though not sure I can use FragmentManager in SettingsActivity
Sure you can. You can add new fragments, access available fragments (from predefined XML using findById or dynamically added using findByTag). You cannot, however, access the same instance of the fragment as was kept by your main activity.
To share information between the fragments and the two activities, you need to persist the state of your fragments somehow (which is a different topic).
All in all I think you are on the right path, you just need to combine the right pieces of the puzzle :)

Fragment replace doesn't work always

This is my situation:
I have several fragments added dynamicly to an FragmentStatePagerAdapter, this works fine. But now i want to be able to replace an fragment when I push on an button.
public class QuestionFragment extends UpperFragment {
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
setRetainInstance(true);
CustomViewPager.enabled = true;
View rootView = inflater.inflate(R.layout.question, container, false);
Button btn = ((Button) rootView.findViewById(R.id.bQuestion));
if (how == true) {
btn.setVisibility(View.VISIBLE);
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// lijst afgaan met alle how en kijken welke id nodig is
for (int i = 0; i < XmlParserSax.howFragments.size(); i++) {
Fragment how = XmlParserSax.howFragments.get(i);
if (howId.equals(((UpperFragment) how).getIdNum())) {
FragmentTransaction transaction = getFragmentManager()
.beginTransaction();
transaction
.replace(R.id.flQuestion, how, "howFragment")
.setTransition(
FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.addToBackStack(null).commit();
break;
}
}
}
});
} else {
btn.setVisibility(View.GONE);
}
return rootView;
}
So when i press the button the current layout (R.id.flQuestion) is replaced with the new fragment. This works, but the tricky part comes here:
When I slide to the next fragment and the slide to the fragment with the button it keeps working but if i slide 2 times to the next fragment (of the same type QuestionFragment) it does the functionallity of the new fragment but it doesn't show the new fragment.. So it seems that it can't replace the R.id.flQuestion because it is stored in memory maybe?
I need to be sure that the fragment is always replaced even if the next 2 fragments are of the same type and same layout (R.id.flQuestion)..
This is the class layout of the new frag
public class HowFragment extends UpperFragment {
..
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
setRetainInstance(true);
View rootView = inflater.inflate(R.layout.how, container, false);
//if back key pressed return to layout of Question
rootView.setFocusableInTouchMode(true); //this line is important
rootView.requestFocus();
rootView.setOnKeyListener( new View.OnKeyListener()
{
#Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
if( keyCode == KeyEvent.KEYCODE_BACK )
{
CustomViewPager.enabled = true;
return false;
}
return true;
}
} );
//don't allow pushing button again
rootView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
return true;
}
});
return rootView;
}
Also important to tell: i'm using framelayouts for both the fragments (so no hard coded fragment tag in the xml)
To make it clear:
This happens when the next fragment is from different class, no problem
This happens when next fragments are from the same layout and class:
I'll have to test the code, but from what I see, easiest way to make this work is to grab new Fragment even if it is the same fragment class.
Fragment how = XmlParserSax.howFragments.get(i);
If this function is returning new instance of a fragment, it should work.
Hope that helps
Edit :
I'm pretty sure the activity can access the button after the fragments are created.
Otherwise you need a handler to pass the click to handle it in the adapter. I'm seeing the your list of fragments are static (Not recommended). Since you haven't added any codes for how you setup the pageradapter, I have no idea what list you are using, but you need to change out the item in that list. From the Activity where you initialized the pager, you can call the public function to replace the current pager item pager. (you can use ViewPager.getCurrentItem())
I haven't tested, so you might have to tweak and play around to perfect it.
Hope this helps.
Here is a sample :
public static class MyAdapter extends FragmentStatePagerAdapter {
ArrayList<Fragment> fragmentArray = new ArrayList<Fragment();
public MyAdapter(FragmentManager fm) {
super(fm);
}
//didn't put in the function to populate the list with fragments
public void replaceItem(Fragment newFrag, int pos){
fragmentArray.remove(pos);
fragmentArray.add(pos,newFrag);
notifyDataSetChanged();
}
#Override
public int getCount() {
return fragmentArray.size();
}
#Override
public Fragment getItem(int position) {
return fragmentArray.get(position);
}
}

Categories