【问题标题】:How do you change the visibility of widgets inside individual listview rows from onlistitemclick in Android?如何从 Android 中的 onlistitemclick 更改单个列表视图行中小部件的可见性?
【发布时间】:2012-01-13 17:10:34
【问题描述】:

我的最终目标是将this tutorial 中的信息来源从数组更改为游标。这是full code 的链接。它的要点是您单击列表中的一行,然后在列出的标题下方弹出主体,然后再次点击它,主体就消失了。我不想记住笔记是否打开,我也不想通过视图回收保持它打开,也不想在你打开一个或任何其他我能想到的花哨排列时关闭所有其他的。

一切正常,但是当 onListItemClick 事件处理程序触发,更改可见性和notifyDataSetChanged()s 时,列表会做一些奇怪的事情,包括单击两次以更改可见性,而不是重新测量自身,导致列表行每三次点击左右才为自己腾出空间。

之前的尝试导致了一个完美的工作列表,除了点击时,我想要隐藏和显示的每一行中的块会隐藏和显示,并且教程中的列表工作完美,但当然使用静态大小在所有方面,都只是一个模板。

我确信问题在于获取附加到列表行的可见性信息或在设置后更改它。

这里是onListItemClick

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
    super.onListItemClick(l, v, position, id);
    ((NotesCursorAdapter) getListAdapter()).toggle(position, v);
}

NotesCursorAdapter里面的toggle方法:

    public void toggle(int position, View view) {
        ViewHolder holder = (ViewHolder) view.getTag();

        holder.mExpanded[position] = !holder.mExpanded[position];
        notifyDataSetChanged();
    }

NotesCursorAdapter之外的ViewHolder

static class ViewHolder {
    public TextView title;
    public TextView body;
    public boolean mExpanded[];
}

还有NotesCursorAdapter 本身:

class NotesCursorAdapter extends CursorAdapter {

    private static final int VISIBLE = 0;
    private static final int GONE = 8;

    public NotesCursorAdapter(Context context, Cursor c) {
        super(context, c);
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View rowView = inflater.inflate(R.layout.row, null, true);

        ViewHolder holder = new ViewHolder();
        holder.title = (TextView) rowView
                .findViewById(R.id.title);
        holder.body = (TextView) rowView
                .findViewById(R.id.body);
        holder.mExpanded = new boolean[cursor.getCount()];
        rowView.setTag(holder);

        return rowView;
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        ViewHolder holder = (ViewHolder) view.getTag();

        holder.title.setText(cursor.getString(cursor
                .getColumnIndexOrThrow(DbAdapter.KEY_TITLE))
                + holder.mExpanded[cursor.getPosition()]);
        holder.body.setText(cursor
                .getString(cursor
                        .getColumnIndexOrThrow(DbAdapter.KEY_BODY)));
        holder.body
                .setVisibility(holder.mExpanded[cursor.getPosition()] ? VISIBLE : GONE);

    }

    public void toggle(int position, View view) {
        ViewHolder holder = (ViewHolder) view.getTag();

        holder.mExpanded[position] = !holder.mExpanded[position];
        notifyDataSetChanged();
    }
}

我不知道下一步该往哪里看。我需要制作自己的getView() 方法吗?我能从getItem() 中得到什么有用的东西吗?尝试像这样使用listview 我是不是完全疯了?

我进行了更多调查,代码正在运行,但点击事件似乎影响了相反的观点。我的意思是,当您单击顶部列表项时,它会影响底部列表项。当您单击顶部的第二个时,它会影响底部的第二个。如果列表项的数量为奇数,则中间的列表项可以正常工作。在某种程度上,无论我用什么方式来确定我正在影响的视图的 id 都是翻转的。 listview 是否从下往上对事物进行编号?

【问题讨论】:

    标签: android listview visibility


    【解决方案1】:

    我刚刚也从 API Demos 的 List6 中实现了类似的东西。我的工作完美(尽管我无法从 ImageButton 获得焦点)。我使用了 SimpleCursorAdapter,但我想您可以使用 CursorAdapter。我修改了你的代码。

    不知道你为什么还添加了 View 参数,所以我把它去掉了。

    private ArrayList<Boolean> mExpanded;
    
    @Override
        protected void onListItemClick(ListView l, View v, int position, long id) {
            super.onListItemClick(l, v, position, id);
            ((NotesCursorAdapter) getListAdapter()).toggle(position);
        }
    

    您的 ViewHolder。很简单,只是构造函数从你的 getView 接收参数。

    private class ViewHolder {
    private TextView title;
    private TextView body;
    
    public ViewHolder(View convertView, boolean expanded) {
    
    title = (TextView) convertView.findViewById(R.id.title);
    body = (TextView) convertView.findViewById(R.id.body);
    
    //not sure if you need this...
    //body.setVisibility(expanded ? View.VISIBLE : View.GONE);
    }
    
    public void setTitle(int position) {
        cursor.moveToPosition(position);
        title.setText(cursor.getString(cursor
            .getColumnIndexOrThrow(PowersDbAdapter.KEY_TITLE)));
    }
    
    public void setBody(int position) {
        cursor.moveToPosition(position);
        body.setText(cursor.getString(cursor
                    .getColumnIndexOrThrow(PowersDbAdapter.KEY_BODY)));
    }
    
    public void setExpanded(boolean expanded) {
        body.setVisibility(expanded ? View.VISIBLE : View.GONE);
        }       
    }
    

    这是您的自定义 ListAdapter。我不确定你为什么要使用带有 ViewHolder 的 bindView 和 newView,Android 使用 bindView 和 newViews 来处理。但是在这种情况下您需要使用 ViewHolder,因此 getView 是必要的。

    private class NotesCursorAdapter extends SimpleCursorAdapter {
    
    private Cursor cursor;
    private LayoutInflater inflater;
    
    public NotesCursorAdapter(Context context, int layout, Cursor cursor, String[] from, int[] to) {
        super(context, layout, cursor, from, to);
        this.cursor = cursor;
        inflater = LayoutInflater.from(context);
        mExpanded = new ArrayList<Boolean>();
    }
    
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;          
        if (convertView == null) {
            convertView = inflater.inflate(R.layout.row, parent, false);
    
            populateExpanded();
    
            holder = new ViewHolder(convertView, mExpanded.get(position));
    
            convertView.setTag(holder);
        }
        else {
            holder = (ViewHolder) convertView.getTag();
        }
            holder.setTitle(position);
            holder.setBody(position);
            holder.setExpanded(mExpanded.get(position));
    
            return rowView;
    }
    
    private void populateExpanded() {
        // We want our hidden TextView to not be expanded initially
        for (int i = 0; i < cursor.getCount(); i++) {
            mExpanded.add(false);
        }
    }
    
    public void toggle(int position) {
    if (mExpanded.get(position) == false) {
        mExpanded.set(position, true);
    }
    else {
        mExpanded.set(position, false);
    }
        notifyDataSetChanged();
        }
    }
    

    下面是如何使用 SimpleCursorAdapter 映射它

    setListAdapter(new NotesCursorAdapter(MyClass.this, R.layout.row, cursor,
                new String[] { "my_title", "my_body" }, new int[] { R.id.MyTitleTextView, R.id.MyBodyTextView }));
    

    【讨论】:

    • 我正在尝试让您的代码正常工作。在哪里调用 setExpanded() 以及当持有人为空时如何设置持有人上的数组大小?我将视图作为参数,因为我在尝试不同的东西,所以我让构造函数接受我能得到的任何东西。我使用 CursorAdapter 而不是 SimpleCursorAdaptor 因为我最终会做一些不简单的事情,而且我不认为 SimpleCursorAdapter 的目的不仅仅是将字符串映射到 TextViews 和 ImageViews。 CursorAdapter 需要 newView() 和 bindView() 方法的代码。
    • 好的,我修复了一些错误。我创建了一个方法来为布尔数组设置初始值。就像他们在 List6 中预定义的一样(但这是一种静态的廉价方式,而不是动态的)。 SimpleCursorAdapter 可以做你想做的一切。在我的应用程序中,我有一个文本视图(如您的标题)和一个线性布局,其可见性为 GONE。它包含两个图像按钮(带有 onclicklisteners)和一个文本视图(显示一个数字)。这是非常不简单的。你到底有什么打算,你以后要做什么?
    • 我明白了。问题是我正在跟踪适配器和视图持有者中扩展视图的状态。一旦我在适配器类中添加了一个布尔数组,然后用它来确定视图的状态,应用程序就开始做一些奇怪的事情,当我从视图持有者中删除可见性布尔值时,它工作得很好。非常感谢你的帮助。让我尝试一个简单的光标适配器让我真正了解了 getView()/newView()/bindView() 系统的工作原理,并让我真正认真地了解了对象的来源。
    • 太棒了!不是问题!我遇到这个问题真的很良心,因为我同时在做基本相同的事情。但是我再次更改了代码,因为我在 ArrayIndexOutofBounds 上遇到了问题,所以我切换到了 ArrayLists。
    【解决方案2】:

    K,我明白了。设置显示不正确,因为 ListView 有两层:视图层和数据层。 android 在 ListView 中回收 Views 的方式意味着使用哪个 View 来显示数据并不重要。因此,要使用 CursorAdapter,视图信息进入 newView() 方法,来自 Cursor 的数据信息进入 bindView() 方法。这是固定的东西:

    这里没有太大变化,只传递需要的东西:

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        ((NotesCursorAdapter) l.getAdapter()).toggle(position);
    }
    

    视图持有者只获取构建视图所需的信息,而不是视图中的任何信息,包括可见状态:

    static class ViewHolder {
        public TextView title;
        public TextView body;
        public Button button;
    }
    

    可见状态在数组中作为适配器的属性进行跟踪。视图信息在 newView() 方法中,数据信息在 bindView() 方法中。当 notifyDataSetChanged() 触发时,所有视图都被回收,数据被重新分层。因为视图信息和数据信息是分开的,所以哪个视图与哪个数据匹配并不重要。

        class NotesCursorAdapter extends CursorAdapter {
    
        private boolean[] expandedArray;
    
        public NotesCursorAdapter(Context context, Cursor c) {
            super(context, c);
            expandedArray = new boolean[c.getCount()];
        }
    
        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            View rowView = inflater.inflate(R.layout.row, parent, false);
    
            ViewHolder holder = new ViewHolder();
            holder.title= (TextView) rowView
                    .findViewById(R.id.title);
            holder.body= (TextView) rowView
                    .findViewById(R.id.body);
            holder.button= (Button) rowView
                    .findViewById(R.id.button);
    
            rowView.setTag(holder);
            return rowView;
        }
    
        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            ViewHolder holder = (ViewHolder) view.getTag();
    
            holder.title.setText(cursor.getString(cursor
                    .getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
            holder.body.setText(cursor.getString(cursor.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
            holder.body
                    .setVisibility(expandedArray[cursor.getPosition()] ? View.VISIBLE
                            : View.GONE);
            holder.button
                    .setVisibility(expandedArray[cursor.getPosition()] ? View.VISIBLE
                            : View.GONE);
            holder.button.setTag(cursor.getPosition());
    
        }
    
        public void toggle(int position) {
            expandedArray[position] = !expandedArray[position];
            notifyDataSetChanged();
        }
    }
    

    为了完整起见,这里是在 onCreate()、onActivityResult() 或列表需要重新加载时调用的 ListAdapter 映射的映射:

    private void fillData() {
        notesCursor = mDbHelper.fetchAllNotes();
        startManagingCursor(notesCursor);
        NotesCursorAdapter notes = new NotesCursorAdapter(this, notesCursor);
        setListAdapter(notes);
    }
    

    【讨论】:

      猜你喜欢
      • 2022-08-14
      • 1970-01-01
      • 2013-01-29
      • 2019-09-02
      • 2014-07-04
      • 2012-11-26
      • 2016-09-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多