刷新单行

在获得 ListView的单行数据之后,我想更新该单行。

目前我使用的是 notifyDataSetChanged();,但这使得 View的反应非常缓慢。还有其他的解决方案吗?

62925 次浏览

一种选择是直接操作 ListView。首先检查更新的行的索引是否在 getFirstVisiblePosition()getLastVisiblePosition()之间,这两个位置给出适配器中在屏幕上可见的第一个和最后一个位置。然后你可以得到一行 ViewgetChildAt(int index)并改变它。

下面的代码对我很有用。请注意,当调用 GetChild ()时,必须偏移列表中的第一个项,因为它是该项的相对值。

int iFirst = getFirstVisiblePosition();
int iLast = getLastVisiblePosition();


if ( indexToChange >= numberOfRowsInSection() ) {
Log.i( "MyApp", "Invalid index. Row Count = " + numberOfRowsInSection() );
}
else {
if ( ( index >= iFirst ) && ( index <= iLast ) ) {
// get the view at the position being updated - need to adjust index based on first in the list
View vw = getChildAt( sysvar_index - iFirst );
if ( null != vw ) {
// get the text view for the view
TextView tv = (TextView) vw.findViewById(com.android.myapp.R.id.scrollingListRowTextView );
if ( tv != null ) {
// update the text, invalidation seems to be automatic
tv.setText( "Item = " + myAppGetItem( index ) + ". Index = " + index + ". First = " + iFirst + ". Last = " + iLast );
}
}
}
}

作为 Romain Guy 解释了一下Google I/O 会话期间,在列表视图中只更新一个视图的最有效方法如下所示(此方法更新整个视图数据) :

ListView list = getListView();
int start = list.getFirstVisiblePosition();
for(int i=start, j=list.getLastVisiblePosition();i<=j;i++)
if(target==list.getItemAtPosition(i)){
View view = list.getChildAt(i-start);
list.getAdapter().getView(i, view, list);
break;
}

假设 target是适配器的一个项目。

这段代码检索 ListView,然后浏览当前显示的视图,将您正在查找的 target项与每个显示的视图项进行比较,如果您的目标是其中之一,则获取封闭视图并在该视图上执行适配器 getView以刷新显示。

值得注意的是,invalidate并不像一些人期望的那样工作,也不会像 getView 那样刷新视图,因此 notifyDataSetChanged将重新构建整个列表,并最终为每个显示的项目调用 getview,而且 invalidateViews也会影响一大堆。

最后一点,如果只需要更改行视图的子视图,而不是像 getView那样更改整个行,那么还可以获得额外的性能。在这种情况下,以下代码可以替换 list.getAdapter().getView(i, view, list);(更改 TextView文本的示例) :

((TextView)view.findViewById(R.id.myid)).setText("some new text");

用我们信任的密码。

这个简单的方法对我来说很有效,你只需要知道位置索引就可以得到视图:

// mListView is an instance variable


private void updateItemAtPosition(int position) {
int visiblePosition = mListView.getFirstVisiblePosition();
View view = mListView.getChildAt(position - visiblePosition);
mListView.getAdapter().getView(position, view, mListView);
}

如果它适合您的用例,那么还有另一件更有效的事情可以做。

如果您正在改变状态,并且可以以某种方式调用适当的(通过知道位置) mListView.getAdapter().getView(),那么它将是最有效的。

我可以通过在 ListAdapter.getView()类中创建一个匿名内部类来演示一种非常简单的方法。在这个示例中,我有一个显示文本“ new”的 TextView,当单击列表项时,该视图被设置为 GONE:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
// assign the view we are converting to a local variable
View view = convertView;


Object quotation = getItem(position);
// first check to see if the view is null. if so, we have to inflate it.
if (view == null)
view = mInflater.inflate(R.layout.list_item_quotation, parent, false);


final TextView newTextView = (TextView) view.findViewById(R.id.newTextView);


view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mCallbacks != null)
mCallbacks.onItemSelected(quotation.id);
if (!quotation.isRead()) {
servicesSingleton.setQuotationStatusReadRequest(quotation.id);
quotation.setStatusRead();
newTextView.setVisibility(View.GONE);
}
}
});


if(quotation.isRead())
newTextView.setVisibility(View.GONE);
else
newTextView.setVisibility(View.VISIBLE);


return view;
}

框架自动使用正确的 position,在调用 getView之前,您确实需要担心如何再次获取它。

顺便问一句,有人考虑过覆盖方法的“查看视图”吗?

   public void onItemClick(AdapterView<?> parent, View view, int position, long id) {


//Update the selected item
((TextView)view.findViewById(R.id.cardText2)).setText("done!");


}

对我来说,下面这句话起作用了:

Set (position,new ModelClass (value)) ;

NotifyDataSetChanged () ;