如何对从 BaseAdapter 继承的 CustomAdapter for Spinner 进行 Android 数据绑定? [英] How to do Android Data Binding a CustomAdapter inherited from BaseAdapter for Spinner?

查看:27
本文介绍了如何对从 BaseAdapter 继承的 CustomAdapter for Spinner 进行 Android 数据绑定?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在使用带有 BaseAdapter 的 Android DataBinding 为 Spinner 实现 CustomAdapter 时遇到问题.

数据有两个值.我想使用两个 TextView.CustomAdapter 必须从 BaseAdapter 继承,ArrayAdapter 的更简单的变体只支持一个 TextView.从长远来看,第二个 TextView 可能是一个 ImageView,因此将两个值合并为一个 String 以便能够使用 ArrayAdapter 仍然无济于事.

我也尝试过:为了确保这个想法和 Spinner 正常工作,我实现了一个没有 DataBinding 的版本,我的数据绑定实现基于

PlanetAdapter 的代码以及在 MainActivty 的 onCreate 方法中创建它的调用:

行星适配器

public class PlanetAdapter extends BaseAdapter{私有 int itemLayoutResourceId;私人最终名单<Planet>行星;私人 LayoutInflater 充气机;public PlanetAdapter(@NonNull Context context, @LayoutRes int itemLayoutResourceId, List Planets){充气器 = LayoutInflater.from(context);this.itemLayoutResourceId = itemLayoutResourceId;this.planets = 行星;}@覆盖公共 int getCount(){返回行星大小();}@覆盖公共对象 getItem(int position){返回行星.get(位置);}@覆盖public long getItemId(int position){返回位置;}@覆盖public View getView(int position, View convertView, ViewGroup parent){PlanetViewHolder 支架;if (convertView == null) {PlanetSpinnerItemBinding itemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.planet_spinner_item, parent, false);itemBinding.setPlanet(planets.get(position));持有人 = 新的 PlanetViewHolder(itemBinding);holder.view = itemBinding.getRoot();持有人.view.setTag(持有人);}别的 {持有人 = (PlanetViewHolder) convertView.getTag();}返回持有人.view;}私有静态类 PlanetViewHolder{私人视图;PlanetViewHolder(PlanetSpinnerItemBinding 绑定){this.view = binding.getRoot();}}}

创建和设置适配器:

 公共类 MainActivity 扩展 AppCompatActivity 实现 IDataChangeListener{private static final String BUNDLE_SELECTED_PLANET = "bundle_selected_planet";私人 ActivityMainBinding 活动MainBinding_;私人列表<Planet>行星_;@覆盖protected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);activityMainBinding_ = DataBindingUtil.setContentView(this, R.layout.activity_main);行星_ = loadPlanets(this, R.array.planetsInSolarSystem);如果(行星_!= null){行星_.add(0, 新行星("", 0));//在列表顶部插入一个空白项PlanetAdapterplanetAdapter = new PlanetAdapter(this, R.layout.planet_spinner_item,planets_);activityMainBinding_.setSpinAdapterPlanet(planetAdapter);行星 selectedPlanet = savedInstanceState != null ?savedInstanceState.<Planet>getParcelable(BUNDLE_SELECTED_PLANET) :planets_.get(3);//初始选择的行星是地球,3是插入空白项后地球的索引activityMainBinding_.setBindingPlanet(new BindingPlanet(this, selectedPlanet));}}//loadPlanets 跳过.}

行星类:

public class Planet 实现 Parcelable {私人字符串名称_;私人浮动距离_;//到太阳的距离(天文单位)公共行星(字符串名称,浮动距离){名称_ = 名称;距离_ = 距离;}受保护的星球(包裹在){name_ = in.readString();distance_ = in.readFloat();}public static final Creator<Planet>创造者 = 新创造者<星球>() {@覆盖公共星球 createFromParcel(Parcel in) {返回新的行星(输入);}@覆盖公共行星[] newArray(整数大小){返回新的行星[大小];}};@覆盖公共 int describeContents() {返回0;}@覆盖public void writeToParcel(Parcel dest, int flags) {dest.writeString(name_);dest.writeFloat(distance_);}@覆盖公共字符串 toString() {返回名称_ != null ?name_ : super.toString();}公共字符串 getName() {返回名称_;}公共无效集名称(字符串名称){名称_ = 名称;}公共浮动 getDistance() {返回距离_;}公共无效设置距离(浮动距离){距离_ = 距离;}}

物品布局的XML:spinner_planet_item.xml`

<变量名称=行星"type="au.com.chrisli.spinnertwowaydatabindingdemo.Planet"></变量></数据><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"机器人:方向=水平"android:layout_width="match_parent"android:layout_height="match_parent"><文本视图android:id="@+id/行星名称"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{planet.name}"工具:文本=虚拟星球"android:textAppearance="@style/TextAppearance.AppCompat.Medium"/><文本视图android:id="@+id/planetDistance"android:layout_width="wrap_content"android:layout_height="wrap_content"机器人:paddingLeft =8dp"android:text="@{String.valueOf(planet.distance)}"android:textAppearance="@style/TextAppearance.AppCompat.Medium"工具:文本=2393.0公里"/></LinearLayout>

在github代码中,我也在类PlanetAdapter中包含了经典案例(没有数据绑定),在文件末尾,但注释掉了.如果您想查看它是否有效 - 只需在那里切换 getView 和 PlaneViewHolder 实现即可.

完整修改后的项目在我的 github 上:https://github.com/Minsky/SO_Question_SpinnerDataBindingBaseAdapter

我的 BaseAdapter 的 Binding 实现有什么问题?

Commonsware 在他的评论中写道,在回收案例中绑定视图缺失.PlanetAdapter 中 getView() 方法的工作代码如下所示:

@Overridepublic View getView(int position, View convertView, ViewGroup parent){PlanetViewHolder 支架;if (convertView == null) {PlanetSpinnerItemBinding itemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.planet_spinner_item, parent, false);持有人 = 新的 PlanetViewHolder(itemBinding);holder.view = itemBinding.getRoot();持有人.view.setTag(持有人);}别的 {持有人 = (PlanetViewHolder) convertView.getTag();}holder.binding.setPlanet(planets.get(position));返回持有人.view;}

现在 PlanetViewHolder 持有绑定:

私有静态类PlanetViewHolder {私人视图;私有 PlanetSpinnerItemBinding 绑定;PlanetViewHolder(PlanetSpinnerItemBinding 绑定){this.view = binding.getRoot();this.binding = 绑定;}}

已解决"分支中的最终 Github 项目:https://github.com/Minsky/SO_Question_SpinnerDataBindingBaseAdapter/tree/已解决

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

这篇关于如何对从 BaseAdapter 继承的 CustomAdapter for Spinner 进行 Android 数据绑定?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆