I am implementing a Recycler View and its respective adapter using Android Data Binding. The problem is that in all the tutorials I have seen, they initialize the data of the adapter with a Collection of View model class, like this:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> implements View.OnClickListener {
private ArrayList<ViewModel> items;
public RecyclerViewAdapter(ArrayList<ViewModel> items) {
this.items = items;
}
.....
}
But the data I want to pass to the Recycler View is a Collection of records from my database:
private ArrayList<Record> items;
How can I do that??
Thanks in advance!
EDIT
li_item.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="report"
type="viewmodel.ReportVM"/>
</data>
....
<TextView
android:id="#+id/tv_report_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:textSize="18sp"
android:paddingBottom="6dp"
android:text="#{report.name}"
tools:text="Report 1"
/>
....
ADAPTER
public class ReportRVAdapter extends RecyclerView.Adapter<ReportRVAdapter.ReportViewHolder> {
private List<ReportDb> data;
public ReportRVAdapter(final List<ReportDb> reportData) {
this.data = reportData;
}
#Override
public ReportRVAdapter.ReportViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.li_item, parent, false);
return new ReportViewHolder(v);
}
#Override
public void onBindViewHolder(final ReportViewHolder holder, final int position) {
ReportDb item = data.get(position);
holder.getBinding().setVariable(BR.name, item.getName());
holder.getBinding().setVariable(BR.contractor, item.getContractor());
//make binding happen immediately
holder.getBinding().executePendingBindings();
}
#Override
public int getItemCount() {
return data == null ? 0 : data.size();
}
public class ReportViewHolder extends RecyclerView.ViewHolder {
private ViewDataBinding binding;
public ReportViewHolder(final View rowView) {
super(rowView);
binding = DataBindingUtil.bind(rowView);
}
public ViewDataBinding getBinding() {
return binding;
}
}
}
MODEL VIEW
public class ReportVM extends BaseObservable {
public String name;
public String contractor;
public ReportVM() {
}
#Bindable
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
#Bindable
public String getContractor() {
return contractor;
}
public void setContractor(final String contractor) {
this.contractor = contractor;
}
}
You can use any collection in RecyclerView, It depends on you how you bind value with the View(TextView, ImageView).
Example:-
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
final String name = mDataset.get(position);
holder.txtHeader.setText(mDataset.get(position));
}
in above example, it depends on you, if you use Map then you need to get value using key. For set same get method can be used.
Finally I did realized that instead of:
holder.getBinding().setVariable(BR.name, item.getName());
holder.getBinding().setVariable(BR.contractor, item.getContractor());
//make binding happen immediately
holder.getBinding().executePendingBindings();
I should write this:
holder.getBinding().setReport(new ReportVM(item));
and my POJO:
private ReportDb report;
public ReportVM(final ReportDb report) {
this.report = report;
}
public String getName() {
return report.getName();
}
public String getContractor() {
return report.getContractor();
}
Related
What I am doing: I have displayed the list view using data binding
What I am trying to find: How to properly add a on click event and display a toast as student name
Student.java
public class Student {
private String name;
private String email;
public Student() {
}
public Student(String name, String email) {
this.name = name;
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
ActDemoListView.java
public class ActDemoListView extends AppCompatActivity {
private ActDemoListViewViewModel actDemoListViewViewModel;
private ActDemoListViewBinding actDemoListViewBinding;
private RecyclerView recyclerView;
private AdptStudent adptStudent;
private List<Student> studentList = new ArrayList<>();
/************************************* Life Cycle Methods *************************************/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initOnCreate();
}
/************************************* Life Cycle Methods *************************************/
/************************************* Init Methods *******************************************/
/** Init OnCreate **/
private void initOnCreate() {
setContentView(R.layout.act_two_way_display_data);
//Connect the view model to activity
connectViewModel();
//Bind the layout to activity
bindLayoutToActivity();
recyclerView = actDemoListViewBinding.recyclerList;
adptStudent = new AdptStudent(studentList);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(adptStudent);
prepareMovieData();
}
/************************************* Init Methods *******************************************/
private void connectViewModel() {
actDemoListViewViewModel = ViewModelProviders.of(this).get(ActDemoListViewViewModel.class);
}
private void bindLayoutToActivity() {
actDemoListViewBinding = DataBindingUtil.setContentView(this,R.layout.act_demo_list_view);
}
private void prepareMovieData() {
Student movie = new Student("Shruthi", "user11#google.com");
studentList.add(movie);
movie = new Student("Shalvi", "user1#google.com");
studentList.add(movie);
movie = new Student("Pavan", "user2#google.com");
studentList.add(movie);
movie = new Student("Brijesh", "user3#google.com");
studentList.add(movie);
movie = new Student("Anudeep", "user4#google.com");
studentList.add(movie);
adptStudent.notifyDataSetChanged();
}
}
AdptStudent.java
public class AdptStudent extends RecyclerView.Adapter<AdptStudent.MyViewHolder> {
private List<Student> studentsList = new ArrayList<>();
public AdptStudent(List<Student> studentsList) {
this.studentsList = studentsList;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ListItemBinding listItemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),R.layout.list_item, parent, false);
return new MyViewHolder(listItemBinding);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
Student student = studentsList.get(position);
holder.listItemBinding.setStudent(student);
}
#Override
public int getItemCount() {
return studentsList.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
private ListItemBinding listItemBinding;
public MyViewHolder(ListItemBinding ListItemBinding) {
super(ListItemBinding.getRoot());
this.listItemBinding=ListItemBinding;
}
}
}
There are several ways to do it. When i want an item on a recycler view to be clickable i do this.
Firstly i create an interface
public interface ItemClickListener {
void onClick(View view, int position, boolean click);
}
Secondly, on the view holder class i add these methods
#Override
public void onClick(View view) {
itemClickListener.onClick(view, getAdapterPosition(), false);
}
public void setItemClickListener(ItemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
And lastly on the onBindViewHolder
#Override
public void onBindViewHolder(#NonNull final CategoryAdapter.MyViewHolder myViewHolder, int i) {
myViewHolder.setItemClickListener(new ItemClickListener() {
#Override
public void onClick(View view, int position, boolean click) {
Intent intent = new Intent(myViewHolder.context, Myclass.class);
myViewHolder.context.startActivity(intent);
}
});
}
If you want me to provide all the code for the adapter just ask.
I have used the following approach. Note that your "Item" can be your viewmodel if you like which you have access to inside the bound layout for each item. And from there you can call whatever method you want inside the vm, or set a LiveData or whatever to tell the view to display the toast. I recomment to use SingleLiveEvent for this purpose.
First I created a BaseAdapter.
public class BaseAdapter<T> extends ListAdapter<T, SingleItemViewHolder<T>> {
private final int variableId;
protected BaseAdapter(#NonNull DiffUtil.ItemCallback<T> diffCallback, int variableId) {
super(diffCallback);
this.variableId = variableId;
}
#NonNull
#Override
public SingleItemViewHolder<T> onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
ViewDataBinding binding = DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()), viewType, parent, false);
return new SingleItemViewHolder<>(binding, variableId);
}
#Override
public void onBindViewHolder(#NonNull SingleItemViewHolder<T> holder, int position) {
holder.bind(getItem(position));
}
}
This adapter uses the following SingleItemViewHolder.
public final class SingleItemViewHolder<T> extends RecyclerView.ViewHolder {
private final ViewDataBinding binding;
private final int variableId;
/**
* Constructor
*
* #param binding the binding to use
* #param variableId variable to set on the binding
*/
public SingleItemViewHolder(ViewDataBinding binding, int variableId) {
super(binding.getRoot());
this.binding = Objects.requireNonNull(binding);
this.variableId = variableId;
}
/**
* Sets the data binding variable to the provided item
* and calls {#link ViewDataBinding#executePendingBindings()}.
*
* #param item item to bind
* #throws NullPointerException if item is null ({#code item == null})
*/
public void bind(#NonNull T item) {
Objects.requireNonNull(item);
binding.setVariable(variableId, item);
binding.executePendingBindings();
}
}
Then you use it by subclassing the BaseAdapter and providing your own DiffCallback and layout like this.
public final class ModelAdapter extends BaseAdapter<Model> {
public ModelAdapter() {
super(new DiffCallback(), BR.item);
}
#Override
public int getItemViewType(int position) {
return R.layout.item_model;
}
private static final class DiffCallback extends DiffUtil.ItemCallback<Model> {
#Override
public boolean areItemsTheSame(#NonNull Model oldItem, #NonNull Model newItem) {
return oldItem.id.equals(newItem.id);
}
#Override
public boolean areContentsTheSame(#NonNull Model oldItem, #NonNull Model newItem) {
return oldItem.equals(newItem);
}
}
}
Where Model is a simple java object class with some fields (not included for brevity).
Then the layout which shows the actual model and allows for the data binding.
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="item"
type="com.example.models.Model" />
</data>
....
Then you can simply use that item inside the layout.
Bonus usage example
Then instantiate it however you want. My recommendation, how I did it, was to instantiate in the view (fragment) the attach it using data binding.
<data>
<variable
name="adapter"
type="com.example.ModelAdapter" />
</data>
....
<androidx.recyclerview.widget.RecyclerView
recycler_view_base_adapter_items="#{vm.models}"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adapter="#{adapter}"
android:orientation="vertical"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
With the following #BindingAdapter.
#BindingAdapter(value = {"recycler_view_base_adapter_items"})
public static <T> void setRecyclerViewBaseAdapterItems(RecyclerView view,
#Nullable final List<T> items) {
final RecyclerView.Adapter viewAdapter = view.getAdapter();
if (viewAdapter == null || items == null) {
Timber.w("recycler_view_base_adapter_items did nothing.");
return;
}
try {
#SuppressWarnings("unchecked") final BaseAdapter<T> adapter = (BaseAdapter<T>) viewAdapter;
adapter.submitList(items);
} catch (ClassCastException e) {
Timber.e(e);
}
}
Well... i want to show data retrieved from firebase database in RecycleView and do further stuffs like editing,updating etc. So, i'm using observable pattern to retrieve data(which successfully did) and trying to pass data to the constructor and the adapter class. The data did show up in the constructor class but didn't load in adapter class and also in the RecycleView.
Fragment class or main class
public class overviewFragment extends Fragment {
View view;
public static RecyclerView overRecycleView;
public overviewFragment() {}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.overview_fragment, container, false);
overRecycleView = view.findViewById(R.id.overViewRecycle);
overviewRecyclerAdapter adapter = new overviewRecyclerAdapter(getContext(), observerData.overViewlist);
overRecycleView.setLayoutManager(new LinearLayoutManager(getActivity()));
// adapter.notifyDataSetChanged();
overRecycleView.setAdapter(adapter);
return view;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
observableData ob =new observableData();
observerData observerData = new observerData(ob)
ob.setMeasurement();
ob.setMeasurement(); }}
Observable class to retrieve firebase data and notify observer
public class observableData extends Observable {
private String data;
public observableData() { }
public void setMeasurement(){
final DatabaseReference n = ScrollingActivityforTutor.db();
n.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String a = dataSnapshot.child("name").getValue().toString();
Log.d("name from database ", a);
data =a;
measurementChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
public void measurementChanged(){
setChanged();
notifyObservers();
}
public String getData() {
return data;
}
}
Observer class from where constructor class will be called
public class observerData implements Observer {
Observable observable;
public String data;
public static List<itemOverview> overViewlist = new ArrayList<>();;
public observerData(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
#Override
public void update(Observable o, Object arg) {
observableData od = (observableData) o;
this.data = od.getData();
passData();
}
public void passData(){
overViewlist.add(new itemOverview(data));
Log.d("data in observer ", data);
}
}
Constructor class
public class itemOverview {
private String text;
public itemOverview(String text) {
Log.d("IN CONSTRUCTOR ", text); // It is perfectly showing the data
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}}
Adapter class
public class overviewRecyclerAdapter extends
RecyclerView.Adapter<overviewRecyclerAdapter.overviewViewHolder> {
Context mcontext;
List<itemOverview> mdata;
public overviewRecyclerAdapter(Context mcontext, List<itemOverview> mdata)
{
this.mcontext = mcontext;
this.mdata = mdata; }
public static class overviewViewHolder extends RecyclerView.ViewHolder{
private TextView t;
public overviewViewHolder(#NonNull View itemView) {
super(itemView);
t = itemView.findViewById(R.id.overViewtextView); }}
#NonNull
#Override
public overviewRecyclerAdapter.overviewViewHolder
onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v;
v = LayoutInflater.from(mcontext).inflate(R.layout.item_overview,
parent, false);
overviewViewHolder overview = new overviewViewHolder(v);
return overview; }
#Override
public void onBindViewHolder(#NonNull
overviewRecyclerAdapter.overviewViewHolder holder, int position) {
Log.d("IN ADAPTER ", mdata.get(position).getText()); // HERE, IT IS
NOT WORKING
holder.t.setText(mdata.get(position).getText());
}
#Override
public int getItemCount() {
return mdata.size() ;
}}
But when i use measurementChanged() (in the observable class) outside the onDatachange method and put data (variable) with some string value, everything worked and the given data showed up in the recycleView.
dont understand where the problem is and where to debug.
However, SORRY for such big miles of codes :)
Thanks.
I can see that you are trying to add elements to a RecyclerView programmatically. But the RecyclerView is not updated when you update the array containing the necessary data. As you have made the RecyclerView static, you can easily notify the changes to the adapter using the following code:
public void passData(){
overViewlist.add(new itemOverview(data));
Log.d("data in observer ", data);
overviewRecyclerAdapter adapter = (overviewRecyclerAdapter) overviewFragment.overRecycleView.getAdapter();
adapter.notifyDataSetChanged();
}
There are much better ways of doing this. adapter.notifyItemInserted(overViewlist.size()-1); is probably a better solution in your case (in terms of performance).
P.S. You should follow the naming conventions in java to make your code more readable.
I have implemented recycler view with data binding using a baseadapter which handles all the binding of any layout item.
I have tried to implement per item click listener using method reference and Listener bindings, but I couldn't do that.
Here is my code. Can you give me a sample which is the simple way to detect every single item of the recycler view and I want to add click listener for every single item for different purposes. Thanks.
MyBaseAdapter
public abstract class MyBaseAdapter extends RecyclerView.Adapter<MyBaseAdapter.MyViewHold> {
public class MyViewHold extends RecyclerView.ViewHolder implements View.OnClickListener {
public ViewDataBinding binding;
Context context;
public MyViewHold(ViewDataBinding binding) {
super(binding.getRoot());
this.binding = binding;
context = binding.getRoot().getContext();
binding.getRoot().setOnClickListener(this);
}
// Here BR.pojo must be matched to layout variable name
//our layout variable was
/*
<variable
name="pojo"
type="com.durbinlabs.databinding.POJO"
/>
*/
public void bind(Object obj) {
binding.setVariable(BR.pojo, obj);
binding.setVariable(BR.food, obj);
binding.executePendingBindings();
}
#Override
public void onClick(View view) {
// for all view
}
}
#Override
public MyViewHold onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
ViewDataBinding binding = DataBindingUtil.inflate(layoutInflater, getLayoutIdForType(viewType)
, parent, false);
return new MyBaseAdapter.MyViewHold(binding);
}
#Override
public void onBindViewHolder(MyViewHold holder, int position) {
holder.bind(getDataAtPosition(position));
Log.d("click", "" + holder.getItemId());
}
public abstract Object getDataAtPosition(int position);
public abstract int getLayoutIdForType(int viewType);}
my pojo class
public class POJO extends BaseObservable {
int img;
String name;
public POJO(int img) {
this.img = img;
}
public void setImg(int img) {
this.img = img;
}
public int getImg() {
return img;
}
public POJO(int img, String name) {
this.img = img;
this.name = name;
}
#Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}}
recycler view row layout (per item)
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="pojo"
type="com.durbinlabs.databinding.POJO" />
</data>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:padding="6dip">
<ImageView
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
android:layout_marginRight="6dip"
android:contentDescription="TODO"
android:onClick="#{handlers::imgClick}"
android:src="#mipmap/ic_launcher" />
<TextView
android:layout_width="fill_parent"
android:layout_height="26dip"
android:layout_alignParentRight="true"
android:layout_centerInParent="true"
android:layout_toRightOf="#id/icon"
android:ellipsize="marquee"
android:maxLines="1"
android:text="#{pojo.name}"
android:textColor="#android:color/black"
android:textSize="12sp" />
</RelativeLayout>
Adapter
public class Adapter extends MyBaseAdapter {
private List<POJO> itemList;
Context context;
public Adapter(List<POJO> itemList) {
this.itemList = itemList;
}
public Adapter(List<POJO> itemList, Context context) {
this.itemList = itemList;
this.context = context;
}
#Override
public Object getDataAtPosition(int position) {
return itemList.get(position);
}
#Override
public int getLayoutIdForType(int viewType) {
return R.layout.rowlayout;
}
#Override
public int getItemCount() {
return itemList.size();
}}
Try this in your adapter class not base adapter
holder.binding.getRoot().findViewById(R.id.icon).setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(context, "clicked", Toast.LENGTH_SHORT).show();
}
}
);
I have a problem in implementing a CustomAdapter for a Spinner using Android DataBinding with BaseAdapter.
The data has two values. I want to use two TextViews. The CustomAdapter must be inherited from BaseAdapter, the simpler variant with ArrayAdapter supports only one TextView. In the long run the second TextView could be an ImageView, so merging the two values into one String to be able to use an ArrayAdapter would still not help.
What I tried also: To make sure the idea and the Spinner works as normal, I implemented a version without DataBinding And I based my data binding implementation on chrislizh's project at https://github.com/chrislizh/SpinnerTwoWayDataBindingDemo, which is using an ArrayAdapter.
I also tried to call: binding.executePendingBindings(), and tried to not use the ViewHolder pattern.
The problem results in detail:
The project is about planets. The Spinner allows the selection of a planet. Each planet has a name and a distance. Both values will be displayed. The result of my implementation of the CustomAdapter, called PlanetAdapter, with DataBinding displays the first item twice, after a selection. See screenshots. Selection of any other planet than the first item, 'switches' its position with the selection, and keeps being displayed twice. This way one planet is always missing in the displayed list.
The code of the PlanetAdapter and the call to create it in the MainActivty's onCreate method:
PlanetAdapter
public class PlanetAdapter extends BaseAdapter
{
private int itemLayoutResourceId;
private final List<Planet> planets;
private LayoutInflater inflater;
public PlanetAdapter(#NonNull Context context, #LayoutRes int itemLayoutResourceId, List<Planet> planets)
{
inflater = LayoutInflater.from(context);
this.itemLayoutResourceId = itemLayoutResourceId;
this.planets = planets;
}
#Override
public int getCount()
{
return planets.size();
}
#Override
public Object getItem(int position)
{
return planets.get(position);
}
#Override
public long getItemId(int position)
{
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
PlanetViewHolder holder;
if (convertView == null) {
PlanetSpinnerItemBinding itemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.planet_spinner_item, parent, false);
itemBinding.setPlanet(planets.get(position));
holder = new PlanetViewHolder(itemBinding);
holder.view = itemBinding.getRoot();
holder.view.setTag(holder);
}
else {
holder = (PlanetViewHolder) convertView.getTag();
}
return holder.view;
}
private static class PlanetViewHolder
{
private View view;
PlanetViewHolder(PlanetSpinnerItemBinding binding)
{
this.view = binding.getRoot();
}
}}
Creation and setting the Adapter:
public class MainActivity extends AppCompatActivity implements IDataChangeListener
{
private static final String BUNDLE_SELECTED_PLANET = "bundle_selected_planet";
private ActivityMainBinding activityMainBinding_;
private List<Planet> planets_;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
activityMainBinding_ = DataBindingUtil.setContentView(this, R.layout.activity_main);
planets_ = loadPlanets(this, R.array.planetsInSolarSystem);
if (planets_ != null) {
planets_.add(0, new Planet("", 0)); // insert a blank item on the top of the list
PlanetAdapter planetAdapter = new PlanetAdapter(this, R.layout.planet_spinner_item, planets_);
activityMainBinding_.setSpinAdapterPlanet(planetAdapter);
Planet selectedPlanet = savedInstanceState != null ? savedInstanceState.<Planet>getParcelable(BUNDLE_SELECTED_PLANET) : planets_.get(3);//initial selected planet is Earth, 3 is the index of Earth after a blank item inserted
activityMainBinding_.setBindingPlanet(new BindingPlanet(this, selectedPlanet));
}
} // loadPlanets skipped.
}
The planet class:
public class Planet implements Parcelable {
private String name_;
private float distance_; //distance to Sun in AU(Astronomical Unit)
public Planet(String name, float distance) {
name_ = name;
distance_ = distance;
}
protected Planet(Parcel in) {
name_ = in.readString();
distance_ = in.readFloat();
}
public static final Creator<Planet> CREATOR = new Creator<Planet>() {
#Override
public Planet createFromParcel(Parcel in) {
return new Planet(in);
}
#Override
public Planet[] newArray(int size) {
return new Planet[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name_);
dest.writeFloat(distance_);
}
#Override
public String toString() {
return name_ != null ? name_ : super.toString();
}
public String getName() {
return name_;
}
public void setName(String name) {
name_ = name;
}
public float getDistance() {
return distance_;
}
public void setDistance(float distance) {
distance_ = distance;
}
}
The XML of the item layout: spinner_planet_item.xml
`
<data>
<variable
name="planet"
type="au.com.chrisli.spinnertwowaydatabindingdemo.Planet">
</variable>
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/planetName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{planet.name}"
tools:text="Dummy Planet"
android:textAppearance="#style/TextAppearance.AppCompat.Medium"/>
<TextView
android:id="#+id/planetDistance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="8dp"
android:text="#{String.valueOf(planet.distance)}"
android:textAppearance="#style/TextAppearance.AppCompat.Medium"
tools:text="2393.0 km"/>
</LinearLayout>
In the github code, I also included the classic case (without data binding) in the class PlanetAdapter, at the end of the file, but commented out. In case you want to see that it works - just switch getView und PlaneViewHolder implementations there.
The full modified project is on my github at: https://github.com/Minsky/SO_Question_SpinnerDataBindingBaseAdapter
What's wrong with my Binding implementation of the BaseAdapter?
As Commonsware wrote in his comment, binding the views in the recycle case was missing. The working code for the method getView() in PlanetAdapter is something like this:
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
PlanetViewHolder holder;
if (convertView == null) {
PlanetSpinnerItemBinding itemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.planet_spinner_item, parent, false);
holder = new PlanetViewHolder(itemBinding);
holder.view = itemBinding.getRoot();
holder.view.setTag(holder);
}
else {
holder = (PlanetViewHolder) convertView.getTag();
}
holder.binding.setPlanet(planets.get(position));
return holder.view;
}
And the PlanetViewHolder holds now the binding:
private static class PlanetViewHolder {
private View view;
private PlanetSpinnerItemBinding binding;
PlanetViewHolder(PlanetSpinnerItemBinding binding) {
this.view = binding.getRoot();
this.binding = binding;
}
}
final Github projet in branch "solved": https://github.com/Minsky/SO_Question_SpinnerDataBindingBaseAdapter/tree/solved
i'm working baseadapter.i have custom baseadapter.i use my adapter in spinner.i have one problem.mybaseadapter's OnItemClickedListener returned all my custom class
carbon.widget.Spinner$CustomClass#535ca050s
this is a result
this is a my code
public static class CustomClass {
private String Name;
public void setName(String name)
{
this.Name=name;
}
public String getName()
{
return Name;
}
public CustomClass(String name)
{
this.Name=name;
}
}
public static class Adapter extends RecyclerView.Adapter<ViewHolder, CustomClass> {
private ArrayList<CustomClass>items=new ArrayList<>();
#Override
public CustomClass getItem(int position) {
return items.get(position);
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.carbon_popup_row, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, final int position) {
super.onBindViewHolder(holder, position);
holder.tv.setText(items.get(position).getName());
Log.e("position String", items.get(position).getName() + "s");
}
#Override
public int getItemCount() {
return items.size();
}
public void setItems(ArrayList<CustomClass> items) {
this.items = items;
notifyDataSetChanged();
}
}
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView tv;
public ViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.carbon_itemText);
}
}
i inserted some values in my baseadapter like this
for (int i = 0; i < 40; i++) {
CustomClass customClass = new CustomClass("item" + i);
// customClass.setName("beka" + i);
list.add(customClass);
}
Spinner day = (Spinner) findViewById(R.id.day);
day.setItems(list);
this is a my setItem method
public void setItems(ArrayList<CustomClass> items) {
popupMenu.setAdapter(defaultAdapter);
defaultAdapter.setOnItemClickedListener(onItemClickedListener);
defaultAdapter.setItems(items);
}
RecyclerView.OnItemClickedListener onItemClickedListener = new RecyclerView.OnItemClickedListener() {
#Override
public void onItemClicked(int position) {
setText(popupMenu.getAdapter().getItem(position).toString());
Log.e("position String",popupMenu.getAdapter().getItem(position).toString()+"s");
popupMenu.dismiss();
}
};
as i said i can't return my custom class's getName. how i can solve my problem? if anyone knows solution please help me
p.s
i try to rewrite this example
https://github.com/ZieIony/Carbon
in this example autor used String arrays but i want to use my Custom Array
how i can solve my problem?
popupMenu.getAdapter().getItem(position) is returning CustomClass in your, and since you didn't override toString() in it, you are seeing the default implementation of Object.toString(). For the sake oh showing the name you could override it and return name. E.g.
#Override
public String toString() {
return name != null ? name : "no name set";
}
Alternatively you can simply call popupMenu.getAdapter().getItem(position).getName()
E.g.
CustomClass objectAtPos = popupMenu.getAdapter().getItem(position);
if (objectAtPos != null) {
setText(objectAtPos.getName());
Log.e("position String",objectAtPos.getName()+"s");
}
popupMenu.dismiss();