Here's my Fragment:
public class Fragment_Cities extends Fragment implements Adapter_Cities.CitiesListener {
private Adapter_Cities adapterCities;
private FragmentCitiesBinding binding;
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
binding = FragmentCitiesBinding.inflate(inflater, container, false);
return binding.getRoot();
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
adapterCities = new Adapter_Cities(this);
binding.recyclerView.setAdapter(adapterCities);
ViewModel_Cities viewModelCities = new ViewModelProvider(this).get(ViewModel_Cities.class);
viewModelCities.getLiveDataCities().observe(getViewLifecycleOwner(), model_cities ->
adapterCities.setAdapterData(model_cities));
}
#Override
public void onCitySelected(Model_Cities city) {
if (!alertDialog.isShowing()) alertDialog.show();
new DialogFragment_Map().newInstance(city.getName(), city.getLatLon(), this).
show(requireActivity().getSupportFragmentManager(), null);
}
}
**
Here's my Adapter:
**
public class Adapter_Cities extends RecyclerView.Adapter<Adapter_Cities.MyViewHolder> implements Filterable {
private List<Model_Cities> cityList, cityListFiltered;
public CitiesListener citiesListener;
public class MyViewHolder extends RecyclerView.ViewHolder {
private final RowCityBinding rowCityBinding;
public MyViewHolder(View itemView) {
super(itemView);
rowCityBinding = RowCityBinding.bind(itemView);
// itemView.setOnClickListener(v -> recyclerViewItemClickListener.onCitySelected(cityListFiltered.get(getBindingAdapterPosition())));
}
}
public Adapter_Cities(CitiesListener citiesListener) {
this.citiesListener = citiesListener;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_city, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(Adapter_Cities.MyViewHolder holder, int position) {
holder.rowCityBinding.setModel(cityListFiltered.get(position));
holder.rowCityBinding.executePendingBindings();
}
public void setAdapterData(List<Model_Cities> cityList) {
this.cityList = cityList;
this.cityListFiltered = cityList;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return cityListFiltered == null ? 0 : cityListFiltered.size();
}
public interface CitiesListener {
void onCitySelected(Model_Cities city);
}
}
**
Here's my XML Layout:
**
<data>
<variable
name="callback"
type="com.base.assignment.adapters.Adapter_Cities.CitiesListener" />
<variable
name="model"
type="com.base.assignment.models.Model_Cities" />
</data>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="15dp"
android:background="#color/white"
android:onClick="#{()->callback.onCitySelected(model)}">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{model.title}"
android:textColor="#color/black"
android:textSize="15sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{model.subTitle}"
android:textColor="#color/black"
android:textSize="12sp" />
</LinearLayout>
</androidx.cardview.widget.CardView>
**
Here's the result i got:
**
My actual requirement is when user clicks on RecyclerView Item, the app must show a DialogFragment which should be called by a Fragment. So, I'm trying to trigger onClick of RecyclerView layout through XML, and the action that should happen on clicking is -> The information of clicked item should be sent to Fragment with Model Object as it's parameter.
Whereas When I clicked on Recyclerview Item no action is been happening. No crashes, no information in logcat, no updates in App UI.
As DataBinding shows the Compile time errors, i am seeing the below errors:
And right to it, i see the code details of the issue (as attached below):
For that set your listener interface for layout within onBindViewHolder().
#Override
public void onBindViewHolder(Adapter_Cities.MyViewHolder holder, int position) {
holder.rowCityBinding.setCallback(citiesListener);
holder.rowCityBinding.setModel(cityListFiltered.get(position));
holder.rowCityBinding.executePendingBindings();
}
Related
I'm creating an application in which I want to show about all guitar chords. To do this, I created a database with the name of the chords (chord_name) and I want to output it as a list (RecyclerView) in a fragment, but it doesn't come out. At the same time, the application works fine and nothing crashes, but the list is not shown
screen my application on phone
сlass FragmentChords
public class FragmentChords extends Fragment {
private FirebaseFirestore db = FirebaseFirestore.getInstance();
private CollectionReference chordsRef = db.collection("chords");
private ChordsAdapter adapter;
#Override
#PropertyName("chord_name")
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_chords, container, false);
RecyclerView recyclerView = view.findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
Query query = chordsRef.orderBy("chord_name", Query.Direction.DESCENDING);
FirestoreRecyclerOptions<Chords> options = new FirestoreRecyclerOptions.Builder<Chords>()
.setQuery(query,Chords.class)
.build();
adapter = new ChordsAdapter(options);
recyclerView.setAdapter(adapter);
return view;
}
#Override
public void onStart() {
super.onStart();
adapter.startListening();
}
#Override
public void onStop() {
super.onStop();
adapter.stopListening();
}
}
class Chords
public class Chords {
public String chord_name;
public Chords() {
}
public Chords(String chord_name) {
this.chord_name = chord_name;
}
#PropertyName("chord_name")
public String getChord_name() {
return chord_name;
}
public void setChord_name(String chord_name) {
this.chord_name = chord_name;
}
}
class ChordsAdapter
public class ChordsAdapter extends FirestoreRecyclerAdapter<Chords, ChordsAdapter.ChordsHolder> {
public ChordsAdapter(#NonNull FirestoreRecyclerOptions<Chords> options) {
super(options);
}
#Override
protected void onBindViewHolder(#NonNull ChordsHolder holder, int position, #NonNull Chords model) {
holder.textViewChordName.setText(model.getChord_name());
}
#NonNull
#Override
public ChordsHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_chords_item,
parent, false);
return new ChordsHolder(v);
}
class ChordsHolder extends RecyclerView.ViewHolder {
TextView textViewChordName;
public ChordsHolder(#NonNull View itemView) {
super(itemView);
textViewChordName = itemView.findViewById(R.id.chords_chord_name);
}
}
}
fragment_chords.xml
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/title_bar_layout_chords"
tools:listitem="#layout/recycler_chords_item" />
recycler_chords_item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:orientation="vertical"
app:cardBackgroundColor="#color/backgroundApp"
app:cardCornerRadius="8dp"
app:cardElevation="8dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_marginHorizontal="16dp"
android:orientation="horizontal">
<TextView
android:id="#+id/chords_chord_name"
android:layout_width="200dp"
android:layout_height="52dp"
android:gravity="center_vertical"
android:text="Chord name"
android:textColor="#color/textWhite"
android:textSize="20sp" />
<ImageView
android:id="#+id/chords_image_info"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="#drawable/ic_info"
app:tint="#color/textWhite"
android:contentDescription="TODO" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
I want to create a custom calendarView with ViewPager2 and RecyclerView . When I run the app its showing nothing in 3 first page! But when I scroll it other pages are fine ! This problem happening in android 7.0.0 and below and for android 7.1.1 and above is ok and works good. I created a custom calendar before in same way but now its not working !
Adapter for ViewPager :
public class SmartCalendarViewAdapter extends FragmentStateAdapter {
public SmartCalendarViewAdapter(#NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
}
public SmartCalendarViewAdapter(#NonNull Fragment fragment) {
super(fragment);
}
#NonNull
#Override
public Fragment createFragment(int pos) {
return SmartCalendarFragment.getInstance(pos);
}
#Override
public int getItemCount() {
return Integer.MAX_VALUE;
}
}
Calendar Fragment :
public class SmartCalendarFragment extends Fragment {
private RecyclerView mCalendarRecyclerView;
private TextView txtPos;
private int mPosition;
public static SmartCalendarFragment getInstance(int position) {
SmartCalendarFragment smartCalendarFragment = new SmartCalendarFragment();
Bundle bundle = new Bundle();
bundle.putInt("mPosition", position);
smartCalendarFragment.setArguments(bundle);
return smartCalendarFragment;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
if (bundle != null) {
mPosition = bundle.getInt("mPosition");
}
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.smart_calendar_fragment_layout, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
findViews(view);
ArrayList<Integer> models = new ArrayList<>();
for (int i = 1; i <= 42; i++) {
models.add(i);
}
mCalendarRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 7));
CalendarCellAdapter adapter = new CalendarCellAdapter(models);
mCalendarRecyclerView.setAdapter(adapter);
txtPos.setText(String.valueOf(mPosition));
}
private void findViews(View view) {
mCalendarRecyclerView = view.findViewById(R.id.smart_calendar_recyclerView);
txtPos = view.findViewById(R.id.txtPos);
}
}
Inner RecyclerView Adapter :
public class CalendarCellAdapter extends RecyclerView.Adapter<CalendarCellAdapter.CellViewHolder> {
private List<Integer> mSmartCalendarCellModels;
public CalendarCellAdapter(List<Integer> smartCalendarCellModels) {
mSmartCalendarCellModels = smartCalendarCellModels;
}
#NonNull
#Override
public CellViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int i) {
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.item_attendance_calendar_cell_layout,parent,false);
return new CellViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull CellViewHolder cellViewHolder, int i) {
cellViewHolder.bindView(mSmartCalendarCellModels.get(i));
}
#Override
public int getItemCount() {
return mSmartCalendarCellModels.size();
}
class CellViewHolder extends RecyclerView.ViewHolder {
TextView mTextView;
public CellViewHolder(#NonNull View itemView) {
super(itemView);
mTextView=itemView.findViewById(R.id.item_attendance_cell_txtDate);
}
void bindView(Integer model){
mTextView.setText(String.valueOf(model));
}
}
}
MainActivity :
public class MainActivity extends AppCompatActivity {
ViewPager2 mViewPager2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewPager2=findViewById(R.id.viewPager2);
SmartCalendarViewAdapter adapter=new SmartCalendarViewAdapter(this);
mViewPager2.setOffscreenPageLimit(1);
mViewPager2.setAdapter(adapter);
mViewPager2.setCurrentItem(Integer.MAX_VALUE/2,false);
}
}
Fragment layout file :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/smart_calendar_fragment_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layoutDirection="rtl"
android:orientation="vertical">
<TextView
android:id="#+id/txtPos"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/smart_calendar_recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="never" />
</LinearLayout>
Cell Layout file :
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/item_attendance_cell_root"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<TextView
android:id="#+id/item_attendance_cell_txtDate"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:padding="4dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
tools:text="1" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity Layout file :
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/viewPager2"
android:background="#ADABF3"
app:layout_constraintTop_toTopOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
Changing the width of the TextView in your cell layout file to wrap_content fixes the issue.
Not sure why it only affects the preloaded pages, and why only on Android 7 and lower... but setting the TextView to have 1:1 ratio has no visible effect anyway, cos the parent is stretched to fill the space to allow for 7 cells across the screen width.
My RecyclerView does not count above 10 items. After the 10th item, it shows item 9 again and then item 1 and 2.
This RecyclerView produces the bug:
mViewModel.getmPremixableIngredientsLive().observe(getViewLifecycleOwner(), new Observer<ArrayList<Ingredient>>() {
#Override
public void onChanged(ArrayList<Ingredient> premixableIngredients) {
ShowIngredientsRecyclerViewAdapter premixableComponentsRecyclerViewAdpater = new ShowIngredientsRecyclerViewAdapter(premixableIngredients);
mBinding.premixableIngredientsRecyclerview.setAdapter(premixableComponentsRecyclerViewAdpater);
GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), 1);
mBinding.premixableIngredientsRecyclerview.setLayoutManager(gridLayoutManager);
}
});
This is the code of my adapter:
public class ShowIngredientsRecyclerViewAdapter extends RecyclerView.Adapter<ShowIngredientsRecyclerViewAdapter.ViewHolder> {
private ArrayList<Ingredient> mIngredients;
private IngredientRecyclerViewItemBinding mBinding;
public ShowIngredientsRecyclerViewAdapter(ArrayList<Ingredient> ingredients) {
mIngredients = ingredients;
}
#NonNull
#Override
public ShowIngredientsRecyclerViewAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
mBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()),
R.layout.ingredient_recycler_view_item,
parent,
false
);
return new ViewHolder(mBinding.getRoot());
}
#Override
public void onBindViewHolder(#NonNull ShowIngredientsRecyclerViewAdapter.ViewHolder holder, int position) {
mBinding.position.setText((position + 1) + ".");
mBinding.componentName.setText(mIngredients.get(position).getmComponent().getmName());
mBinding.amount.setText((mIngredients.get(position).getGrammPerCow() / 1000) + "kg/cow");
mBinding.dragBtn.setVisibility(View.GONE);
}
#Override
public int getItemCount() {
return mIngredients.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(#NonNull View itemView) {
super(itemView);
}
}
}
xml layout for the item:
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:id="#+id/ingredient_recyclerview_item_linearLayout"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:paddingLeft="0dp"
android:paddingStart="0dp">
<TextView
android:id="#+id/position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1"/>
<TextView
android:id="#+id/component_name"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Component 1"/>
<TextView
android:id="#+id/amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="50kg"/>
<ImageButton
android:id="#+id/drag_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="#drawable/ic_drag_grey"
android:background="#android:color/transparent"
android:layout_marginStart="8dp"/>
</LinearLayout>
</layout>
You are not implementing databinding correctly in your recyclerview. You shouldn't declare mBinding as a global variable in the adapter. Instead, it should be a global variable in the custom viewholder class. I share an example recyclerview adapter code with databinding below. You can adapt it to your case. Notice that binding is not a global variable in the adapter, but in the custom viewholder. And the viewholder accept binding as argument, so we pass binding instance to the viewholder when we create the viewholder and we use that instance when onBindViewHolder is called.
public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.ProductViewHolder> {
private final List<Product> mProductList;
private final ProductItemClickListener mListener;
ProductAdapter(#NonNull List<Product> productList, ProductItemClickListener listener) {
mProductList =productList;
mListener = listener;
}
#NonNull
#Override
public ProductViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
ItemProductBinding binding = DataBindingUtil
.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_product,
parent, false);
return new ProductViewHolder(binding);
}
#Override
public void onBindViewHolder(#NonNull ProductViewHolder holder, int position) {
holder.bind(mProductList.get(position), mListener);
}
#Override
public int getItemCount() {
return mProductList.size();
}
class ProductViewHolder extends RecyclerView.ViewHolder{
final ItemProductBinding binding;
ProductViewHolder(ItemProductBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
void bind(Product currentProduct, ProductItemClickListener clickListener){
//For each item, corresponding product object is passed to the binding
binding.setProduct(currentProduct);
binding.setProductItemClick(clickListener);
//This is to force bindings to execute right away
binding.executePendingBindings();
}
}
public interface ProductItemClickListener {
void onProductItemClicked(Product product);
}
}
Ok I'll try one more time. Last time I asked about passing data between recyclerview and item and one person helped me with open item by click, but I still don't have idea how to show the data of clicked item in new activity. I want to click on an item and then display the data of that item in new activity. In this activity I want to edit data.
Does anyone knows how to do it? I need any idea.
RecyclerView Adapter with OnItemClickListener interface:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.TaskViewHolder> {
private List<MainViewModel> mTasks;
private List<Task> tasks = new ArrayList<>();
private Context context;
private EditTaskViewModel editTaskViewModel;
public RecyclerViewAdapter(List<MainViewModel> tasks, Context context, EditTaskViewModel editTaskViewModel) {
this.mTasks = tasks;
this.context = context;
this.editTaskViewModel = editTaskViewModel;
}
#NonNull
#Override
public RecyclerViewAdapter.TaskViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
final RecyclerViewItemBinding binding = DataBindingUtil.inflate(inflater, R.layout.recycler_view_item, parent, false);
binding.setItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(View view) {
Intent intent = new Intent(view.getContext(), EditTaskActivity.class);
intent.putExtra("id", binding.getPosition());
view.getContext().startActivity(intent);
Toast.makeText(view.getContext(), "ID " + binding.getPosition(), Toast.LENGTH_SHORT).show();
}
});
return new TaskViewHolder(binding);
}
#Override
public void onBindViewHolder(#NonNull final RecyclerViewAdapter.TaskViewHolder holder, final int position) {
Task currentTask = tasks.get(position);
holder.mBinding.descriptionItem.setText(currentTask.getDescription());
holder.mBinding.dateItem.setText(currentTask.getDate());
holder.mBinding.timeItem.setText(currentTask.getTime());
holder.mBinding.setPosition(position);
}
#Override
public int getItemCount() {
return tasks.size();
}
public void setTasks(List<Task> tasks) {
this.tasks = tasks;
notifyDataSetChanged();
}
public Task getTaskPosition(int position) {
return tasks.get(position);
}
public class TaskViewHolder extends RecyclerView.ViewHolder {
private final RecyclerViewItemBinding mBinding;
public TaskViewHolder(RecyclerViewItemBinding binding) {
super(binding.getRoot());
this.mBinding = binding;
}
public void bind (MainViewModel mainViewModel){
mBinding.setItemView(mainViewModel);
mBinding.executePendingBindings();
}
}
public interface OnItemClickListener {
void onItemClick(View view);
}
Item XML file:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="itemView"
type="com.example.daniellachacz.taskmvvm.viewmodel.MainViewModel">
</variable>
<variable
name="itemClickListener"
type="com.example.daniellachacz.taskmvvm.adapter.RecyclerViewAdapter.OnItemClickListener">
</variable>
<variable
name="task"
type="com.example.daniellachacz.taskmvvm.model.Task">
</variable>
<variable
name="position"
type="int">
</variable>
</data>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="120dp"
android:shadowColor="#color/colorPrimary"
android:backgroundTint="#color/cardview_shadow_end_color">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="110dp"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:onClick="#{(view)-> itemClickListener.onItemClick(view)}">
<TextView
android:id="#+id/description_item"
android:layout_width="250dp"
android:layout_height="96dp"
android:layout_marginStart="5dp"
android:layout_marginTop="9dp"
android:layout_marginBottom="5dp"
android:text="#{itemView.description}"
android:textSize="18sp"
android:textColor="#020202"
android:focusable="true" />
<TextView
android:id="#+id/date_item"
android:layout_width="90dp"
android:layout_height="40dp"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="9dp"
android:layout_marginEnd="10dp"
android:gravity="center"
android:text="#{itemView.date}"
android:textColor="#020202"
android:textSize="16sp" />
<TextView
android:id="#+id/time_item"
android:layout_width="90dp"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:layout_alignStart="#+id/date_item"
android:layout_marginBottom="10dp"
android:layout_marginEnd="10dp"
android:gravity="center"
android:text="#{itemView.time}"
android:textColor="#020202"
android:textSize="16sp" />
</RelativeLayout>
</android.support.v7.widget.CardView>
</layout>
onCreate:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
floatingActionButton = findViewById(R.id.floating_action_button);
List<Task> tasks = new ArrayList<>();
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setHasFixedSize(true);
final RecyclerViewAdapter recyclerViewAdapter = new RecyclerViewAdapter(context, tasks);
recyclerView.setAdapter(recyclerViewAdapter);
mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
mainViewModel.getAllTasks().observe(this, recyclerViewAdapter::setTasks);
Here are a couple of suggestions you might find useful:
Don't depend on ViewModels in your adapters. ViewModels are meant to handle events from views (Fragments or Activities) and broadcast updates back to the views via some observable mechanism (most commonly LiveData instances). Referencing your ViewModels directly inside an adapter is bad, since it couples them together. This means that it will be very hard for you to reuse your adapter with a different ViewModel if needed. I know it doesn't seem likely at this point in time, but just trust me on this one. After the changes have been applied, your adapter should look something like this:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.TaskViewHolder> {
private LayoutInflater mLayoutInflater;
private List<Task> mTasks;
private OnItemClickListener mOnItemClickListener;
public RecyclerViewAdapter(#NonNull Context context, #NonNull List<Task> tasks) {
mLayoutInflater = LayoutInflater.fromContext(context);
mTasks = tasks;
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
mOnItemClickListener = onItemClickListener;
}
#NonNull
#Override
public RecyclerViewAdapter.TaskViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
final RecyclerViewItemBinding binding = DataBindingUtil.inflate(mLayoutInflater, R.layout.recycler_view_item, parent, false);
return new TaskViewHolder(binding);
}
#Override
public void onBindViewHolder(#NonNull final RecyclerViewAdapter.TaskViewHolder holder, final int position) {
Task currentTask = tasks.get(position);
holder.bind(currentTask, mOnItemClickListener);
}
#Override
public int getItemCount() {
return tasks.size();
}
public void setTasks(List<Task> tasks) {
this.tasks = tasks;
notifyDataSetChanged();
}
public Task getTaskPosition(int position) {
return tasks.get(position);
}
public class TaskViewHolder extends RecyclerView.ViewHolder {
private final RecyclerViewItemBinding mBinding;
public TaskViewHolder(RecyclerViewItemBinding binding) {
super(binding.getRoot());
this.mBinding = binding;
}
public void bind (Task item, OnItemClickListener onItemClickListener) {
mBinding.setItem(item);
mBinding.executePendingBindings();
itemView.setOnClickListener(view -> {
if (onItemClickListener != null) {
onItemClickListener.onItemClick(view, item);
}
}
}
}
public interface OnItemClickListener {
void onItemClick(View view, Task item);
}
}
The OnItemClickListener.onItemClick() method now passes the view and the item itself as parameters. This is the easiest way to expose the clicked item to whoever might be interested. The on click listener is now set at the adapter level, using setOnItemClickListener().
The setting of the OnClickListener of the item view is now done in the bind() method of the TaskViewHolder. When binding, we know the exact item that is going to populate the view, so we can return it to the OnItemClickListener.
You have to simplify the layout as well, since there are a lot of things that are not really needed. It may look like this:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="task"
type="com.example.daniellachacz.taskmvvm.model.Task">
</variable>
</data>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="120dp"
android:shadowColor="#color/colorPrimary"
android:backgroundTint="#color/cardview_shadow_end_color">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="110dp"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp">
<TextView
android:id="#+id/description_item"
android:layout_width="250dp"
android:layout_height="96dp"
android:layout_marginStart="5dp"
android:layout_marginTop="9dp"
android:layout_marginBottom="5dp"
android:text="#{item.description}"
android:textSize="18sp"
android:textColor="#020202"
android:focusable="true" />
<TextView
android:id="#+id/date_item"
android:layout_width="90dp"
android:layout_height="40dp"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="9dp"
android:layout_marginEnd="10dp"
android:gravity="center"
android:text="#{item.date}"
android:textColor="#020202"
android:textSize="16sp" />
<TextView
android:id="#+id/time_item"
android:layout_width="90dp"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:layout_alignStart="#+id/date_item"
android:layout_marginBottom="10dp"
android:layout_marginEnd="10dp"
android:gravity="center"
android:text="#{item.time}"
android:textColor="#020202"
android:textSize="16sp" />
</RelativeLayout>
</android.support.v7.widget.CardView>
</layout>
The only variable is the item and we are binding it's properties to the TextViews.
I guess this should be enough to get you going.
Just a couple of other things that are not directly related to the question, but are important.
Null safety - you never check the input when calling setTask() in the adapter. A client may pass null and cause crashes all over the place. You should try and prevent that.
Calling notifyDataSetChanged() is not a good practice when working with RecyclerView.Adapter since this will cancel all the built-in animations of the RecyclerView. It's better to use the other notify... methods. You might want to check DiffUtil at some point.
It depends on how your data is stored and if it accessible in the second activity. If you have a static ArrayList of your data you can pull the data from it using the index you passed in your onclick of the RV item. For example:
class myData{
private ArrayList<Data> myDataArray;
static ArrayList<Data> getMyDataArray(){
return myDataArray;
}
static void setMyDataArray(array)
myDataArray = array;
}
So you fill your RV with getMyDataArray() then you set the onlclick to send the index clicked in the RV to the next activity.
In onLoad of the second activity:
int myDataIndex = getIntent().getIntExtra("id",0);
Data myData = myData.getMyDataArray().get(myDataIndex);
Note: Data is whatever your data is, could be strings, or ints, or a custom class/object with data.
Here is what I have done for item click listener in recycler view using data binding.
ADAPTER CODE
public class TC_DashboardRecViewAdapter extends RecyclerView.Adapter<TC_DashboardRecViewAdapter.ViewHolder> {
Context context;
List<String> list;
private TcDashboardItemBinding tcDashboardItemBinding;
ItemClickListener itemClickListener;
public TC_DashboardRecViewAdapter(Context context, List<String> dashboardItems, ItemClickListener itemClickListener) {
this.context = context;
this.list = dashboardItems;
this.itemClickListener = itemClickListener;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
ViewDataBinding binding = DataBindingUtil.inflate(inflater, R.layout.tc_dashboard_item, parent, false);
tcDashboardItemBinding = (TcDashboardItemBinding) parent.getTag();
return new ViewHolder(binding);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
holder.bind(list.get(position), itemClickListener, position);
}
#Override
public int getItemCount() {
return list.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
private ViewDataBinding binding;
public ViewHolder(ViewDataBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(String s, ItemClickListener itemClickListener, int position) {
this.binding.setVariable(BR.itemModel, s);
this.binding.executePendingBindings();
binding.getRoot().setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(itemClickListener !=null){
itemClickListener.onItemClicked(binding.getRoot(), s, position);
}
}
});
}
}
ITEM CLICK INTERFACE:
public interface ItemClickListener {
void onItemClicked(View vh, Object item, int pos);
}
FRAGMENT/ACTIVITY CODE:
public class TC_DashboardFragment extends BaseFragment implements ItemClickListener {
public void onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
tc_dashboardRecViewAdapter = new TC_DashboardRecViewAdapter(getContext(), getDashboardItems(), this);
linearLayoutManager = new LinearLayoutManager(getContext());
dashboardrecyclerview.setLayoutManager(new LinearLayoutManager(getContext()));
binding.setAdapter(tc_dashboardRecViewAdapter);
}
#Override
public void onItemClicked(View vh, Object item, int pos) {
Toast.makeText(mainActivity, item.toString(), Toast.LENGTH_SHORT).show();
}
When you run the code and click on recycler view item it would show a toast with the text from the item clicked.
I am trying to implement increase and decrease of quantity in a TextView with Handlers, but always it throws the same error kindly help me to understand where I am lacking to get the expected result.
Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
java.lang.RuntimeException: Found data binding errors.
****/ data binding error ****msg:Identifiers must have user defined types from the XML file. view is missing it
file:C:\Users\Tushar Rai\Desktop\Demo\Demo\app\src\main\res\layout\fragment_list.xml
****\ data binding error ****
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="quantity"
type="int"/>
<variable
name="Handlers"
type="com.demo.www.demo.ui.Fragment.Handlers"/>
<import type="android.view.View"/>
</data>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentEnd="true"
android:paddingTop="16dp"
android:paddingBottom="16dp">
<Button
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/decrease"
android:onClick="#{Handlers.decrement(view, 0)}" />
<TextView
android:layout_width="32dp"
android:layout_height="wrap_content"
app:quantity="#{quantity}"
android:textAlignment="center" />
<Button
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/increase"
android:onClick="#{Handlers.increment(view, 10)}" />
</LinearLayout>
public class Fragment extends Fragment {
FirebaseRecyclerAdapter adapter;
Firebase mFirebaseRef = new Firebase("https://demo.firebaseio.com/").child("list");
public Fragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
final View rootView = inflater.inflate(R.layout.recycler_view, container, false);
final RecyclerView recyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
adapter = new FirebaseRecyclerAdapter<List, ViewHolder>(List.class, R.layout.fragment_list,
ViewHolder.class, mFirebaseRef) {
#Override
protected void populateViewHolder(ViewHolder viewHolder, List list, int i) {
FragmentBinding binding = viewHolder.getBinding();
binding.setList(list);
Handlers handlers = new Handlers();
binding.setHandlers(handlers);
}
};
recyclerView.setAdapter(adapter);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), null));
return rootView;
}
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public FragmentBinding binding;
public ViewHolder(View itemView) {
super(itemView);
binding = DataBindingUtil.bind(itemView);
itemView.setOnClickListener(this);
}
public FragmentBinding getBinding() {
return binding;
}
#Override
public void onClick(View v) {
}
}
#BindingAdapter("imageUrl")
public static void setImage(ImageView imageView, String imageUrl) {
Picasso.with(imageView.getContext()).load(imageUrl).into(imageView);
}
#BindingAdapter("quantity")
public static void setQuantityText(TextView view, int quantity) {
view.setText(String.valueOf(quantity));
}
public class Handlers {
public void decrement(View view, int min) {
FragmentBinding binding = DataBindingUtil.findBinding(view);
binding.setQuantity(Math.min(min, binding.getQuantity() - 1));
}
public void increment(View view, int max) {
FragmentBinding binding = DataBindingUtil.findBinding(view);
binding.setQuantity(Math.max(max, binding.getQuantity() + 1));
}
}
}
I think problem is in your increment & decrement methods. You can not overload these methods parameter.
You need to write like :
public class Handlers {
public void decrement(View view) {
FragmentBinding binding = DataBindingUtil.findBinding(view);
binding.setQuantity(Math.min(min, binding.getQuantity() - 1));
}
public void increment(View view) {
FragmentBinding binding = DataBindingUtil.findBinding(view);
binding.setQuantity(Math.max(max, binding.getQuantity() + 1));
}
}
And in xml:
android:onClick="#{Handlers.increment}"
see the link for more : DataBinding