4

更新:不再出现错误!现在请评论模式本身。优点和缺点。你喜欢什么,你不喜欢什么。什么可以修。仍然不明白我为什么这样做......让我知道(但请阅读下面的帖子)

我一直在努力为 Android 中的 BaseAdapters 创建一个新的设计模式,到目前为止,我真的很喜欢它的结果!


我有一个数据结构,可以为特定类型的集合中的每个实体保存所有需要的数据。我正在使用自己的 UI 布局创建特定的 BaseAdapter 实现,以在屏幕上显示给用户。非常基本和伟大的想法......没有什么新鲜事。


好的,那我为什么要这个?

这样做的总体思路是 1) 抽象出 getView 方法的实现,让更多的业余开发人员更容易创建自己的自定义适配器,以及 2) 简化和抽象出尽可能多的细节。

注意:在最后 2 段代码中,我只有 3 件事要实现(更有意义)。当前使用的 Holder 模式中存在的内部类、SetLayoutResource(...) 方法和 ExtractLayoutResources(...) 方法。

我想要这个并认为它很好的三个关键原因是......抽象,抽象,抽象!


具体CustomAdapter.java

(具体的示例代码......这基本上就是一切的样子!!!这基本上是您创建新的自定义 BaseAdapter 必须输入的所有内容!!!)

public class ConcreteCustomAdapter extends BaseDataAdapter<Song, SongHolder> {

    public ConcreteCustomAdapter(Context context, int resource, Song[] data) {

        super(context, resource, data); 

        // Give the base class a reference to the actual type of Holder class to use
        this.setViewHolder(new SongHolder());

    }

    @Override
    protected void setLayoutResources(SongHolder holder, Song data) {

        // Set the View Holder objects properties with the current data
        holder.imgUiControl.setImageResource(data.thumbnail);
        holder.txtUiControl.setText(data.Name);

    }

    @Override
    protected void extractLayoutResources(View row, SongHolder holder) {


        // Convert XML UI component definitions into the static View Holder object
        holder.imgUiControl = (ImageView) row.findViewById(R.id.imgUiControl);
        holder.txtUiControl = (TextView) row.findViewById(R.id.txtUiControl);

    }

    // Class that holds all the UI component references
    static class SongHolder implements IHolder {

        ImageView imgUiControl;
        TextView txtUiControl;

    }

}

歌曲.java

(用户创建!)

// Entity that holds ALL the data
public class Song implements IData {

    public int thumbnail;
    public String Name;
    ...

    // Constructors, Getters, Setters
    ...
}

代码的其余部分用于支持目的


IHolder.java

// Current Adapter Pattern uses Holder Objects, this represents that and the data via interface
public interface IHolder {

    interface IData { } 

}

BaseDataAdapter.java

(用户不应触摸此)

// D for Data....H for Holder (sorry not convention)
public abstract class BaseDataAdapter<D extends IData, H extends IHolder> extends BaseAdapter {

    private Context context;
    private int layoutResourceID;
    private D data[] = null;
    private H holder = null;

    public BaseDataAdapter(Context context, int resource, D[] data) {
        //super(context, resource, data);

        this.layoutResourceID = resource;
        this.context = context;
        this.data = data;

    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        //super.getView(position, convertView, parent);

        View row = convertView;

        if (row == null) {

            LayoutInflater newView = ((Activity) context).getLayoutInflater();
            row = newView.inflate(this.layoutResourceID, parent, false);

            extractLayoutResources(row, holder);

            row.setTag(holder);

        } else {

            holder = (H) row.getTag();

        }

        setLayoutResources(holder, data[position]);

        return row;

    }

    public void setViewHolder(H holder) {

        this.holder = holder;

    }

    abstract protected void setLayoutResources(H holder, D data);
    abstract protected void extractLayoutResources(View row, H holder);


    @Override
    public int getCount() { return this.data.length; }

    @Override
    public Object getItem(int position) { return this.data[position]; }

    @Override
    public long getItemId(int position) { return 0; }

}
4

3 回答 3

2

我会这样做:

public abstract class TypedListAdapter<T, H extends TypedListAdapter.ViewHolder> extends BaseAdapter {

  private final int itemViewId;
  private List<T> data;

  public TypedAdapter(final int itemViewId) {
    this(itemViewId, null);
  }

  public TypedAdapter(final int itemViewId, final List<T> data) {
    this.itemViewId = itemViewId;
    this.data = preventNull(data);
  }

  private List<T> preventNull(final List<T> data) {
    return data == null ? Collections.<T>emptyList() : data;
  }

  public void setData(final List<T> data) {
    final List<T> nonNullData = preventNull(data);
    if (nonNullData != this.data) {
      this.data = nonNullData;
      notifyDataSetChanged();
    }
  }

  @Override
  public int getCount() {
    return data.size();
  }

  @Override
  public T getItem(final int position) {
    return data.get(position);
  }

  @Override
  public long getItemId(final int position) {
    return position;
  }

  @Override
  public View getView(final int position, final View convertView, final ViewGroup parent) {
    final H holder = obtainHolder(convertView, parent);
    bind(holder, getItem(position));
    return holder.view;
  }

  protected abstract void bind(final H holder, final T item);

  private H obtainHolder(final View convertView, final ViewGroup parent) {
    if (convertView == null) {
      final View view = LayoutInflater.from(parent.getContext()).inflate(itemViewId, parent, false);
      return createHolder(view);
    } else {
      return (H) convertView.getTag();
    }
  }

  protected abstract H createHolder(final View view);

  public static class ViewHolder {
    public final View view;

    public ViewHolder(final View view) {
      this.view = view;
      view.setTag(this);
    }
  }
}

适配器的实现可能如下所示:

public class SongListAdapter extends TypedListAdapter<Song, SongListAdapter.SongHolder> {

  public SongListAdapter(final int itemViewId) {
    super(itemViewId);
  }

  public SongListAdapter(final int itemViewId, final List<Song> data) {
    super(itemViewId, data);
  }

  @Override
  protected void bind(final SongHolder holder, final Song item) {
    holder.name.setText(item.getName());
    holder.thumbnail.setImageResource(item.getThumbnail());
  }

  @Override
  protected SongHolder createHolder(final View view) {
    return new SongHolder(view);
  }

  public static class SongHolder extends ViewHolder {

    public final ImageView thumbnail;
    public final TextView name;

    public SongHolder(final View view) {
      super(view);

      thumbnail = (ImageView) view.findViewById(R.id.thumbnail);
      name = (TextView) view.findViewById(R.id.name);
    }
  }

}
于 2013-09-26T06:27:11.743 回答
1

关于您的方法 SetLayoutResource 的错误,您忘记了转换 IHolder。

 @Override
protected void SetLayoutResource(IHolder holder, int position) {

    Song currentSong = data[position];

    // ERRORS HERE: imgSongThumbnail and company cannot be resolved, or is not a field (in IHolder!)
    holder.imgSongThumbnail.setImageResource(currentSong.thumbnail);
    holder.txtSongName.setText(currentSong.Name);
    ....

}

应该

 @Override
protected void SetLayoutResource(IHolder holder, int position) {

    Song currentSong = data[position];
    SongHolder songHolder = (IHolder) holder;
    // ERRORS HERE: imgSongThumbnail and company cannot be resolved, or is not a field (in IHolder!)
    songHolder.imgSongThumbnail.setImageResource(currentSong.thumbnail);
    songHolder.txtSongName.setText(currentSong.Name);
    ....

}

您可以在以下链接中下载一个工作示例 https://docs.google.com/file/d/0B5bbdw1x2IDET1YxZE4wdnpxZnc/edit?usp=sharing

于 2013-09-25T23:43:57.240 回答
1

首先是一些一般性评论:

  • 方法名称应以小写字母开头。
  • 我个人建议重命名您的泛型类型以便更好地理解。因为T我会TDataV我会使用类似的东西VHolder

现在来回答:

由于您使用的是泛型,因此您还应该在方法签名中使用它们:

abstract protected void setLayoutResource(THolder holder, int position);
abstract protected THolder extractLayoutResources(ViewGroup parent, View row, THolder holder);

你现在最终在你的GeneralSongListAdapter

@Override
protected SongHolder ExtractLayoutResources(ViewGroup parent, View row,
    SongHolder holder) {

现在您应该能够直接访问这些字段而无需强制转换。

此外,我建议重命名您的抽象方法。我不认为他们现在的名字正确地说明了该方法应该做什么。如果你仔细观察,CursorAdapter你会看到createViewbindView这正是你正在做的事情。也许使用这个命名。

于 2013-09-26T07:18:05.560 回答