ListView:如何从外部以编程方式访问 Item 的元素?

Posted

技术标签:

【中文标题】ListView:如何从外部以编程方式访问 Item 的元素?【英文标题】:ListView: how to access Item's elements programmatically from outside? 【发布时间】:2014-12-09 08:49:23 【问题描述】:

我有以下情况。

我有一个 ListView,ListView 的每个项目都由不同的小部件(TextViews、ImageViews 等)组成,在自定义适配器的 getView() 方法中膨胀形成一个布局。

现在,我想实现以下目标:

当某个事件被触发时,我想更改项目内的 View 的背景。

请问我该怎么做?


这是项目布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/cardlayout"
android:layout_
android:layout_
android:background="@android:color/transparent"
android:orientation="vertical"
android:paddingBottom="5dp"
android:paddingRight="5dp"
android:paddingTop="5dp" >

<FrameLayout
    android:layout_
    android:layout_ >

    <View
        android:id="@+id/card"
        android:layout_
        android:layout_
        android:background="@drawable/card_selector" />

</FrameLayout>  
</LinearLayout>

我需要更改card的背景

我试过这样做:

View v=lv.getAdapter().getView(index, null, lv);
View card =(View)v.findViewById(R.id.card);
card.setBackgroundResource(R.drawable.pressed_background_card);

但是没有成功:-((

【问题讨论】:

"但是没有成功:-(("为什么? @mmlooloo 因为它不起作用,如果我按照我的问题做,没有任何改变 【参考方案1】:

当你的事件被触发时,你应该在你的适配器上调用一个 notifyDataSetChanged,这样它就会再次调用你所有可见元素的 getView。

您的 getView 方法应该考虑到某些元素可能具有不同的背景颜色(如果元素不需要更改背景,请不要忘记将其设置为正常颜色,否则通过回收您将拥有许多具有更改背景的元素滚动时)

编辑:

我会尝试这样的:

@Override
public View getView(int position, View convertView, ViewGroup parent) 
    if(convertView == null)
    
        convertView = LayoutInflater.from(getContext()).inflate(R.layout.card, parent, false);
    

    //This part should also be optimised with a ViewHolder
    //because findViewById is a costly operation, but that's not the point of this example
    CardView cardView =(CardView)convertView .findViewById(R.id.card);

    //I suppose your card should be determined by your adapter, not a new one each time
    Card card = getItem(position);

    //here you should check sthg like the position presence in a map or a special state of your card object
    if(mapCardWithSpecialBackground.contains(position))
    
        card.setBackgroundResource(specialBackground);
    
    else
    
        card.setBackgroundResource(normalBackground);
    
    cardView.setCard(card);

    return convertView;

在特殊事件中,我会将项目的位置添加到地图中并调用 notifyDataSetChanged。

【讨论】:

谢谢 Xavier,就像我对 Mahmoud 的回答的评论一样,我不能打电话给 notifyDataSetChanged() :-((( 这应该是公认的答案。 Xavier,您应该在此处添加您所做的@Mahmouds 回答的 cmets,因为这是很好的信息。【参考方案2】:

使用 onitemclicklistener 方法 onclicksomething.. 需要四个或五个参数。 (查看父级、查看视图、int 位置、int id)。使用 view 参数自定义背景。

更新 这是我的一些代码,如果你不明白,我建议阅读有关回收和 ViewHolder 模式的内容。

@Override
    public View getView(int position, View convertView, ViewGroup parent) 
    
            ViewHolder viewHolder;
            // If convertView isn't a recycled view, create a new.
            if(convertView == null)
                LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = inflater.inflate(R.layout.row_gallery_frame, parent, false);

                viewHolder = new ViewHolder();
                // Here you must be able to find your Widget inside convertView and set a listener to it I guess?
                viewHolder.nameHolder = (TextView) convertView.findViewById(R.id.nameTv);
                // Set a reference to newly inflated view
                convertView.setTag(viewHolder);
            
            // If it is, then get the ViewHolder by tag
            else
                viewHolder = (ViewHolder)convertView.getTag();
            
            // Set the data
            GalleryFrame galleryFrame = galleryFrameArrayList.get(position);

            viewHolder.nameHolder.setText(galleryFrame.getName());

            return convertView;
        
    
    // Viewholder pattern which holds all widgets used
    public static class ViewHolder
        public TextView nameHolder;
    

【讨论】:

感谢 Rodv,问题是我需要一个位于 View 内部的 Wideget... :(( 但是使用 mysomething = (someview)view.findviewbyid(R.id.widgetid); mysomething.backgroundcolor... Rody,谢谢Rody,这行不通,如果你这样调用findviewbyid,你会得到null 您能否提供有关您的自定义适配器在问题中的外观的更多信息?我以前做过,应该不难解决。 @LisaAnne 你得到空值是因为你每次向下滚动并显示更多项目时都会创建新视图。因此,它不会知道以前创建的那些。如果您可能已经注意到,这可能会导致滚动延迟。换句话说,您必须使用 ViewHolder 模式来回收您的视图。我会用一个例子来更新我的答案。等一下:)【参考方案3】:

我假设您有一个模型对象,用于“绘制”列表项,例如,背景颜色是根据 boolean 或其他东西确定的。

您需要做的就是更改您决定TextView 应该具有哪种背景颜色的值。

你的getView() 方法应该有这样的代码

if (myModelObj.isBrown()) 
    myTextView.setBackgroundResource(R.drawable.brown_bg);
else
    myTextView.setBackgroundResource(R.drawable.not_brown_bg);

当你的事件被触发时,你应该做的就是在你的模型中设置 brown 布尔值 并在您的适配器上调用notifyDataSetChanged()

编辑

如果由于某种原因您不想调用 nofitfyDataSetChanged(),虽然它不会移动列表的滚动位置,并且通过正确的回收不会导致性能下降

您可以找到代表您要编辑的列表项的 View 对象(如果它是可见的),然后只需更改其中的背景,根本不需要刷新列表。

int wantedPosition = 10; // Whatever position you're looking for
int firstPosition = listView.getFirstVisiblePosition() - listView.getHeaderViewsCount();
int wantedChild = wantedPosition - firstPosition
if (wantedChild < 0 || wantedChild >= listView.getChildCount()) 
    // Wanted item isn't displayed
    return;

View wantedView = listView.getChildAt(wantedChild);

然后使用 WantedView 编辑你的背景

这个答案可以在here找到

【讨论】:

感谢 Mahmoud,但我不能打电话给 notifyDataSetChanged(),因为我无法承受重绘整个 ListView,因为我需要 ListView 保持在用户选择的相同位置:-((跨度> notifyDataSetChanged(且未失效)不会使您的列表滚动 Xaview 你是对的,但是会导致整个 ListView 可怕地闪烁 :-(( 这是因为你没有利用回收利用,即使提供的 convertView 不为空,你也总是夸大你的视图。你不应该再给它充气,而只是改变你的物品的特定内容【参考方案4】:

试试这个:

View v=lv.getAdapter().getView(index, null, lv);
View card =(View)v.findViewById(R.id.card);
card.setBackgroundResource(R.drawable.pressed_background_card);
card.invalidate();
v.invalidate();

这些功能会强制您的视图重新绘制自身,并且它们将再次呈现。 看invalidate()

【讨论】:

【参考方案5】:

我通常会这样做:

public static class EventDetailsRenderer 

    private TextView title;

    private TextView description;

    private Event item;

    public EventDetailsRenderer(View view) 
        extractFromView(view);
    

    private final void extractFromView(View view) 
        title = (TextView) view.findViewById(R.id.EventTitle);
        description = (TextView) view.findViewById(R.id.Description);
    

    public final void render() 
            render(item);
    

    public final void render(Event item) 
        this.item= item;
        title.setText(item.getTitle());
        description.setText(item.getDescription());
    


private class EventsAdapter
        extends ArrayAdapter<Event> 

    public EventsAdapter(Context context) 
        super(context, R.layout.list_node__event_details, 0);
    

    public void addAllItems(Event... services) 
        for (int i = 0; i < services.length; i++) 
            add(services[i]);
        
    

    @Override
    public View getView(int position, View convertView, ViewGroup parent) 
        Event event = getItem(position);
        EventDetailsRenderer eventRenderer;

        if (convertView != null && convertView.getTag() != null) 
            eventRenderer = (EventDetailsRenderer) convertView.getTag();
         else 
            convertView = getActivity().getLayoutInflater().inflate(R.layout.list_node__event_details, null);
            eventRenderer = new EventDetailsRenderer(convertView);
            convertView.setTag(eventRenderer);
        

        eventRenderer.render(event);
        return convertView;
    

注意:这个示例可能无法编译,我从我拥有的一些代码中粘贴了它,并删除了一些行以显示示例,但逻辑相同。

然后当你想渲染它时,只需从列表中获取孩子,迭代它们,检查渲染器是否包含你想要翻转的卡片并调用它的渲染方法......然后你渲染一个特定的项目列表而不影响其余项目。

让我知道这是否有效...

亚当。

【讨论】:

【参考方案6】:

用户EasyListViewAdapters库https://github.com/birajpatel/EasyListViewAdapters

特点

    比实现自己的适配器更容易(即处理 BaseAdaper#getView)。更容易提供多行支持。 库负责回收所有视图,确保性能 & 帮助您的列表视图平滑滚动。 更简洁的代码。 通过为不同的 RowViewSetter 类保留 不同的行类型使您的代码易于管理和重用。 无数据浏览,图书馆负责浏览数据 绘制视图或发生事件时的数据结构,以便 用户无需查找他们的数据即可采取行动。 只需传递正确的行类型库,即可自动映射您的 数据类型到行类型以呈现视图。可以通过以下方式创建行视图 使用 XML 或 Java(不限于 XML-Only 方法)。 可以注册加载更多回调来实现分页 支持到你的名单。 处理子viewclicks,也可以注册 孩子(出现在您的行内)查看点击事件。 所有这些视图都注册到 single OnClickListner 以便 当点击事件发生时,这种机制非常内存效率 您获得 clickedChildView、rowData、int eventId 作为回调的用户 参数。

【讨论】:

以上是关于ListView:如何从外部以编程方式访问 Item 的元素?的主要内容,如果未能解决你的问题,请参考以下文章

如何以编程方式选择 ListView 中的项目?

WPF ListView - 如何以编程方式添加项目?

如何以编程方式将 ListView 列标题属性 IsHitTestVisible 设置为 False?

以编程方式从 Windows Phone 8.1 XAML 中的 ListView 中的特定 ListViewItem 到达 TextBlock

如何以编程方式使 ListView 的列自动调整大小?

按钮按下以编程方式将新行添加到 ListView,如何?