My fragment has a Recycler View. Therefore I have a RecyclerView Adapter too. From this Adapter, I am opening an AlertDialog. When I click OK, I need to pass the onclick event from my DialogFragment back to my RecyclerView Adapter.
Currently, I am doing it like here, but this passes the event back to the activity and not to the RecyclerView Adapter.
public class FreshwaterRecyclerViewAdapter extends RecyclerView.Adapter<FreshwaterRecyclerViewAdapter.ViewHolder> implements BiotopeDialogFragment.NoticeDialogListener {
private List<Biotope> data;
private LayoutInflater layoutInflater;
FreshwaterRecyclerViewAdapter(Context context, List<Biotope> data) {
this.layoutInflater = LayoutInflater.from(context);
this.data = data;
}
//The dialog fragment receives a reference to this Activity through the
//Fragment.onAttach() callback, which it uses to call the following methods
//defined by the NoticeDialogFragment.NoticeDialogListener interface
#Override
public void onDialogPositiveClick(DialogFragment dialog) {
notifyItemInserted(getItemCount()-1);
}
#Override
public void onDialogNegativeClick(DialogFragment dialog) {
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView;
if (viewType == R.layout.biotope_cardview){
itemView = layoutInflater.inflate(R.layout.biotope_cardview, parent, false);
} else {
itemView = layoutInflater.inflate(R.layout.biotope_add_button, parent, false);
}
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
if (position == data.size()) {
holder.imageButtonAddBiotope.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
FragmentManager fragmentManager = ((AppCompatActivity) layoutInflater.getContext()).getSupportFragmentManager();
DialogFragment dialog = new BiotopeDialogFragment();
dialog.show(fragmentManager, "NoticeDialogFragment");
}
});
} else {
holder.textViewBiotopeTitle.setText(getItem(position).name);
Picasso.get().load(Uri.parse(getItem(position).imageUri)).into(holder.imageViewBiotope);
LastValuesRecyclerViewAdapter recyclerAdapter = new LastValuesRecyclerViewAdapter(layoutInflater.getContext(), getData());
holder.recyclerViewLastValues.setLayoutManager(new LinearLayoutManager(layoutInflater.getContext(), LinearLayoutManager.HORIZONTAL, false));
holder.recyclerViewLastValues.setAdapter(recyclerAdapter);
}
}
//total number of rows
#Override
public int getItemCount() {
return data.size() + 1; //+1 for the add button
}
#Override
public int getItemViewType(int position) {
return (position == data.size()) ? R.layout.biotope_add_button : R.layout.biotope_cardview;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private TextView textViewBiotopeTitle;
private ImageView imageViewBiotope;
private RecyclerView recyclerViewLastValues;
private ImageButton imageButtonAddBiotope;
public ViewHolder(View view) {
super(view);
textViewBiotopeTitle = (TextView) view.findViewById(R.id.textViewBiotopeTitle);
imageViewBiotope = (ImageView) view.findViewById(R.id.imageViewBiotopeCardview);
recyclerViewLastValues = (RecyclerView) view.findViewById(R.id.recyclerViewLastValues);
imageButtonAddBiotope = (ImageButton) view.findViewById(R.id.imageButtonAddBiotope);
}
}
Biotope getItem(int id) {
return data.get(id);
}
private List<String> getData() {
List<String> data = new ArrayList<>();
data.add("PO4");
data.add("NO3");
return data;
}
}
This is my dialog.
public class BiotopeDialogFragment extends DialogFragment {
private NoticeDialogListener listener;
public interface NoticeDialogListener {
public void onDialogPositiveClick(DialogFragment dialog);
public void onDialogNegativeClick(DialogFragment dialog);
}
//Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
//Verify that the host activity implements the callback interface
try {
//Instantiate the NoticeDialogListener so we can send events to the host
listener = (NoticeDialogListener) context;
} catch (ClassCastException e) {
//The activity doesn't implement the interface, throw exception
throw new ClassCastException("FreshwaterRecyclerViewAdapter must implement NoticeDialogListener | Context: " + context.toString());
}
}
public static final String TAG = "biotope_dialog_fragment";
private ActivityResultLauncher<Intent> activityResultLauncher;
private Uri imageUri = null;
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext());
LayoutInflater inflater = requireActivity().getLayoutInflater();
//Inflate and set the layout for the dialog
//Pass null as the parent view because its going in the dialog layout
View view = inflater.inflate(R.layout.fragment_dialog_biotope, null, false);
builder.setView(view);
View colorPickerPreviewView = view.findViewById(R.id.colorPickerPreviewView);
ColorPickerView colorPickerView = view.findViewById(R.id.colorPickerView);
ImageView imageViewBiotope = view.findViewById(R.id.imageViewBiotopePreview);
TextInputEditText textFieldBiotopeName = view.findViewById(R.id.textFieldBiotopeName);
builder.setTitle("New biotope")
.setPositiveButton("ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
BiotopeDatabase database = BiotopeDatabase.getDbInstance(requireContext().getApplicationContext());
Biotope biotope = new Biotope();
if (textFieldBiotopeName.getText() != null) {
biotope.name = textFieldBiotopeName.getText().toString();
} else {
biotope.name = "";
}
if (imageUri != null) {
biotope.imageUri = imageUri.toString();
} else {
biotope.imageUri = "";
}
biotope.color = colorPickerView.getColor();
database.biotopeDao().insertAll(biotope);
//Send the positive button event back to the host activity
listener.onDialogPositiveClick(BiotopeDialogFragment.this);
}
})
.setNegativeButton("noke", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
//Send the negative button event back to the host activity
listener.onDialogNegativeClick(BiotopeDialogFragment.this);
Objects.requireNonNull(BiotopeDialogFragment.this.getDialog()).cancel();
}
});
return builder.create();
}
public static BiotopeDialogFragment display(FragmentManager fragmentManager) {
BiotopeDialogFragment fragment = new BiotopeDialogFragment();
fragment.show(fragmentManager, TAG);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
This is my fragment building the RecyclerView. Alternatively, I can pass the event back to the fragment if it is not possible to pass it to the adapter.
public class BiotopesFragment extends Fragment {
private FreshwaterRecyclerViewAdapter recyclerAdapter;
public static BiotopesFragment newInstance(String param1, String param2) {
BiotopesFragment fragment = new BiotopesFragment();
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_biotopes, container, false);
RecyclerView recyclerViewFreshwater = (RecyclerView) root.findViewById(R.id.recyclerViewFreshwater);
recyclerAdapter = new FreshwaterRecyclerViewAdapter(getContext(), getData());
recyclerViewFreshwater.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
recyclerViewFreshwater.setAdapter(recyclerAdapter);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL);
recyclerViewFreshwater.addItemDecoration(dividerItemDecoration);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(ItemTouchHelper.START | ItemTouchHelper.END, 0) {
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, #NonNull RecyclerView.ViewHolder target) {
int fromPosition = viewHolder.getAdapterPosition();
int toPosition = target.getAdapterPosition();
Collections.swap(getData(), fromPosition, toPosition);
recyclerView.getAdapter().notifyItemMoved(fromPosition, toPosition);
return false;
}
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
}
});
itemTouchHelper.attachToRecyclerView(recyclerViewFreshwater);
return root;
}
private List<Biotope> getData() {
BiotopeDatabase database = BiotopeDatabase.getDbInstance(requireContext().getApplicationContext());
BiotopeDao biotopeDao = database.biotopeDao();
return biotopeDao.getAll();
}
}
Ideal Way to do this to create all UI component in Fragment not in the adapter . Create an interface to handle events in the fragment and provide callback to the fragment from adapter. now your Fragment should create all the UI component .
Now coming to the
How to provide callback from dialog fragment to Fragment.
You can use setTargetFragment but its deprecated . Now you can use setFragmentResultListener instead of setTargetFragment(), its the safest way i think. Once you get the result back in fragment you can call any method of your adapter.
Related
I am giving product id with barcode scanner. I can add product to listView but when i try to increase or decrease amount of the product. It doesn't update UI. I used Toast message to see weather list is updated, it updates list but doesn't update UI
I have tried to use runOnUiThread() but i couldn't find any solution. How to update UI can you please help me
custom_lisView_row
BaseActivity which keeps MainFragment on it
public class BaseActivity extends AppCompatActivity {
public static final String MAIN_FRAGMENT = "mainFragment";
public static final String PRODUCTS = "products";
FragmentManager fragmentManager;
Dialog dialog ;
public static ArrayList<MyProduct> myProductList = new ArrayList<>();
public static MyTablet myTablet = new MyTablet();
Activity mActivity;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base);
//Initialize fragment manager
fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.fl_BaseActivity, new MainFragment()).commit();
//Create database
mDatabase = FirebaseDatabase.getInstance().getReference();
dialog = new Dialog(this);
//Runs when i enter product id
initScanner();
}
public void updateMyProductList(MyProduct myProduct){
for(int i= 0 ; i< myProductList.size() ; i++ ){
MyProduct temp = myProductList.get(i);
if (temp.getId().equals(myProduct.getId())) {
temp.setAmount(temp.getAmount() + myProduct.getAmount());
myProductList.set(i, temp);
return;
}
}
myProductList.add(myProduct);
updateMainFragment();
}
private void initScanner() {
mDatabase.child(PRODUCTS).child(finalData).get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull Task<DataSnapshot> task) {
MyProduct myProduct = task.getResult().getValue(MyProduct.class);
myProduct.setAmount(1);
dialog.setContentView(R.layout.custom_product_dialog);
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
dialog.setCancelable(false);
TextView tv_addBasket_product_dialog = dialog.findViewById(R.id.tv_addBasket_product_dialog);
tv_addBasket_product_dialog.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
updateMyProductList(myProduct);
dialog.dismiss();
}
});
dialog.show();
}
};
}
public void updateMainFragment() {
if (isExist(MAIN_FRAGMENT)) {
Fragment fragment = findFragment(MAIN_FRAGMENT);
((MainFragment) fragment).updateMyList();
}
}
//Add fragments to BaseActivity
public void addFragments(Fragment fragment, String tag) {
fragmentManager.beginTransaction().add(R.id.fl_BaseActivity, fragment, tag).commit();
}
//Replace fragments to BaseActivity
public void replaceFragments(Fragment fragment, String tag) {
fragmentManager.beginTransaction().replace(R.id.fl_BaseActivity, fragment, tag).commit();
}
//Remove fragment from BaseActivity
public void removeFragment(String tag) {
Fragment fragmentB = fragmentManager.findFragmentByTag(tag);
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
if (fragmentB != null) {
fragmentTransaction.remove(fragmentB);
fragmentTransaction.commit();
}
}
// finds fragment and returns it
// It may return null first check fragment is exist. use isExist() method
public Fragment findFragment(String tag) {
Fragment fragment = fragmentManager.findFragmentByTag(tag);
return fragment;
}
//Check fragment exist in BaseActivity
public boolean isExist(String tag) {
Fragment fragmentB = fragmentManager.findFragmentByTag(tag);
if (fragmentB != null) {
return true;
}
return false;
}
}
MainFragment
public class MainFragment extends Fragment {
ListView lv_MainFragment;
public MyProductListAdapter myListAdapter;
public static ArrayList<MyProduct> myProductList;
Activity mActivity;
#Override
public void onAttach(Context context) {
super.onAttach(context);
mActivity = getActivity();
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myProductList = BaseActivity.myProductList;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
lv_MainFragment = view.findViewById(R.id.lv_MainFragment);
myListAdapter = new MyProductListAdapter(mActivity.getApplicationContext(), R.layout.custom_product_list_row, myProductList);
lv_MainFragment.setAdapter(myListAdapter);
return view;
}
public void updateMyList() {
myProductList = BaseActivity.myProductList;
myListAdapter.notifyDataSetChanged();
}
}
MyProductListAdapter
public class MyProductListAdapter extends ArrayAdapter<MyProduct> {
private Context mContext;
private ArrayList<MyProduct> list;
AppCompatButton acb_DecreaseAmount_productListRow, acb_IncreaseAmount_productListRow;
public MyProductListAdapter(Context context, int resource, ArrayList<MyProduct> objects) {
super(context, resource, objects);
this.mContext = context;
this.list = objects;
}
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
View view = convertView;
if (view == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.custom_product_list_row, parent, false);
tv_ProductAmount_productListRow = view.findViewById(R.id.tv_ProductAmount_productListRow);
acb_DecreaseAmount_productListRow = view.findViewById(R.id.acb_DecreaseAmount_productListRow);
acb_IncreaseAmount_productListRow = view.findViewById(R.id.acb_IncreaseAmount_productListRow);
tv_ProductAmount_productListRow.setText(String.valueOf(list.get(position).getAmount()));
acb_IncreaseAmount_productListRow.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
double productPrice = list.get(position).getPrice();
int productAmount = list.get(position).getAmount();
productAmount++;
list.get(position).setAmount(productAmount);
Toast.makeText(mContext, String.valueOf(productAmount), Toast.LENGTH_SHORT).show();
tv_ProductAmount_productListRow.setText(String.valueOf(list.get(position).getAmount()));
}
});
acb_DecreaseAmount_productListRow.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int productAmount = list.get(position).getAmount();
if (productAmount > 1) {
double productPrice = list.get(position).getPrice();
productAmount--;
list.get(position).setAmount(productAmount);
Toast.makeText(mContext, String.valueOf(productAmount), Toast.LENGTH_SHORT).show();
tv_ProductAmount_productListRow.setText(String.valueOf(list.get(position).getAmount()));
}
}
});
}
return view;
}
}
Hej Metehan,
your use case sounds perfect for a RecyclerView with a ListAdapter. You just submit a new list of products to the adapter and it will handle the updating and notifying for you.
In my project, I have a PagerAdapter to inflate CardViews that hold text objects created by the user.
On each CardView I have edit and delete buttons to access methods to do with the ArrayList holding the text objects.
I have coded an interface in my adapter to handle clicks made within the text object creation activity. However, my instatiateItem method in my adapter is now showing on the #Override annotation "Method does not override method from its superclass". When I remove the #Override annotation I get an error in my Logcat:
java.lang.UnsupportedOperationException: Required method instantiateItem was not overridden
How do I implement a click listener for buttons on a CardView in a PagerAdapter?
Adapter code:
public class CreateAdapter extends PagerAdapter {
private List<PdfPage> pagesList;
private LayoutInflater layoutInflater;
private Context context;
private onItemClickListener mListener;
public interface onItemClickListener{
//click method for edit button
void onEditClick(int position);
//click method for delete button
void onDeleteClick(int position);
}
public void setOnItemClickListener(onItemClickListener listener){
mListener = listener;
}
public CreateAdapter(List<PdfPage> pagesList, Context context) {
this.pagesList = pagesList;
this.context = context;
}
#Override
public int getCount() {
return pagesList.size();
}
#Override
public boolean isViewFromObject(#NonNull View view, #NonNull Object object) {
return view.equals(object);
}
#NonNull
#Override
public Object instantiateItem(#NonNull ViewGroup container, final int position, final onItemClickListener listener) {
layoutInflater = LayoutInflater.from(context);
View view = null;
if(pagesList.get(position).getPageType() == "Text") {
view = layoutInflater.inflate(R.layout.create_text_cardview, container, false);
TextView inputText = view.findViewById(R.id.inputTextView);
ImageButton editButton = view.findViewById(R.id.imageButtonEdit);
ImageButton deleteButton = view.findViewById(R.id.imageButtonDelete);
//if this works, see 12:30 of video for image getter
inputText.setText(pagesList.get(position).getPageText());
//edit click listener
editButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null){
int position2 = pagesList.indexOf(pagesList.get(position));
listener.onEditClick(position2);
}
}
});
//delete click listener
deleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null){
int position3 = pagesList.indexOf(pagesList.get(position));
listener.onDeleteClick(position3);
}
}
});
container.addView(view, 0);
}
//put else statement here for image cardView
assert view != null;
return view;
}
#Override
public void destroyItem(#NonNull ViewGroup container, int position, #NonNull Object object) {
container.removeView((View)object);
}
Activity code:
public class CreateActivity extends AppCompatActivity {
ViewPager viewPager;
CreateAdapter createAdapter;
List<PdfPage> pdfPageList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create);
pdfPageList = new ArrayList<>();
createAdapter = new CreateAdapter(pdfPageList, this);
viewPager = findViewById(R.id.viewPager);
viewPager.setAdapter(createAdapter);
viewPager.setPadding(130, 0, 130, 0);
}
#Override
protected void onStart() {
super.onStart();
createAdapter = new CreateAdapter(pdfPageList, this);
viewPager = findViewById(R.id.viewPager);
viewPager.setAdapter(createAdapter);
viewPager.setPadding(130, 0, 130, 0);
createAdapter.setOnItemClickListener(new CreateAdapter.onItemClickListener() {
#Override
public void onEditClick(int position) {
}
#Override
public void onDeleteClick(int position) {
pdfPageList.remove(position);
}
});
}
Your instantiateItem method does not override from it's superclass because you added an extra argument final onItemClickListener listener.
As you are using the same clickListener for all entries and passing the item position as an argument, I suggest that you pass the onItemClickListener in the adapter constructor, or just set it via
public void setOnItemClickListener(onItemClickListener listener){
mListener = listener;
}
Adapter code:
public class CreateAdapter extends PagerAdapter {
private List<PdfPage> pagesList;
private LayoutInflater layoutInflater;
private Context context;
private onItemClickListener mListener;
public interface onItemClickListener{
//click method for edit button
void onEditClick(int position);
//click method for delete button
void onDeleteClick(int position);
}
public void setOnItemClickListener(onItemClickListener listener){
mListener = listener;
}
public CreateAdapter(List<PdfPage> pagesList, Context context) {
this.pagesList = pagesList;
this.context = context;
}
#Override
public int getCount() {
return pagesList.size();
}
#Override
public boolean isViewFromObject(#NonNull View view, #NonNull Object object) {
return view.equals(object);
}
#NonNull
#Override
public Object instantiateItem(#NonNull ViewGroup container, final int position) {
layoutInflater = LayoutInflater.from(context);
View view = null;
if(pagesList.get(position).getPageType() == "Text") {
view = layoutInflater.inflate(R.layout.create_text_cardview, container, false);
TextView inputText = view.findViewById(R.id.inputTextView);
ImageButton editButton = view.findViewById(R.id.imageButtonEdit);
ImageButton deleteButton = view.findViewById(R.id.imageButtonDelete);
//if this works, see 12:30 of video for image getter
inputText.setText(pagesList.get(position).getPageText());
//edit click listener
editButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mlistener != null){
int position2 = pagesList.indexOf(pagesList.get(position));
mlistener.onEditClick(position2);
}
}
});
//delete click listener
deleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mlistener != null){
int position3 = pagesList.indexOf(pagesList.get(position));
mlistener.onDeleteClick(position3);
}
}
});
container.addView(view, 0);
}
//put else statement here for image cardView
assert view != null;
return view;
}
#Override
public void destroyItem(#NonNull ViewGroup container, int position, #NonNull Object object) {
container.removeView((View)object);
}
And your Activity code could stay as it is.
since several days I try to handle data in recyclerview in viewpager2. The viewpager has an adapter managing the data in recyclerview. But everything I try to do seems to not work. Maybe I missundersteand the purpose or something. I hope you can help me.
This activity manages the viewpager and its adapter. It sends the data to the inner recyclerview:
public class AudioFilePanel extends AppCompatActivity
{
private String currentTab;
private ViewPagerAdapter adapter;
private ViewPager2 viewPager;
private TabLayout tabLayout;
private Map<String, List<String>> content;
private String path;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_audio_file_panel);
viewPager = findViewById(R.id.view_pager2);
tabLayout = findViewById(R.id.tabs);
Button addFilesByTag = findViewById(R.id.add_files_with_tag);
if (null == currentTab)
{
currentTab = "music";
}
content = listByTag();
adapter = new ViewPagerAdapter(getApplicationContext(), new ArrayList<>(content.values()));
viewPager.setAdapter(adapter);
new TabLayoutMediator(tabLayout, viewPager,
(tab, position) -> tab.setText(content.keySet().toArray()[position].toString())).attach();
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener()
{
#Override
public void onTabSelected(TabLayout.Tab tab)
{
currentTab = tab.getText().toString();
}
#Override
public void onTabUnselected(TabLayout.Tab tab)
{
}
#Override
public void onTabReselected(TabLayout.Tab tab)
{
}
});
addFilesByTag.setOnClickListener(l ->
{
Intent fileBrowser = new Intent(AudioFilePanel.this, FileBrowser.class);
startActivityForResult(fileBrowser, 1);
});
}
private void updateViweData()
{
content = listByTag();
adapter = new ViewPagerAdapter(getApplicationContext(), new ArrayList<>(content.values()));
viewPager.setAdapter(adapter);
}
private Map<String, List<String>> listByTag()
{
Map<String, List<String>> result = new HashMap<>();
DirectoryDao dao = new DirectoryDao(getApplicationContext());
String[] categories = {"music", "ambience", "effect"};
for (String category : categories)
{
List<String> directories =
dao.getDirectoriesForCategory(category).stream().map(Directory::getPath).collect(Collectors.toList());
result.put(category, directories);
}
return result;
}
This is the view pager adapter. It takes the data directly from the activity and should trigger data update in the inner recyclerview every time when an item will be clicked. See line 118-128:
public class ViewPagerAdapter extends RecyclerView.Adapter<ViewPagerAdapter.ViewHolder>
{
private List<List<String>> filesListsByCategory;
private LayoutInflater mInflater;
private Context ctx;
private ItemListAdapter adapter;
private List<String> categoryFiles;
public ViewPagerAdapter(Context context, List<List<String>> data)
{
this.mInflater = LayoutInflater.from(context);
this.filesListsByCategory = data;
this.ctx = context;
}
#NotNull
#Override
public ViewHolder onCreateViewHolder(#NotNull ViewGroup parent, int viewType)
{
return new ViewHolder(mInflater.inflate(R.layout.item_viewpager, parent, false));
}
#Override
public void onBindViewHolder(ViewHolder holder, int position)
{
FileBrowserService fbs = new FileBrowserService();
categoryFiles = filesListsByCategory.get(position);
adapter = new ItemListAdapter(categoryFiles, new ItemList.OnListFragmentInteractionListener()
{
#Override
public void onListFragmentInteraction(String item)
{
categoryFiles = fbs.getFiles(categoryFiles.get(position));
categoryFiles.add(0, "previous directory");
updateUi(adapter, holder);
}
});
holder.myView.setAdapter(adapter);
}
private void updateUi(ItemListAdapter adapter, ViewHolder holder)
{
adapter.notifyDataSetChanged();
holder.myView.setAdapter(adapter);
}
#Override
public int getItemCount()
{
return filesListsByCategory.size();
}
class ViewHolder extends RecyclerView.ViewHolder
{
RecyclerView myView;
RelativeLayout relativeLayout;
ViewHolder(View itemView)
{
super(itemView);
myView = itemView.findViewById(R.id.my_list);
myView.setLayoutManager(new LinearLayoutManager(ctx));
relativeLayout = itemView.findViewById(R.id.container);
}
}
}
It is the adapter of inner recyclerview. It represents just a list of elements:
public class ItemListAdapter extends RecyclerView.Adapter<ItemListAdapter.ViewHolder>
{
private List<String> files;
private final OnListFragmentInteractionListener mListener;
public ItemListAdapter(List<String> items, OnListFragmentInteractionListener listener)
{
files = items;
mListener = listener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.fragment_item, parent, false));
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position)
{
holder.file = files.get(position);
holder.mContentView.setText(files.get(position));
holder.mView.setOnClickListener(v ->
{
if (null != mListener)
{
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
mListener.onListFragmentInteraction(holder.file);
}
});
}
#Override
public int getItemCount()
{
return files.size();
}
class ViewHolder extends RecyclerView.ViewHolder
{
private final View mView;
private final TextView mContentView;
private String file;
private ViewHolder(View view)
{
super(view);
mView = view;
mContentView = view.findViewById(R.id.content);
}
}
}
The last thing is the ItemList fragment.
public class ItemList extends Fragment
{
// TODO: Customize parameter argument names
private static final String ARG_COLUMN_COUNT = "column-count";
// TODO: Customize parameters
private int mColumnCount = 1;
private OnListFragmentInteractionListener mListener;
/**
* Mandatory empty constructor for the fragment manager to instantiate the fragment (e.g. upon screen orientation
* changes).
*/
public ItemList()
{
}
// TODO: Customize parameter initialization
#SuppressWarnings("unused")
public static ItemList newInstance(int columnCount)
{
ItemList fragment = new ItemList();
Bundle args = new Bundle();
args.putInt(ARG_COLUMN_COUNT, columnCount);
fragment.setArguments(args);
return fragment;
}
#Override
public void onAttach(Context context)
{
super.onAttach(context);
if (context instanceof OnListFragmentInteractionListener)
{
mListener = (OnListFragmentInteractionListener) context;
} else
{
throw new RuntimeException(context.toString() + " must implement OnListFragmentInteractionListener");
}
}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (getArguments() != null)
{
mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_item_list, container, false);
if (view instanceof RecyclerView)
{
Context context = view.getContext();
RecyclerView recyclerView = (RecyclerView) view;
if (mColumnCount <= 1)
{
recyclerView.setLayoutManager(new LinearLayoutManager(context));
} else
{
recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount));
}
recyclerView.setAdapter(new ItemListAdapter(getArguments().getStringArrayList("list"), mListener));
}
return view;
}
#Override
public void onDetach()
{
super.onDetach();
mListener = null;
}
/**
* This interface must be implemented by activities that contain this fragment to allow an interaction in this
* fragment to be communicated to the activity and potentially other fragments contained in that activity.
* <p/>
* See the Android Training lesson
* <a href= "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnListFragmentInteractionListener
{
// TODO: Update argument type and name
void onListFragmentInteraction(String item);
}
}
When I click, the data won't update. What I would like to have is:
This is what you have to do
add this method into your adapter class
public void updateData(List<String> filesList, int flag) {
if (flag == 0) { //append
for (int i = 0; i < filesList.size(); i++) {
files.add(messageList.get(i));
notifyItemInserted(getItemCount());
}
} else { //clear all
files.clear();
notifyDataSetChanged();
}
}
Then whenever you need to update recycle view call like below
mItemListAdapter.updateData(yourNewListOfItems, 0);
if you need to reset recycle-view call like below
mItemListAdapter.updateData(null, 1);
Add notifyDataSetChanged() in your adapter
I have an Fragment with recycleview where I populate it with json items from internet.
It load fine and Next step I want to is open new Activity when any row is clicked. It works in activity, thus I modified the same code for fragment but for fragment it throws exception in line
mExampleAdapter.setOnItemClickListener(getActivity());
with errror setOnItemClickListener of refrence adatper cannot be applied to Fragment activty and thus when I change line to
(ExampleAdapter.OnItemClickListener)
and when i build and run . Then app crashes with error that Mainactivity which holds framgnet cannot be cast in to .ExampleAdapter$OnItemClickListener
Here is my whole Fragment class
public class Mynotes extends Fragment implements ExampleAdapter.OnItemClickListener{
public static final String YTD_LINK = "link";
private RecyclerView mRecyclerView;
private ExampleAdapter mExampleAdapter;
private ArrayList<ExampleItem> mExampleList;
private RequestQueue mRequestQueue;
String url="https://api.myjson.com/bins/16mecx";
public Mynotes() {
}
#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.activity_jsonfeed, container, false);
mRecyclerView = view.findViewById(R.id.recycler_view);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mRecyclerView.addItemDecoration(new MyDividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL, 36));
mExampleList = new ArrayList<>();
mExampleAdapter = new ExampleAdapter(getActivity(), mExampleList);
mRecyclerView.setAdapter(mExampleAdapter);
mExampleAdapter.setOnItemClickListener((ExampleAdapter.OnItemClickListener) getActivity());
parseJSON();
return view;
}
private void parseJSON() {
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
myProgressBar.setVisibility(View.GONE);
try {
JSONArray jsonArray = response.getJSONArray("hits");
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject hit = jsonArray.getJSONObject(i);
String videoTitle = hit.getString("title");
String link = hit.getString("link");
mExampleList.add(new ExampleItem(videoTitle, link));
mExampleAdapter.notifyDataSetChanged();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
});
mRequestQueue.add(request);
}
#Override
public void onItemClick(int position) {
Intent intent = new Intent(getActivity(), NewActiviyt.class);
ExampleItem clickedItem = mExampleList.get(position);
intent.putExtra(YTD_LINK, clickedItem.getmLink());
startActivity(intent);
}
#Override
public void onRefresh() {
}
}
and my Adapter Class is
public class ExampleAdapter extends RecyclerView.Adapter<ExampleAdapter.ExampleViewHolder> {
private Context mContext;
private ArrayList<ExampleItem> mExampleList;
private OnItemClickListener mListener;
public interface OnItemClickListener {
void onItemClick(int position);
}
public void setOnItemClickListener(OnItemClickListener listener) {
mListener = listener;
}
public ExampleAdapter(Context context, ArrayList<ExampleItem> exampleList) {
mContext = context;
mExampleList = exampleList;
}
#Override
public ExampleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(mContext).inflate(R.layout.example_item, parent, false);
return new ExampleViewHolder(v);
}
#Override
public void onBindViewHolder(ExampleViewHolder holder, int position) {
ExampleItem currentItem = mExampleList.get(position);
String title = currentItem.getTitle();
// int likeCount = currentItem.getLikeCount();
// String imageUrl = currentItem.getImageUrl();
holder.mTextViewCreator.setText(title);
// holder.mTextViewLikes.setText("Likes: " + likeCount);
// Glide.with(mContext).load(imageUrl).apply(RequestOptions.circleCropTransform()).into(holder.mImageView);
}
#Override
public int getItemCount() {
return mExampleList.size();
}
public class ExampleViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public TextView mTextViewCreator;
// public TextView mTextViewLikes;
// public ImageView mImageView;
public ExampleViewHolder(View itemView) {
super(itemView);
// mTextViewLikes = itemView.findViewById(R.id.text_view_likes);
// mImageView = itemView.findViewById(R.id.image_view);
mTextViewCreator = itemView.findViewById(R.id.text_title);
}
#Override
public void onClick(View view) {
if (mListener != null) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
mListener.onItemClick(position);
}
}
}
}
}
Thanks in advance.
replace getActivity with getContext when you work in fragment,
You can read more here
What is different between getContext and getActivity from Fragment in support library?
Your activity should implement ExampleAdapter.OnItemClickListener
I am having problems updating my RecyclerView with new data. If I press a confirmation button on a CardView in the first tab, the card should get added to the second tab but it won't update it there until I rotate the screen. I get the data for the card from reading a text file. Please advise me how to call the notifyDataSetChange method after I have added the new data to my text file. I have tried everything and all I get is NullPointerExceptions. The RecyclerViews are in fragments and I use FragementStatePagerAdapter.
I'll put some of my classes here. Ask if you need more information.
RecyclerViewAdapter.java
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewHolder> {
private List<String> mListTitle;
private List<String> mListDesc;
private List<String> mListPoints;
private List<String> mListDates;
private String fragment_tag;
public RecyclerViewAdapter() {
}
public RecyclerViewAdapter(List<List<String>> super_list, String tag) {
this.mListTitle = super_list.get(0);
this.mListDesc = super_list.get(1);
this.mListPoints = super_list.get(2);
this.mListDates = super_list.get(3);
fragment_tag = tag;
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new RecyclerViewHolder(inflater, parent, fragment_tag);
}
#Override
public void onBindViewHolder(RecyclerViewHolder holder, int position) {
holder.mTitleText.setText(mListTitle.get(position));
holder.mDescText.setText(mListDesc.get(position));
holder.mPointsText.setText(mListPoints.get(position));
if (fragment_tag.equals("completed")) {
holder.mDateText.setText(mListDates.get(position));
}
}
#Override
public int getItemCount() {
return mListTitle.size();
}
}
class RecyclerViewHolder extends RecyclerView.ViewHolder {
RecyclerView recyclerView;
RecyclerViewAdapter mAdapter;
public TextView mTitleText, mDescText, mDateText, mPointsText, popupTitle;
public Button mConfButton, popCancelBtn, popAcceptBtn;
public RecyclerViewHolder(View itemView) {
super(itemView);
}
public RecyclerViewHolder(final LayoutInflater inflater, final ViewGroup container, String tag) {
// Inflating the card layout depending on the tag parameter.
super(inflater.inflate
((tag.equals("challenges")) ? R.layout.card_view_chall : R.layout.card_view_comp, container,
false));
mTitleText = itemView.findViewById(R.id.title_holder);
mDescText = itemView.findViewById(R.id.desc_holder);
mPointsText = itemView.findViewById(R.id.points_holder);
mDateText = itemView.findViewById(R.id.date_holder);
if (tag.equals("challenges")) {
mConfButton = itemView.findViewById(R.id.card_conf_button);
mConfButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Setting the layout inflater for popup window.
LayoutInflater pInflater = (LayoutInflater) itemView.getContext().getSystemService(LAYOUT_INFLATER_SERVICE);
ViewGroup container1 = (ViewGroup) pInflater.inflate(R.layout.confirmation_popup, null);
final PopupWindow popupWindow = new PopupWindow(container1, 700, 600, true);
popupTitle = container1.findViewById(R.id.popuptext);
popAcceptBtn = container1.findViewById(R.id.accept_button);
popCancelBtn = container1.findViewById(R.id.cancel_button);
popupTitle.setText(mTitleText.getText().toString());
// Dismisses the popup window
popCancelBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
popupWindow.dismiss();
}
});
// Click listener for dialog accept button.
popAcceptBtn.setOnClickListener(new View.OnClickListener() {
String date;
#Override
public void onClick(View view) {
List<String> list = new ArrayList<>();
list.add(mTitleText.getText().toString());
list.add(mDescText.getText().toString());
list.add(mPointsText.getText().toString());
list.add(date = new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(new Date()));
// Saving data from current card into the completed challenges list.
TempDataReader reader = new TempDataReader(itemView.getContext());
new TempDataReader(itemView.getContext()).saveFile(list);
// I want to notify the dataset change here if possible!
popupWindow.dismiss();
}
});
popupWindow.showAtLocation(itemView, Gravity.CENTER, 25, 100);
}
});
}
}
}
SectionsPagerAdapter.java
public class SectionsPagerAdapter extends FragmentStatePagerAdapter{
private ViewPager viewPager;
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public void addFragment(Fragment fragment, String title){
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
#Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
}
}
CompletedFragment.java
public class CompletedFragment extends Fragment {
RecyclerView recyclerView;
RecyclerViewAdapter adapter;
public Fragment newInstance() {
return new CompletedFragment();
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.completed_fragment, container, false);
recyclerView = view.findViewById(R.id.completed_frag);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
TempDataReader reader = new TempDataReader(getActivity());
List<List<String>> super_list = reader.readCompFile();
if(super_list == null || super_list.size() < 1){
return null;
} else{
adapter = new RecyclerViewAdapter(super_list,"completed");
recyclerView.setAdapter(adapter);
return view;
}
}
}
EDIT:
Added the code for the second fragment, which is the one that should be updated after the onClick at RecyclerViewHolder-class.
You have to add a function in your adapter for adding data:
public void addData(String title, String desc, String point, String date) {
this.mListTitle.add(title);
this.mListDesc.add(desc);
this.mListPoints.add(point);
this.mListDates.add(date);
notifyDataSetChanged();
}
If you want to enable animations call notifyItemInserted() instead of notifyDataSetChanged();
It is important that you add a String to every list because in your onBindViewHolder() you get the item to display from every list with list.get(position). Otherwise you'll get a IndexOutOfBoundsException.
You can create an interface and use as a callback. Send it as a parameter of the RecyclerViewAdapter and then to your RecyclerViewHolder. When the item should be added you call the callback that will get you back to your fragment. There you can read the file again and call notifyDataSetChanged.
I know i explain pretty bad so i will try to change your code so that it does what i said:
this will be your RecyclerViewAdapter:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewHolder> {
private List<String> mListTitle;
private List<String> mListDesc;
private List<String> mListPoints;
private List<String> mListDates;
private String fragment_tag;
private Runnable callback;
public RecyclerViewAdapter() {
}
public RecyclerViewAdapter(List<List<String>> super_list, String tag, Runnable callBack) {
//add the callback here(Runnable) and save it into a local variable
this.callback=callback;
this.mListTitle = super_list.get(0);
this.mListDesc = super_list.get(1);
this.mListPoints = super_list.get(2);
this.mListDates = super_list.get(3);
fragment_tag = tag;
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new RecyclerViewHolder(inflater, parent, fragment_tag, callback);
//send the callback to your viewHolder
}
#Override
public void onBindViewHolder(RecyclerViewHolder holder, int position) {
holder.mTitleText.setText(mListTitle.get(position));
holder.mDescText.setText(mListDesc.get(position));
holder.mPointsText.setText(mListPoints.get(position));
if (fragment_tag.equals("completed")) {
holder.mDateText.setText(mListDates.get(position));
}
}
#Override
public int getItemCount() {
return mListTitle.size();
}
}
class RecyclerViewHolder extends RecyclerView.ViewHolder {
RecyclerView recyclerView;
RecyclerViewAdapter mAdapter;
public TextView mTitleText, mDescText, mDateText, mPointsText, popupTitle;
public Button mConfButton, popCancelBtn, popAcceptBtn;
public RecyclerViewHolder(View itemView) {
super(itemView);
}
public RecyclerViewHolder(final LayoutInflater inflater, final ViewGroup container, String tag, Runnable callback) {
//ADD the callback to the parameters list here
// Inflating the card layout depending on the tag parameter.
super(inflater.inflate
((tag.equals("challenges")) ? R.layout.card_view_chall : R.layout.card_view_comp, container,
false));
mTitleText = itemView.findViewById(R.id.title_holder);
mDescText = itemView.findViewById(R.id.desc_holder);
mPointsText = itemView.findViewById(R.id.points_holder);
mDateText = itemView.findViewById(R.id.date_holder);
if (tag.equals("challenges")) {
mConfButton = itemView.findViewById(R.id.card_conf_button);
mConfButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Setting the layout inflater for popup window.
LayoutInflater pInflater = (LayoutInflater) itemView.getContext().getSystemService(LAYOUT_INFLATER_SERVICE);
ViewGroup container1 = (ViewGroup) pInflater.inflate(R.layout.confirmation_popup, null);
final PopupWindow popupWindow = new PopupWindow(container1, 700, 600, true);
popupTitle = container1.findViewById(R.id.popuptext);
popAcceptBtn = container1.findViewById(R.id.accept_button);
popCancelBtn = container1.findViewById(R.id.cancel_button);
popupTitle.setText(mTitleText.getText().toString());
// Dismisses the popup window
popCancelBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
popupWindow.dismiss();
}
});
// Click listener for dialog accept button.
popAcceptBtn.setOnClickListener(new View.OnClickListener() {
String date;
#Override
public void onClick(View view) {
List<String> list = new ArrayList<>();
list.add(mTitleText.getText().toString());
list.add(mDescText.getText().toString());
list.add(mPointsText.getText().toString());
list.add(date = new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(new Date()));
// Saving data from current card into the completed challenges list.
TempDataReader reader = new TempDataReader(itemView.getContext());
new TempDataReader(itemView.getContext()).saveFile(list);
// I want to notify the dataset change here if possible!
//call the callback
callback.run();
popupWindow.dismiss();
}
});
popupWindow.showAtLocation(itemView, Gravity.CENTER, 25, 100);
}
});
}
}
}
And this will be your fragment:
public class CompletedFragment extends Fragment {
RecyclerView recyclerView;
RecyclerViewAdapter adapter;
public Fragment newInstance() {
return new CompletedFragment();
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.completed_fragment, container, false);
recyclerView = view.findViewById(R.id.completed_frag);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
TempDataReader reader = new TempDataReader(getActivity());
List<List<String>> super_list = reader.readCompFile();
if(super_list == null || super_list.size() < 1){
return null;
} else{
adapter = new RecyclerViewAdapter(super_list,"completed", new Runnable() {
#Override
public void run() {
//here read the list again and call notifyDataSetChanged on your recycler
}
});
);
recyclerView.setAdapter(adapter);
return view;
}
}
}
Hope it helps and it works for you. If i did anything wrong, let me know, i can't run the code right now so...
edited, i forgot to add code in the callback