配置更改后的notifyDataSetChanged

时间:2013-09-12 19:56:31

标签: android android-adapter

我在更改设备配置(更改语言,方向等)后进行了一些测试,我注意到在此之后,方法“notifyDataSetChanged()”无效。

动作示例:

我每次执行删除,保存等操作时都会调用updateList()。用户单击删除按钮,会显示DialogFragment,“您确定要删除吗?”,当我更改方向时,或语言,或设备的任何配置,然后在对话框上单击“是”,数据将被删除,但列表不会更新。我需要退出活动,然后回去查看更改。

BookAdapter:

public void updateList(ArrayList<Book> books) {
     bookList = books;
     notifyDataSetChanged();
}

如何在配置更改后使其工作?

编辑:

BookAdapter构造函数:

public BookAdapter(Context c, ArrayList<Book> books) {
    context = c;
    bookList = books
    bookDAO = BookDAO.getInstance(context);
}

BookFragment:

public class BookFragment extends Fragment {

    private BookDAO bookDAO;

    private BookAdapter bookAdapter;

    private ListView listBook;

    private View view;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        bookDAO = bookDAO.getInstance(getActivity());

        view = inflater.inflate(R.layout.book_tab, container, false);

        ArrayList<Book> listBook = null;

        try {
            llistBook = bookDAO.getAll();
        } catch (Exception e) {
            Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
            return view;
        }

        bookAdapter = new BookAdapter(getActivity(), listBook);
        listBook = (ListView)view.findViewById(R.id.listBook);
        listBook.setAdapter(bookAdapter);

        return view;

    }

}

6 个答案:

答案 0 :(得分:6)

您可以尝试将BookAdapter实施为Singleton,以确认您没有从过时的引用中调用updateList(..)

您需要进行的更改:

// I am assuming that you are using a BaseAdapter because
// BookAdapter's constructor that you provided in the code above
// does not contain a call to super(....)
public class BookAdapter extends BaseAdapter {

    private static BookAdapter mAdapter;
    private Context context;
    private static ArrayList<Book> bookList;
    private BookDAO bookDAO;

    // To keep at most one instance of BookAdapter
    public static BookAdapter getInstance(Context con, ArrayList<Book> books) {

        // If an instance exists, return it
        if (mAdapter != null) {
            bookList = books;
            return mAdapter;
        }

        // Else, craete a new instance
        mAdapter =  new MyAdapter(con, books);
        return mAdapter;
    }

    // BookAdapter's only constructor is declared as private to restrict access
    private BookAdapter(Context con, ArrayList<Book> books) {
        context = con;
        bookList = books;
        bookDAO = BookDAO.getInstance(context);
    }

    public void updateList(ArrayList<Book> books) {
        bookList = books;
        notifyDataSetChanged();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        // Retrieve object
        Book bookItem = bookList.get(position);

        ....
        ....

}

}

这就是Fragment的onCreateView将如何改变:

bookAdapter = BookAdapter.getInstance(getActivity(), listBook);

当用户按“是”Are you sure you want to delete?时将执行的代码:

// Remove entry from bookDAO
// Remove entry from listBook
// OR update listBook:
try {
    listBook = bookDAO.getAll();
} catch (Exception e) {
    Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
}

// Assertion: "listBook" does not contain the 
// item that was just deleted from "bookDAO"

// Update ListView's contents
bookAdapter.updateList(listBook);

答案 1 :(得分:2)

问题出现的原因是每次轮换,更改语言等等......重新创建活动并重新创建片段(新实例),因此notififyDataSetChanged实际上是通知片段的旧实例。 解决方案就是这样。使你的片段静止。然后为片段创建一些刷新方法,并在按下对话框时调用它。

在你的活动中你应该有这样的东西。

private static BookFragment  bookFragment;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    if (fragment1 == null) {
        bookFragment = new BookFragment();
    }

    ...
}

创建一些界面,如:

public interface Refreshable {
    public void refresh();
}

然后将所有片段实现此接口。 在对话框得到肯定回答时调用的方法中,您应该调用

...
fragment.refresh();
...

在片段的refresh方法中,您可以调用其适配器方法updateList(...)

可能不是更漂亮的解决方案,但它有效....

为什么会发生这种情况......谷歌的Android开发团队可能知道。

答案 2 :(得分:0)

我在GridViewAdapter中使用了一个字符串ArrayList,它扩展了BaseAdapter。

所以如果我改变了有关List的数据,我可以在Adapter上发送notifyDataSetChanged()。我真的不明白为什么你不能称之为它?

所以我要做的是ovverride这个方法,只需调用notifyDataSetChanged()

@Override
public void onConfigurationChanged(Configuration newConfig) {
   super.onConfigurationChanged(newConfig);
   adapter.notifyDataSetChanged();
}

除此之外,您的图书数据是否基于您的配置/方向?

答案 3 :(得分:0)

尝试对整个适配器生命周期使用相同的列表,并仅更改其内容:

public class BookAdapter extends ArrayAdapter<Book> {

    private final booklist;
    // ..

    public BookAdapter(Context c, ArrayList<Book> books) {
        super(c, R.layout.yourlayout, books);
        context = c;
        bookList = books
        bookDAO = BookDAO.getInstance(context);
    }

    public void updateList(ArrayList<Book> books) {
        bookList.clear();
        boolList.addAll(books);
        notifyDataSetChanged();
    }

    // ..
}

答案 4 :(得分:0)

为什么要将View保留为数据成员?

在保持跨配置更改的视图时,在配置更改发生之前,您将保留对上一个实例的引用。由于它是保存列表的视图 - 它可能会错过数据更新。

尝试删除此字段及其使用,并查看列表是否现在更新。

答案 5 :(得分:-1)

我认为问题来自于任何配置更改(例如方向)都会重新启动当前Activity

因此,我猜你的代码的某些部分仍然引用了之前不存在的活动,并且notifyDataSetChanged不再有效。

您可以快速尝试两件事:

  1. 在您的活动的清单文件中添加此行:android:configChanges="orientation|locale"。此更改意味着您将自己处理在方向或语言更改期间要执行的更改的系统。因此,应用程序将不再自行重新创建活动(因此活动应该相同)。

  2. 另一个技巧可以是在函数onCreateView的开头添加这一行: setRetainInstance(true);。 如文档所述,此行将在配置更改期间保留片段状态:

  3.   

    控制是否在活动重新创建(例如从配置更改)中保留片段实例。这只能用于不在后栈中的片段。如果设置,则重新创建活动时片段生命周期将略有不同:

         
        
    • onDestroy()将不会被调用(但onDetach()仍然会被调用,因为片段正在与其当前活动分离。)
    •   
    • onCreate(Bundle)将不会被调用,因为片段没有被重新创建。
    •   仍然会调用
    • onAttach(Activity)和onActivityCreated(Bundle)。
    •   

    请注意,正如所解释的那样,Fragment生命周期会发生一些变化。

    3)最后一个选项可能是使用onSaveInstanceStateonRestoreInstanceState保留活动状态,详见本答案:https://stackoverflow.com/a/151940/2206688