【问题标题】:Android: Issue with newView and bindView in custom SimpleCursorAdapterAndroid:自定义 SimpleCursorAdapter 中的 newView 和 bindView 问题
【发布时间】:2011-03-03 16:50:13
【问题描述】:

我从the only example I found 之一创建了一个自定义 SimpleCursorAdapter。

当我的 ListActivity 被调用时,我的每个 DB 条目都会调用 newView 和 bindView,并为每个条目再次调用。我有几个问题:

-这个例子对吗(如果不对,我在哪里可以找到)?

-如果 bindView 调用总是在 newView 调用之前,为什么在两个函数中都做同样的事情?

-为什么序列 newView-bindView 为每个项目调用两次?

-为什么有些 CursorAdapter 示例使用 getView 而不是 newView 和 bindView?

基本上SimpleCursorAdapter应该怎么用,我的代码有什么问题?

谢谢


列表活动

public class ContactSelection extends ListActivity {

    private WhipemDBAdapter mDbHelper;
    private FriendAdapter friendAdapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mDbHelper = new WhipemDBAdapter(this);
        mDbHelper.open();     

        setContentView(R.layout.contact_list);   

        Cursor c = mDbHelper.fetchAllFriends();
        startManagingCursor(c);     
        String[] from = new String[] {};
        int[] to = new int[] {};

        this.friendAdapter = new FriendAdapter(this, R.layout.contact_row, c, from, to);
        setListAdapter(this.friendAdapter);

        getListView().setItemsCanFocus(false);
        getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mDbHelper.open();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mDbHelper.close();
    }
}

自定义 SimpleCursorAdapter

public class FriendAdapter extends SimpleCursorAdapter implements OnClickListener {

    private Context mContext;
    private int mLayout;

    public FriendAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
        super(context, layout, c, from, to);

        this.mContext = context;
        this.mLayout = layout;
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {

        Cursor c = getCursor();

        final LayoutInflater inflater = LayoutInflater.from(context);
        View v = inflater.inflate(mLayout, parent, false);     

        String name = c.getString(c.getColumnIndex(WhipemDBAdapter.KEY_NAME));
        String fb_id = c.getString(c.getColumnIndex(WhipemDBAdapter.KEY_FB_ID));

        TextView name_text = (TextView) v.findViewById(R.id.contact_name);
        if (name_text != null) {
            name_text.setText(name);
        }

        ImageView im = (ImageView) v.findViewById(R.id.contact_pic);            
        Drawable drawable = LoadImageFromWebOperations("http://graph.facebook.com/"+fb_id+"/picture");
        if (im != null) {
            im.setImageDrawable(drawable);
        }

        CheckBox bCheck = (CheckBox) v.findViewById(R.id.checkbox);
        if (im != null) {
            bCheck.setTag(fb_id);
        }            

        if (((GlobalVars) mContext.getApplicationContext()).isFriendSelected(fb_id))
            bCheck.setChecked(true);

        bCheck.setOnClickListener(this);

        return v;
    }

    @Override
    public void bindView(View v, Context context, Cursor c) {

         String name = c.getString(c.getColumnIndex(WhipemDBAdapter.KEY_NAME));
         String fb_id = c.getString(c.getColumnIndex(WhipemDBAdapter.KEY_FB_ID));


         TextView name_text = (TextView) v.findViewById(R.id.contact_name);
         if (name_text != null) {
             name_text.setText(name);
         }

        ImageView im = (ImageView) v.findViewById(R.id.contact_pic);            
        Drawable drawable = LoadImageFromWebOperations("http://graph.facebook.com/"+fb_id+"/picture");
        if (im != null) {
            im.setImageDrawable(drawable);
        }

        CheckBox bCheck = (CheckBox) v.findViewById(R.id.checkbox);
        if (im != null) {
            bCheck.setTag(fb_id);
        }

        ArrayList<String> dude = ((GlobalVars) mContext.getApplicationContext()).getSelectedFriendList();

        if (((GlobalVars) mContext.getApplicationContext()).isFriendSelected(fb_id))
            bCheck.setChecked(true);

        bCheck.setOnClickListener(this);
    }


    @Override
    public void onClick(View v) {
        CheckBox cBox = (CheckBox) v;
        String fb_id = (String) cBox.getTag();

        if (cBox.isChecked()) {
            if (!((GlobalVars) mContext.getApplicationContext()).isFriendSelected(fb_id))
                ((GlobalVars) mContext.getApplicationContext()).addSelectedFriend(fb_id);
        } else {
            if (((GlobalVars) mContext.getApplicationContext()).isFriendSelected(fb_id))
                ((GlobalVars) mContext.getApplicationContext()).removeSelectedFriend(fb_id);
        }

    }

    private Drawable LoadImageFromWebOperations(String url)
    {
        try
        {
            InputStream is = (InputStream) new URL(url).getContent();
            Drawable d = Drawable.createFromStream(is, "src name");
            return d;
        }catch (Exception e) {
            System.out.println("Exc="+e);
            return null;
        }
    }

}

【问题讨论】:

    标签: android simplecursoradapter


    【解决方案1】:

    重写getView() 函数使您可以“重新使用”已经膨胀的列表项(当您来回滚动列表时,从当前视口“滚动出”的列表项)。

    这样做可以节省大量内存资源和处理器运行时间,因为膨胀是一项非常耗时的操作。对于您重复使用的每个 convertView,您还可以节省 GC 运行时间(因为垃圾收集器不必收集该特定列表项)。

    您还可以创建一个“视图集合”类(以下示例中的 ViewHolder 类),它将保存膨胀列表项中每个视图的引用。这样,您不必每次使用新值更新列表项时(通常在滚动列表时)都找到它们。 findViewById() 也是一个相当耗时的操作。

    我还认为您可以缓存更多变量,例如布局充气器和列索引。一切都是为了节省时间:-)

    private final Context mContext;
    private final int mLayout;
    private final Cursor mCursor;
    private final int mNameIndex;
    private final int mIdIndex;
    private final LayoutInflater mLayoutInflater;
    
    private final class ViewHolder {
        public TextView name;
        public ImageView image;
        public CheckBox checkBox;
    }
    
    public FriendAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
        super(context, layout, c, from, to);
    
        this.mContext = context;
        this.mLayout = layout;
        this.mCursor = c;
        this.mNameIndex = mCursor.getColumnIndex(WhipemDBAdapter.KEY_NAME);
        this.mIdIndex = mCursor.getColumnIndex(WhipemDBAdapter.KEY_FB_ID);
        this.mLayoutInflater = LayoutInflater.from(mContext);
    }
    
    public View getView(int position, View convertView, ViewGroup parent) {
        if (mCursor.moveToPosition(position)) {
            ViewHolder viewHolder;
    
            if (convertView == null) {
                convertView = mLayoutInflater.inflate(mLayout, null);
    
                viewHolder = new ViewHolder();
                viewHolder.name = (TextView) convertView.findViewById(R.id.contact_name);
                viewHolder.image = (ImageView) convertView.findViewById(R.id.contact_pic);
                viewHolder.checkBox = (CheckBox) convertView.findViewById(R.id.checkbox);
    
                convertView.setTag(viewHolder);
            }
            else {
                viewHolder = (ViewHolder) convertView.getTag();
            }
    
            String name = mCursor.getString(mNameIndex);
            String fb_id = mCursor.getString(mIdIndex);
            Drawable drawable = LoadImageFromWebOperations("http://graph.facebook.com/"+fb_id+"/picture");
            boolean isChecked = ((GlobalVars) mContext.getApplicationContext()).isFriendSelected(fb_id);
    
            viewHolder.name.setText(name);
            viewHolder.image.setImageDrawable(drawable);
            viewHolder.checkBox.setTag(fb_id);
            viewHolder.checkBox.setChecked(isChecked);
        }
    
        return convertView;
    }
    

    【讨论】:

    • 这非常有用。感谢您的时间。我现在正在调查为什么我的数据库中的每个项目都调用 getVBiew 两次。
    • 很好的答案,谢谢。但是如果我们想在 ImageView 中添加一个 onClick 监听器,我们应该怎么做呢?我在我的自定义适配器上添加了一个 onClick(View v) 方法,但它不起作用。有什么提示吗?
    • 我不太确定如何将点击侦听器添加到列表项中的特定视图。我什至不确定这是否是个好主意。我猜您可能需要以某种方式“停用”列表的单击侦听器,或者至少在 onClick 事件到达列表之前使用它,然后实例化您自己的 onClickListener (也许在 Activity 中创建它)并将其传递给您的自定义列表适配器(可能在构造函数中),然后将其分配给您的所有图像。不过,我以前没有做过这样的动作,我不知道哪些邪恶的警告潜伏在黑暗中:-)
    • 我只能猜测;但是您的任何成员字段static
    【解决方案2】:

    这个例子几乎是对的。您不需要在newView() 中进行绑定,因为就像您提到的那样,bindView() 将被调用。如果您看到序列 newView/bindView 每个项目调用两次,您可能正在使用 ListView 并将其高度设置为 wrap_content,这总是一个坏主意。最后,newView()bindView() 特定于CursorAdapter:它实现getView() 并为您调用newView()bindView()。但是,覆盖 getView() 也是完全有效的。这就是其他适配器的工作方式。

    请注意,getView()(以及因此 bindView/newView)仅针对将在屏幕上显示的每个项目调用。

    【讨论】:

    • 非常感谢您的澄清。 ListView 的高度未设置为 wrap_content 思想。
    • 我刚刚找到了您的演讲视频“让您的 Android UI 快速高效”。我希望我早点找到它!
    • 所以当我使用newView BindView 时,我还得去getView 吗?另外,我使用的是标签模式而不是持有人模式。我仍然得到重复的值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-22
    • 1970-01-01
    相关资源
    最近更新 更多