Android ListView không làm mới sau khitifyDataSetChanged


116

Mã ListFragment của tôi

public class ItemFragment extends ListFragment {

    private DatabaseHandler dbHelper;
    private static final String TITLE = "Items";
    private static final String LOG_TAG = "debugger";
    private ItemAdapter adapter;
    private List<Item> items;


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.item_fragment_list, container, false);        
        return view;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.setHasOptionsMenu(true);
        super.onCreate(savedInstanceState);
        getActivity().setTitle(TITLE);
        dbHelper = new DatabaseHandler(getActivity());
        items = dbHelper.getItems(); 
        adapter = new ItemAdapter(getActivity().getApplicationContext(), items);
        this.setListAdapter(adapter);

    }



    @Override
    public void onResume() {
        super.onResume();
        items.clear();
        items = dbHelper.getItems(); //reload the items from database
        adapter.notifyDataSetChanged();
    }

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        if(dbHelper != null) { //item is edited
            Item item = (Item) this.getListAdapter().getItem(position);
            Intent intent = new Intent(getActivity(), AddItemActivity.class);
            intent.putExtra(IntentConstants.ITEM, item);
            startActivity(intent);
        }
    }
}

ListView của tôi

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <ListView
        android:id="@android:id/list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

Nhưng điều này không làm mới ListView. Ngay cả sau khi khởi động lại ứng dụng, các mục cập nhật sẽ không được hiển thị. ItemAdapterMở rộng của tôiBaseAdapter

public class ItemAdapter extends BaseAdapter{

    private LayoutInflater inflater;
    private List<Item> items;
    private Context context;

    public ProjectListItemAdapter(Context context, List<Item> items) {
        super();
        inflater = LayoutInflater.from(context);
        this.context = context;
        this.items = items;

    }

    @Override
    public int getCount() {
        return items.size();
    }

    @Override
    public Object getItem(int position) {
        return items.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ItemViewHolder holder = null;
        if(convertView == null) {
            holder = new ItemViewHolder();
            convertView = inflater.inflate(R.layout.list_item, parent,false);
            holder.itemName = (TextView) convertView.findViewById(R.id.topText);
            holder.itemLocation = (TextView) convertView.findViewById(R.id.bottomText);
            convertView.setTag(holder);
        } else {
            holder = (ItemViewHolder) convertView.getTag();
        }
        holder.itemName.setText("Name: " + items.get(position).getName());
        holder.itemLocation.setText("Location: " + items.get(position).getLocation());
        if(position % 2 == 0) {                                                                                 
            convertView.setBackgroundColor(context.getResources().getColor(R.color.evenRowColor));
        } else {    
            convertView.setBackgroundColor(context.getResources().getColor(R.color.oddRowColor));
        }
        return convertView;
    }

    private static class ItemViewHolder {
        TextView itemName;
        TextView itemLocation;
    }
}

Ai đó có thể giúp đỡ xin vui lòng?


2
Bạn đã kiểm tra xem hoạt động cơ sở dữ liệu có hoạt động bình thường không? Bộ chuyển đổi trông như thế nào? Ngoài ra, nếu bạn tạo một đối tượng trên cho adaptertham chiếu tại sao bạn kiểm tra nó cho rỗng một dòng bên dưới?
Luksprog

Mã không ném ra ngoại lệ và tôi đã kiểm tra bằng cách sử dụng gỡ lỗi. Tất cả các phương thức được thực thi mà không có lỗi. Vâng, đó là một sai lầm ngớ ngẩn.
Coder

Câu trả lời:


229

Nhìn của bạn onResume phương pháp trong ItemFragment:

@Override
public void onResume() {
    super.onResume();
    items.clear();
    items = dbHelper.getItems(); // reload the items from database
    adapter.notifyDataSetChanged();
}

những gì bạn vừa cập nhật trước khi gọi notifyDataSetChanged() không phải là trường của bộ điều hợp private List<Item> items;mà là trường được khai báo giống hệt nhau của phân đoạn. Bộ điều hợp vẫn lưu trữ một tham chiếu đến danh sách các mục bạn đã chuyển khi tạo bộ điều hợp (ví dụ: trong onCreate của phân đoạn). Cách ngắn nhất (về số lượng thay đổi) nhưng không đơn giản để làm cho mã của bạn hoạt động như bạn mong đợi chỉ đơn giản là thay thế dòng:

    items = dbHelper.getItems(); // reload the items from database

với

    items.addAll(dbHelper.getItems()); // reload the items from database

Một giải pháp thanh lịch hơn:

1) loại bỏ các mục private List<Item> items; khỏi ItemFragment- chúng tôi chỉ cần giữ tham chiếu đến chúng trong bộ điều hợp

2) thay đổi onCreate thành:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    super.setHasOptionsMenu(true);
    getActivity().setTitle(TITLE);
    dbHelper = new DatabaseHandler(getActivity());
    adapter = new ItemAdapter(getActivity(), dbHelper.getItems());
    setListAdapter(adapter);
}

3) thêm phương thức trong ItemAdapter:

public void swapItems(List<Item> items) {
    this.items = items;
    notifyDataSetChanged();
}

4) thay đổi onResume của bạn thành:

@Override
public void onResume() {
    super.onResume();
    adapter.swapItems(dbHelper.getItems());
}

Sẽ không sạch sẽ hơn nếu chuyển toàn bộ thứ dbHelper vào Bộ điều hợp? Vì vậy, bạn sẽ chỉ gọi adapter.swapItems();và bộ điều hợp sẽ thực hiện dbHelper.getItems()công việc. Nhưng dù sao, cảm ơn vì câu trả lời :)
Ansgar

7
Tại sao bạn phải xóa () và thêm lại các mục? Đó không phải là chính xác mục đích của notifyDataSetChanged()?
Phil Ryan

1
@tomsaz bạn có thể giúp tôi với điều này stackoverflow.com/questions/28148618/...

1
Cảm ơn @tomsaz Gawel, swapItems của bạn thực sự giúp ích cho tôi rất nhiều, tôi không biết tại sao adapter.notifydatasetchanged của tôi không hoạt động, vì "danh sách" tôi đang vượt qua cũng được cập nhật, ngay cả khi tôi đã kiểm tra bằng cách in nhật ký, bạn có thể giải thích giúp tôi điều này không khái niệm
Kimmi Dhingra

1
Câu trả lời này là chính xác. Vấn đề là Danh sách Mảng Mục của ADAPTOR không được cập nhật. Điều này có nghĩa là bạn có thể gọi thông báo đã thay đổi cho đến khi mặt bạn tái xanh mà không có bất kỳ ảnh hưởng nào. Bộ điều hợp đang cập nhật tập dữ liệu của bạn với cùng một tập dữ liệu nên KHÔNG có thay đổi nào. Một giải pháp thay thế khác được đăng trong câu trả lời này có thể rõ ràng hơn là: adapter.items = items; adapter.notifyDataSetChanged ();
Ray Li

23

Bạn đang chỉ định các mục được tải lại cho các mục biến toàn cục trong onResume(), nhưng điều này sẽ không phản ánh trongItemAdapter lớp, bởi vì nó có biến phiên bản riêng được gọi là 'items'.

Để làm mới ListView, hãy thêm refresh () trong ItemAdapterlớp chấp nhận dữ liệu danh sách tức là các mục

class ItemAdapter
{
    .....

    public void refresh(List<Item> items)
    {
        this.items = items;
        notifyDataSetChanged();
    } 
}

cập nhật onResume()bằng mã sau

@Override
public void onResume()
{
    super.onResume();
    items.clear();
    items = dbHelper.getItems(); //reload the items from database
    **adapter.refresh(items);**
}

1
Điều này hoàn toàn đúng. Phương thức khởi tạo của bộ điều hợp dự kiến ​​sẽ được chuyển các mục, nhưng anh ta chỉ cập nhật trường của lớp bên ngoài.
LuxuryMode

Chào Santhosh. Bạn có thể có một cái nhìn tại một vấn đề tương tự: stackoverflow.com/questions/35850715/...

8

Trong onResume () thay đổi dòng này

items = dbHelper.getItems(); //reload the items from database

đến

items.addAll(dbHelper.getItems()); //reload the items from database

Vấn đề là bạn không bao giờ nói với bộ điều hợp của mình về danh sách các mục mới. Nếu bạn không muốn chuyển một danh sách mới cho bộ điều hợp của mình (có vẻ như bạn không), thì chỉ cần sử dụng items.addAllsau của bạn clear(). Điều này sẽ đảm bảo bạn đang sửa đổi cùng một danh sách mà bộ điều hợp có tham chiếu đến.


Thật khó hiểu khi adapter.clear()không buộc bộ điều hợp nhận ra rằng chế độ xem nên làm mới, nhưng adapter.add()hoặc adapter.addAll()làm. Cảm ơn vì câu trả lời!
w3bshark

Lưu ý rằng tôi đang sử dụng items.addAll()chứ không phải adapter.addAll (). Điều duy nhất cho phép bộ điều hợp phản ứng với các thay đổi là notifyDataSetChanged. Lý do bộ điều hợp thấy các thay đổi là itemsdanh sách này giống với danh sách mà bộ điều hợp đang sử dụng.
Justin Breitfeller

4

Nếu bộ điều hợp đã được đặt, việc đặt lại bộ điều hợp sẽ không làm mới chế độ xem danh sách. Thay vào đó, trước tiên hãy kiểm tra xem listview có bộ điều hợp hay không và sau đó gọi phương thức thích hợp.

Tôi nghĩ rằng không phải là một ý kiến ​​hay khi tạo một phiên bản mới của bộ điều hợp trong khi thiết lập chế độ xem danh sách. Thay vào đó, hãy tạo một đối tượng.

BuildingAdapter adapter = new BuildingAdapter(context);

    if(getListView().getAdapter() == null){ //Adapter not set yet.
     setListAdapter(adapter);
    }
    else{ //Already has an adapter
    adapter.notifyDataSetChanged();
    }

Ngoài ra, bạn có thể thử chạy danh sách làm mới trên Chuỗi giao diện người dùng:

activity.runOnUiThread(new Runnable() {         
        public void run() {
              //do your modifications here

              // for example    
              adapter.add(new Object());
              adapter.notifyDataSetChanged()  
        }
});

Tôi không chắc chắn về cách triển khai chuỗi giao diện người dùng. Hoạt động chính của tôi có 3 phân đoạn (tab) và mã trong câu hỏi có liên quan đến một trong các phân đoạn chứa chế độ xem danh sách. Lý do chuyển các mục đến ItemAdapterlà tôi muốn tô màu các hàng và chế độ xem danh sách hiển thị nhiều mục dữ liệu. Tôi đã đăng mã cho bộ điều hợp.
Coder

Bạn cần đặt mã điền danh sách của mình vào mã mẫu của tôi bằng cách sử dụng "this". thay vì "hoạt động"
AlexGo

Trong một số trường hợp, nó không được cập nhật khi bạn chạy InformDataSetChanged () trong luồng khác, Vì vậy, giải pháp trên là phù hợp cho một số trường hợp.
Ayman Al-Absi

4

Nếu bạn muốn cập nhật listview của bạn không quan trọng nếu bạn muốn làm điều đó trên onResume(), onCreate()hoặc trong một số chức năng khác, điều đầu tiên mà bạn phải nhận ra là bạn sẽ không cần phải tạo một thể hiện mới của bộ chuyển đổi, chỉ cần populate lại các mảng với dữ liệu của bạn. Ý tưởng tương tự như thế này:

private ArrayList<String> titles;
private MyListAdapter adapter;
private ListView myListView;

@Override
public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_activity);

    myListView = (ListView) findViewById(R.id.my_list);

    titles = new ArrayList<String>()

    for(int i =0; i<20;i++){
        titles.add("Title "+i);
    }

    adapter = new MyListAdapter(this, titles);
    myListView.setAdapter(adapter);
}


@Override
public void onResume(){
    super.onResume();
    // first clear the items and populate the new items
    titles.clear();
    for(int i =0; i<20;i++){
        titles.add("New Title "+i);
    }
    adapter.notifySetDataChanged();
}

Vì vậy, tùy thuộc vào câu trả lời đó, bạn nên sử dụng tương tự List<Item>trong của bạn Fragment. Trong lần khởi tạo bộ điều hợp đầu tiên, bạn điền vào danh sách của mình với các mục và đặt bộ điều hợp thành dạng xem danh sách của bạn. Sau đó, trong mỗi thay đổi trong các mục của bạn, bạn phải xóa các giá trị khỏi chính List<Item> itemsvà điền lại nó bằng các mục mới và gọi notifySetDataChanged();.

Đó là cách nó hoạt động : ).


Cảm ơn vi đa trả lơi. Tôi đã thực hiện các thay đổi như bạn đã đề cập. Tôi đã đăng mã của mình. Nó vẫn không hoạt động. Bây giờ nó thậm chí không hiển thị chế độ xem danh sách khi các mục mới được thêm vào.
Coder

Tôi đã thay đổi mã. Điều kỳ lạ cần quan sát là mục đó không được cập nhật trong DB
Coder

Chủ đề này dành cho cơ sở dữ liệu stackoverflow.com/questions/14555332/…
Coder

3

Một câu trả lời từ AlexGo đã giúp tôi:

getActivity().runOnUiThread(new Runnable() {
        @Override
        public void run() {
         messages.add(m);
         adapter.notifyDataSetChanged();
         getListView().setSelection(messages.size()-1);
        }
});

Cập nhật danh sách đã hoạt động với tôi trước đây khi bản cập nhật được kích hoạt từ sự kiện GUI, do đó nằm trong chuỗi giao diện người dùng.

Tuy nhiên, khi tôi cập nhật danh sách từ một sự kiện / chuỗi khác - tức là một cuộc gọi từ bên ngoài ứng dụng, bản cập nhật sẽ không nằm trong chuỗi giao diện người dùng và nó đã bỏ qua lệnh gọi getListView. Gọi bản cập nhật với runOnUiThread như trên đã thực hiện thủ thuật cho tôi. Cảm ơn!!


3

Thử cái này

@Override
public void onResume() {
super.onResume();
items.clear();
items = dbHelper.getItems(); //reload the items from database
adapter = new ItemAdapter(getActivity(), items);//reload the items from database
adapter.notifyDataSetChanged();
}

3
adpter.notifyDataSetInvalidated();

Hãy thử điều này trong onPause()phương pháp của lớp Hoạt động.


1
adapter.setNotifyDataChanged()

nên làm thủ thuật.


3
đặt câu hỏi ở đâu ở đây ??
swiftBoy

1

Nếu danh sách của bạn được chứa trong chính Bộ điều hợp, thì việc gọi hàm cập nhật danh sách cũng sẽ gọi notifyDataSetChanged().

Chạy chức năng này từ Chuỗi giao diện người dùng đã giải quyết được vấn đề cho tôi:

Các refresh()chức năng bên trong Adaptor

public void refresh(){
    //manipulate list
    notifyDataSetChanged();
}

Sau đó, lần lượt chạy chức năng này từ Chuỗi giao diện người dùng

getActivity().runOnUiThread(new Runnable() { 
    @Override
    public void run() {
          adapter.refresh()  
    }
});

Điều này thực sự đã tạo ra sự khác biệt đối với tôi vì bản cập nhật đến qua mạng thông qua một chuỗi khác.
Chuck

0

Hãy thử như thế này:

this.notifyDataSetChanged();

thay vì:

adapter.notifyDataSetChanged();

Bạn cần phải notifyDataSetChanged()đến ListViewkhông đến lớp adapter.


tất nhiên nó sẽ không, cơ hội duy nhất nếu hoạt động được mở rộng bởi một listview
cmario
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.