ListView에서 단일 행을 어떻게 업데이트합니까?
내가 가지고 ListView
있는 디스플레이 뉴스 항목을. 여기에는 이미지, 제목 및 일부 텍스트가 포함됩니다. 이미지는 별도의 스레드 (대기열 및 모두 포함)에로드되고 이미지를 다운로드 할 때 notifyDataSetChanged()
목록 어댑터를 호출 하여 이미지를 업데이트합니다. 이것은 작동하지만 모든 보이는 항목을 호출 하기 getView()
때문에 너무 자주 notifyDataSetChanged()
호출 getView()
됩니다. 목록에서 단일 항목 만 업데이트하고 싶습니다. 어떻게해야합니까?
현재 접근 방식의 문제점은 다음과 같습니다.
- 스크롤이 느립니다
- 이미지에 페이드 인 애니메이션이 있습니다. 목록의 단일 새 이미지가로드 될 때마다 발생합니다.
정보 미셸 덕분에 답을 찾았습니다. 를 사용하여 올바른 뷰를 얻을 수 있습니다 View#getChildAt(int index)
. 캐치는 첫 번째 보이는 항목부터 계산을 시작한다는 것입니다. 실제로 보이는 항목 만 얻을 수 있습니다. 당신은 이것을 해결합니다 ListView#getFirstVisiblePosition()
.
예:
private void updateView(int index){
View v = yourListView.getChildAt(index -
yourListView.getFirstVisiblePosition());
if(v == null)
return;
TextView someText = (TextView) v.findViewById(R.id.sometextview);
someText.setText("Hi! I updated you manually!");
}
이 질문은 Google I / O 2010에서 요청되었으며 여기에서 볼 수 있습니다.
기본적으로 어떤 로맹 가이 설명하는 것은 호출하는 것입니다 getChildAt(int)
온 ListView
뷰와 (내가 생각하는) 호출을받을 getFirstVisiblePosition()
위치와 지수 사이의 상관 관계를 알아.
Romain은 또한 Shelves 라는 프로젝트를 예로 들어 설명합니다. 그가 방법을 의미한다고 생각 ShelvesActivity.updateBookCovers()
하지만의 전화를 찾을 수 없습니다 getFirstVisiblePosition()
.
멋진 업데이트가옵니다 :
RecyclerView는 가까운 시일 내에이 문제를 해결합니다. http://www.grokkingandroid.com/first-glance-androids-recyclerview/ 에서 지적했듯이 다음과 같이 변경 사항을 정확하게 지정하는 메소드를 호출 할 수 있습니다.
void notifyItemInserted(int position)
void notifyItemRemoved(int position)
void notifyItemChanged(int position)
또한 모든 사람들은 RecyclerView를 기반으로 한 새로운 뷰를 사용 하기 를 원할 것입니다. 미래는 굉장해 보인다! :-)
이것이 내가 한 방법입니다.
항목 (행)에는 고유 한 ID가 있어야 나중에 업데이트 할 수 있습니다. 목록이 어댑터에서보기를 가져올 때 모든보기의 태그를 설정하십시오. (기본 태그가 다른 곳에 사용 된 경우 키 태그를 사용할 수도 있습니다)
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
View view = super.getView(position, convertView, parent);
view.setTag(getItemId(position));
return view;
}
업데이트의 경우 목록의 모든 요소를 확인하고 주어진 ID가있는보기가 있으면 표시되어 업데이트를 수행합니다.
private void update(long id)
{
int c = list.getChildCount();
for (int i = 0; i < c; i++)
{
View view = list.getChildAt(i);
if ((Long)view.getTag() == id)
{
// update view
}
}
}
실제로 다른 방법보다 쉽고 위치가 아닌 ID를 처리 할 때 더 좋습니다! 또한 보이는 항목에 대해서는 update를 호출해야합니다.
이 모델 클래스 객체와 같이 모델 클래스를 전역으로 가져옵니다.
SampleModel golbalmodel=new SchedulerModel();
그리고 그것을 글로벌로 초기화하십시오
모델을 뷰로 초기화하여 뷰의 현재 행을 가져옵니다.
SampleModel data = (SchedulerModel) sampleList.get(position);
golbalmodel=data;
변경된 값을 전역 모델 객체 메소드로 설정하고 notifyDataSetChanged를 추가하십시오.
golbalmodel.setStartandenddate(changedate);
notifyDataSetChanged();
대답은 명확하고 정확 CursorAdapter
합니다. 여기 에 사례 에 대한 아이디어를 추가 하겠습니다.
하위 클래스 CursorAdapter
(또는 ResourceCursorAdapter
, 또는 SimpleCursorAdapter
) 인 경우 구현 ViewBinder
하거나 재정의 bindView()
하고 newView()
메소드 를 가져 오는 경우 인수에서 현재 목록 항목 색인을받지 못합니다. 따라서 일부 데이터가 도착하고 관련 표시 목록 항목을 업데이트하려는 경우 해당 인덱스를 어떻게 알 수 있습니까?
내 해결 방법은 다음과 같습니다.
- 작성된 모든 목록 항목보기의 목록을 유지하고이 목록에 항목을 추가하십시오.
newView()
- 데이터가 도착하면 데이터를 반복하고 업데이트해야하는 데이터를 확인
notifyDatasetChanged()
합니다.
뷰 재활용으로 인해 저장 해야하는 뷰 참조 수와 반복은 화면에 표시되는 목록 항목 수와 거의 같습니다.
int wantedPosition = 25; // Whatever position you're looking for
int firstPosition = linearLayoutManager.findFirstVisibleItemPosition(); // This is the same as child #0
int wantedChild = wantedPosition - firstPosition;
if (wantedChild < 0 || wantedChild >= linearLayoutManager.getChildCount()) {
Log.w(TAG, "Unable to get view for desired position, because it's not being displayed on screen.");
return;
}
View wantedView = linearLayoutManager.getChildAt(wantedChild);
mlayoutOver =(LinearLayout)wantedView.findViewById(R.id.layout_over);
mlayoutPopup = (LinearLayout)wantedView.findViewById(R.id.layout_popup);
mlayoutOver.setVisibility(View.INVISIBLE);
mlayoutPopup.setVisibility(View.VISIBLE);
RecycleView의 경우이 코드를 사용하십시오
Erik을 제공하는 코드를 사용했지만 훌륭하게 작동했지만 내 목록보기에 복잡한 사용자 정의 어댑터가 있으며 UI를 업데이트하는 코드의 두 번 구현에 직면했습니다. 어댑터 getView 메소드에서 새보기를 얻으려고했습니다 (목록보기 데이터를 보유한 배열 목록이 이미 업데이트 / 변경되었습니다).
View cell = lvOptim.getChildAt(index - lvOptim.getFirstVisiblePosition());
if(cell!=null){
cell = adapter.getView(index, cell, lvOptim); //public View getView(final int position, View convertView, ViewGroup parent)
cell.startAnimation(animationLeftIn());
}
잘 작동하지만 이것이 좋은 방법인지 모르겠습니다. 따라서 목록 항목을 두 번 업데이트하는 코드를 구현할 필요가 없습니다.
정확히 나는 이것을 사용했다.
private void updateSetTopState(int index) {
View v = listview.getChildAt(index -
listview.getFirstVisiblePosition()+listview.getHeaderViewsCount());
if(v == null)
return;
TextView aa = (TextView) v.findViewById(R.id.aa);
aa.setVisibility(View.VISIBLE);
}
RecyclyerView 메서드 void 와 같은 다른 솔루션을 구성하여 다음과 같이 CustomBaseAdapter 클래스를 notifyItemChanged(int position)
만듭니다 .
public abstract class CustomBaseAdapter implements ListAdapter, SpinnerAdapter {
private final CustomDataSetObservable mDataSetObservable = new CustomDataSetObservable();
public boolean hasStableIds() {
return false;
}
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
public void notifyItemChanged(int position) {
mDataSetObservable.notifyItemChanged(position);
}
public void notifyDataSetInvalidated() {
mDataSetObservable.notifyInvalidated();
}
public boolean areAllItemsEnabled() {
return true;
}
public boolean isEnabled(int position) {
return true;
}
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return getView(position, convertView, parent);
}
public int getItemViewType(int position) {
return 0;
}
public int getViewTypeCount() {
return 1;
}
public boolean isEmpty() {
return getCount() == 0;
} {
}
}
다음 과 같이 CustomAdapterClass의 변수에 대해 CustomDataSetObservable 클래스 를 작성하는 것을 잊지 마십시오 .mDataSetObservable
public class CustomDataSetObservable extends Observable<DataSetObserver> {
public void notifyChanged() {
synchronized(mObservers) {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
public void notifyInvalidated() {
synchronized (mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onInvalidated();
}
}
}
public void notifyItemChanged(int position) {
synchronized(mObservers) {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
mObservers.get(position).onChanged();
}
}
}
on class CustomBaseAdapter there is a method notifyItemChanged(int position)
, and you can call that method when you want update a row wherever you want (from button click or anywhere you want call that method). And voila!, your single row will update instantly..
My solution: If it is correct*, update the data and viewable items without re-drawing the whole list. Else notifyDataSetChanged.
Correct - oldData size == new data size, and old data IDs and their order == new data IDs and order
How:
/**
* A View can only be used (visible) once. This class creates a map from int (position) to view, where the mapping
* is one-to-one and on.
*
*/
private static class UniqueValueSparseArray extends SparseArray<View> {
private final HashMap<View,Integer> m_valueToKey = new HashMap<View,Integer>();
@Override
public void put(int key, View value) {
final Integer previousKey = m_valueToKey.put(value,key);
if(null != previousKey) {
remove(previousKey);//re-mapping
}
super.put(key, value);
}
}
@Override
public void setData(final List<? extends DBObject> data) {
// TODO Implement 'smarter' logic, for replacing just part of the data?
if (data == m_data) return;
List<? extends DBObject> oldData = m_data;
m_data = null == data ? Collections.EMPTY_LIST : data;
if (!updateExistingViews(oldData, data)) notifyDataSetChanged();
else if (DEBUG) Log.d(TAG, "Updated without notifyDataSetChanged");
}
/**
* See if we can update the data within existing layout, without re-drawing the list.
* @param oldData
* @param newData
* @return
*/
private boolean updateExistingViews(List<? extends DBObject> oldData, List<? extends DBObject> newData) {
/**
* Iterate over new data, compare to old. If IDs out of sync, stop and return false. Else - update visible
* items.
*/
final int oldDataSize = oldData.size();
if (oldDataSize != newData.size()) return false;
DBObject newObj;
int nVisibleViews = m_visibleViews.size();
if(nVisibleViews == 0) return false;
for (int position = 0; nVisibleViews > 0 && position < oldDataSize; position++) {
newObj = newData.get(position);
if (oldData.get(position).getId() != newObj.getId()) return false;
// iterate over visible objects and see if this ID is there.
final View view = m_visibleViews.get(position);
if (null != view) {
// this position has a visible view, let's update it!
bindView(position, view, false);
nVisibleViews--;
}
}
return true;
}
and of course:
@Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
final View result = createViewFromResource(position, convertView, parent);
m_visibleViews.put(position, result);
return result;
}
Ignore the last param to bindView (I use it to determine whether or not I need to recycle bitmaps for ImageDrawable).
As mentioned above, the total number of 'visible' views is roughly the amount that fits on the screen (ignoring orientation changes etc), so no biggie memory-wise.
In addition to this solution (https://stackoverflow.com/a/3727813/5218712) just want to add that it should work only if listView.getChildCount() == yourDataList.size();
There could be additional view inside ListView.
Example of how the child elements are populated:
참고URL : https://stackoverflow.com/questions/3724874/how-can-i-update-a-single-row-in-a-listview
'IT' 카테고리의 다른 글
닫을 때 대화 상자를 완전히 제거하는 방법 (0) | 2020.07.01 |
---|---|
열 조합에 고유 제한 조건을 추가하기위한 마이그레이션 (0) | 2020.07.01 |
Numpy에서 벡터의 크기를 어떻게 얻습니까? (0) | 2020.07.01 |
TortoiseSVN을 사용하여 트렁크에서 분기로 또는 그 반대로 변경 사항을 어떻게 병합합니까? (0) | 2020.07.01 |
모든 비트를 true로 설정하기 위해 -1을 사용하는 것이 안전합니까? (0) | 2020.07.01 |