Как обновить Android ListView с динамическими данными в режиме реального времени?
У меня есть данные загрузки фонового потока, которые я хочу отобразить в ListView Android. Данные изменения очень часто (т. е. 1-2 раза в секунду). Иногда количество строк в наборе данных также изменяется (но, конечно, не так часто, как данные в ячейках).
есть два способа обновить данные в ячейках, насколько я могу судить:
имейте фоновый поток уведомить поток пользовательского интерфейса, что новые данные готовы, и поток пользовательского интерфейса может затем вызовите BaseAdapter.notifyDataSetChanged(). Однако я прочитал в нескольких местах, что если этот метод вызывается часто, он будет медленным, потому что ListView должен реструктурировать все свои представления подвидов.
Если количество наборов данных не изменилось, я мог бы найти все видимые ячейки ListView, связанные с измененными данными, и обновить значения вручную без вызова notifyDataSetChanged(). Это, вероятно, сработает, но я думаю, что его к сожалению, мне приходится обновлять представления вручную, когда адаптер списка должен обрабатывать уведомления об обновлении и механизмы для меня, когда я уведомляю его. Этот метод также не будет работать, если количество наборов данных изменяется с течением времени (т. е. не только данные в каждой ячейке ListView меняются, но общее количество ячеек в ListView может расти или сжиматься на основе фонового потока, предоставляющего данные в реальном времени).
Я бы, конечно, оценил мысли от другие, которые реализовали этот сценарий, и как они оптимизировали простоту кода и, самое главное, производительность.
4 ответов
я экспериментировал с ListView
, и вы по существу должны обновить ListView
ячейки вручную без вызова notifyDataSetChanged()
Если у вас есть данные в реальном времени и вы хотите, чтобы ListView
для обновления с лучшей производительностью.
notifyDataSetChanged()
вызывает ListView
перестроить его полностью View
иерархия очень медленная, если вы часто ее вызываете (т. е. раз в секунду).
Примечание (2014). Это не применяется, если вы используете обычный современный ListView с Паттерн ViewHolder. Вы просто позвонить notifyDataSetChanged и это все. Это невероятно эффективно, так как Android знает только обновлять ячейки на экране.
я реализовал схему, где, если размер моего набора данных изменился с одного обновления на другое, я вызываю notifyDataSetChanged()
. Если размер набора данных оставался постоянным от одного обновления до следующего (например, количество ячеек в ListView
то же самое, что и раньше, когда требуется перерисовка данных), затем я повторяю над ListView.getFirstVisiblePosition()
: getLastVisiblePosition()
, и обновлять только видимые ячейки.
Я когда-то реализовал фильтр, такой как код beolow, используя notifyDataSetChanged()
и не было никаких проблем с этим.
Я также изменил представления списка на ходу вручную. Оба хорошо поработали. В некоторых случаях я предпочитаю изменять данные вручную, потому что это быстрее и потому что это не влияет на весь список.
в любом случае, представления создаются на ходу, когда они должны появиться на экране и удаляются, когда они покидают экран, поэтому, если вы измените данные, используемые для создания представления, если пользователь прокручивает ListView и представления выходят из экрана, теоретически представления будут созданы с новыми данными, как только они снова появятся на экране.
Я бы рекомендовал вам попробовать код ниже, чтобы понять, как это делает notifyDataSetChanged()
работа и решить, если это работает для вас.
public class ListText extends Activity {
private ListView lv1;
private Followed followedFriends[];
ListView lst;
EditText edt;
FollowedFilterableAdapter arrad;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
lv1=(ListView)findViewById(R.id.listView1);
edt = (EditText) findViewById(R.id.editText1);
followedFriends = new Followed[10];
followedFriends[0] = new Followed("Alan Walder", "0123456789", "1");
followedFriends[1] = new Followed("Alberto Levi", "123456789", "1");
followedFriends[2] = new Followed("David Rodan", "23456789", "1");
followedFriends[3] = new Followed("David Stern", "3456789", "1");
followedFriends[4] = new Followed("Elias Jawa", "456789", "1");
followedFriends[5] = new Followed("Elian Moreno", "56789", "1");
followedFriends[6] = new Followed("Jonathan Rodan", "6789", "1");
followedFriends[7] = new Followed("Klara Rodan", "789", "1");
followedFriends[8] = new Followed("Willy Rosales", "89", "1");
followedFriends[9] = new Followed("ZZZ ZZZ", "9", "1");
arrad = new FollowedFilterableAdapter(followedFriends);
lv1.setAdapter(arrad);
addTextChangeList();
}
private void addTextChangeList()
{
edt.addTextChangedListener(new TextWatcher()
{
public void onTextChanged( CharSequence arg0, int arg1, int arg2, int arg3)
{
// TODO Auto-generated method stub
}
public void beforeTextChanged( CharSequence arg0, int arg1, int arg2, int arg3)
{
// TODO Auto-generated method stub
}
public void afterTextChanged( Editable arg0)
{
// TODO Auto-generated method stub
ListText.this.arrad.getFilter().filter(arg0);
}
});
}
///////////////////////////////////Internal classes ////////////////////////
private class Followed
{
private String _name;
private String _id;
private boolean _followed;
private String _picToDelete = "http://images.wikia.com/tibia/en/images/7/72/Skeleton.gif";
private Followed(String name, String id, String followed)
{
this._name = name;
this._id = id;
this._followed = followed.equals("1");
}
public String toString(){return _name;}
public String getName(){return _name;}
public String getId(){return _id;}
public boolean idFollowed(){return _followed;}
public String getURL(){return _picToDelete;}
}
/////////////////////////////////////////Adapter//////////////////////////////////
private class FollowedFilterableAdapter extends BaseAdapter implements Filterable
{
/**
* Lock used to modify the content of {@link #mObjects}. Any write operation
* performed on the array should be synchronized on this lock. This lock is also
* used by the filter (see {@link #getFilter()} to make a synchronized copy of
* the original array of data.
*/
private final Object _Lock = new Object();
/*/**
* Indicates whether or not {@link #notifyDataSetChanged()} must be called whenever
* {@link #mObjects} is modified.
*/
//private boolean _NotifyOnChange = true;
private List<Followed> _Objects;
private ArrayList<Followed> _followedFriends;
private ArrayFilter _Filter;
public FollowedFilterableAdapter(Followed[] followedFriends)
{
_Objects = Arrays.asList(followedFriends);
}
public int getCount() {
return _Objects.size();
}
public Followed getItem(int position) {
return _Objects.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
int px = 2;
//Creating the CategoryRow that represents the row
LinearLayout lstItem = new LinearLayout(ListText.this);
lstItem.setLayoutParams(new ListView.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT,1));
lstItem.setOrientation(LinearLayout.HORIZONTAL);
//lstItem.setPadding(px,px,px,px);
lstItem.setGravity(Gravity.CENTER_VERTICAL);
/*<LinearLayout android:layout_width="fill_parent"
android:layout_height="wrap_content" android:orientation="horizontal"
android:padding="2dp" android:gravity="center_vertical">*/
//Adding the Image
ImageView icon = new ImageView(ListText.this);
icon.setLayoutParams(new LayoutParams(50,50));
icon.setImageResource(R.drawable.icon);
icon.setScaleType(ScaleType.CENTER_CROP);
//icon.setImage(tag.getId());
/*<ImageView android:layout_width="50dp" android:id="@+id/iconFollList"
android:src="@drawable/icon" android:layout_height="40dp"></ImageView>*/
//Adding the Linear Layout for the text
RelativeLayout lstTextx = new RelativeLayout(ListText.this);
lstTextx.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT,1));
lstTextx.setGravity(Gravity.CENTER_VERTICAL);
lstTextx.setPadding(5, px, px, px);
/*<LinearLayout android:layout_width="fill_parent"
android:layout_height="wrap_content" android:orientation="vertical"
android:padding="2dp" android:paddingLeft="5dp">*/
//Adding the Name of the person who commented
TextView txtCommenter = new TextView(ListText.this);
txtCommenter.setLayoutParams(
new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
txtCommenter.setTextSize(10);
txtCommenter.setTypeface(Typeface.create("Sans Serif", Typeface.BOLD));
txtCommenter.setText(_Objects.get(position).getName());
/*<TextView android:text="TextView" android:id="@+id/FollListCategory"
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>*/
ImageView btnFollowed = new ImageView(ListText.this);
RelativeLayout.LayoutParams params =
new RelativeLayout.LayoutParams(80,30);
params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
btnFollowed.setLayoutParams(params);
btnFollowed.setImageResource(R.drawable.icon);
btnFollowed.setScaleType(ScaleType.FIT_XY);
//Arming the View
lstItem.addView(icon, 0);
lstTextx.addView(txtCommenter, 0);
lstTextx.addView(btnFollowed,1);
lstItem.addView(lstTextx,1);
return lstItem;
}
/**
* {@inheritDoc}
*/
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
//_NotifyOnChange = true;
}
/*public void setNotifyOnChange(boolean notifyOnChange) {
_NotifyOnChange = notifyOnChange;
}*/
public Filter getFilter() {
if (_Filter == null) {
_Filter = new ArrayFilter();
}
return _Filter;
}
/////////////////////Second Level Internal classes //////////////////////////
private class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
if (_followedFriends == null) {
synchronized (_Lock) {
_followedFriends = new ArrayList<Followed>(_Objects);
}
}
if (prefix == null || prefix.length() == 0) {
synchronized (_Lock) {
ArrayList<Followed> list = new ArrayList<Followed>(_followedFriends);
results.values = list;
results.count = list.size();
}
} else {
String prefixString = prefix.toString().toLowerCase();
final ArrayList<Followed> values = _followedFriends;
final int count = values.size();
final ArrayList<Followed> newValues = new ArrayList<Followed>(count);
for (int i = 0; i < count; i++) {
final Followed value = values.get(i);
final String valueText = value.toString().toLowerCase();
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString)) {
newValues.add(value);
} else {
final String[] words = valueText.split(" ");
final int wordCount = words.length;
for (int k = 0; k < wordCount; k++) {
if (words[k].startsWith(prefixString)) {
newValues.add(value);
break;
}
}
}
}
results.values = newValues;
results.count = newValues.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
//no inspection unchecked
_Objects = (List<Followed>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
}
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<EditText android:text="" android:layout_height="wrap_content"
android:id="@+id/editText1" android:layout_width="match_parent"></EditText>
<ListView android:id="@+id/listView1" android:layout_height="fill_parent"
android:layout_width="match_parent" android:layout_weight="1"></ListView>
</LinearLayout>
надеюсь, что это помогает.
возможно, вы можете установить обработчик в потоке пользовательского интерфейса. Вам нужно создать класс, который реализует Runnable. Передайте ArrayList этому классу. В методе run () создайте адаптер с ArrayList в качестве параметра, затем выполните setAapter в ListView. Вот и все. Вы закончили. Чтобы запустить обработчик: только для этого из вашего рабочего потока: обработчик.Пост(Нью-MyUpdateToUI() ); Вот и все. Я надеюсь, что это достаточно эффективно для вас?
Я столкнулся с очень похожей ситуацией, когда я сохранил textviews представления списка в Hashmap, где каждый textview имел тег для его идентификации. Когда потоковые данные прибыли, все, что я сделал, это разместил runnable на textview после поиска правильного и получил их обновление в кратчайшие сроки. Однако, хотя обновления были мгновенными, пользовательский интерфейс столкнулся с перетаскиванием и пошел вяло, поскольку я обновлял около 30 textviews в секунду. У меня был Listview в фрагменте внутри ViewPager, и это казалось UI поток был забит и заблокирован, когда потоковая передача была включена. Поэтому я настоятельно рекомендую не обновлять вручную Textviews в Listview в отношении пользовательского интерфейса и производительности потока UI