首页 文章

如何刷新Android listview?

提问于
浏览
402

如何在添加/删除动态数据后刷新Android ListView

25 回答

  • 0

    修改了该适配器中的数据后,在 Adapter 对象上调用notifyDataSetChanged() .

    有关如何/何时调用 notifyDataSetChanged() 的一些其他细节可以在this Google I/O video中查看 .

  • 0

    我有一个ArrayList,我想在listview中显示 . ArrayList包含来自mysql的元素 . 我重写了onRefresh方法,在那个方法中我使用了 tablelayout.removeAllViews(); 然后重复了从数据库中再次获取数据的过程 . 但在此之前,请确保清除ArrayList或任何数据结构,否则新数据将附加到旧数据 .

  • 0

    我认为这取决于你刷新的意思 . 您是否应该刷新GUI显示,或者您是否应该刷新子视图,以便您可以以编程方式调用getChildAt(int)并获取与Adapter中的内容相对应的视图 .

    如果要刷新GUI显示,请在适配器上调用notifyDataSetChanged() . 下次重绘时,GUI将刷新 .

    如果您希望能够调用getChildAt(int)并获得反映适配器内容的视图,则调用layoutChildren() . 这将导致从适配器数据重建子视图 .

  • 0

    从列表视图中删除数据后,您必须调用 refreshDrawableState() . 这是一个例子:

    final DatabaseHelper db = new DatabaseHelper (ActivityName.this);
    
    db.open();
    
    db.deleteContact(arg3);
    
    mListView.refreshDrawableState();
    
    db.close();
    

    DatabaseHelper 类中的 deleteContact 方法会有点像

    public boolean deleteContact(long rowId) {
    
       return db.delete(TABLE_NAME, BaseColumns._ID + "=" + rowId, null) > 0;
    
    }
    
  • -1

    如果我在这里讨论我的场景,上述答案中的任何一个都不起作用,因为我的活动显示了db值列表以及删除按钮,当按下删除按钮时,我想从列表中删除该项目 .

    很酷的是,我没有使用Recycler视图,而是使用简单的列表视图和在适配器类中初始化的列表视图 . 因此,调用 notifyDataSetChanged() 将不会在适配器类内部执行任何操作,甚至也不会在初始化适配器对象的activity类中执行任何操作,因为delete方法位于适配器类中 .

    因此,解决方案是在适配器类 getView 方法中从适配器中删除对象(仅删除该特定对象,但如果要删除所有对象,请调用 clear() ) .

    为了得到一些想法,我的代码是什么样的,

    public class WordAdapter extends ArrayAdapter<Word> {
      Context context;
    
      public WordAdapter(Activity context, ArrayList<Word> words) {}
        //.......
    
        @NonNull
        @Override
        public View getView(final int position, View convertView, ViewGroup group) {
          //.......
         ImageButton deleteBt = listItemView.findViewById(R.id.word_delete_bt);
         deleteBt.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 if (vocabDb.deleteWord(currentWord.id)) {
                    //.....
                 } else{
                    //.....
                 }
                 remove(getItem(position)); // <---- here is the trick ---<
                 //clear() // if you want to clear everything
             }
        });
     //....
    

    注意:这里 remove()getItem() 方法继承自Adapter类 .

    • remove() - 删除单击的特定项目

    • getItem(position) - 是从点击的位置获取项目(这里,这是我已添加到列表中的Word对象) .

    这就是我如何将适配器设置为活动类中的listview,

    ArrayList<Word> wordList = new ArrayList();
        WordAdapter adapter = new WordAdapter(this, wordList);
    
        ListView list_view = (ListView) findViewById(R.id.activity_view_words);
        list_view.setAdapter(adapter);
    
  • 40

    请忽略所有invalidate(),invalidateViews(),requestLayout(),...这个问题的答案 .

    正确的做法(幸运的是,也标记为正确答案)是to call notifyDataSetChanged() on your Adapter .

    故障排除

    如果调用 notifyDataSetChanged() 也不帮助.357623_ . 相信我 ListView 已正确更新 . 如果找不到差异,则需要检查适配器中的数据来自何处 .

    如果这只是一个集合,那么您将在内存中检查您是否在调用 notifyDataSetChanged() 之前实际删除了项目或将项目添加到集合中 .

    如果你在调用 notifyDataSetChanged() 之前必须再次调用该方法来检索信息(或操作内存数据) .

    事情是这个 notifyDataSetChanged 只有在数据集发生变化时才有效 . 如果您没有发现变化,那么这就是您要查看的地方 . 如果需要调试 .

    ArrayAdapter与BaseAdapter

    我确实发现使用适配器来管理集合,就像BaseAdapter更好用 . 像ArrayAdapter这样的一些适配器已经管理了自己的集合,这使得更难获得适当的更新集合 . 在大多数情况下,它实际上只是一个不必要的额外难度层 .

    UI线程

    确实,必须从UI线程调用它 . 其他答案有关于如何实现这一点的示例 . 但是,只有在从UI线程外部处理此信息时才需要这样做 . 这是来自服务或非UI线程 . 在简单的情况下,您将从按钮单击或其他活动/片段更新数据 . 所以仍然在UI线程内 . 无需始终弹出runOnUiTrhead .

    快速示例项目

    可以在https://github.com/hanscappelle/so-2250770.git找到 . 只需在Android Studio(gradle)中克隆并打开项目即可 . 该项目的MainAcitivity构建了一个包含所有随机数据的 ListView . 可以使用操作菜单刷新此列表 .

    我为此示例ModelObject创建的适配器实现公开了数据集合

    public class MyListAdapter extends BaseAdapter {
    
        /**
         * this is our own collection of data, can be anything we 
         * want it to be as long as we get the abstract methods 
         * implemented using this data and work on this data 
         * (see getter) you should be fine
         */
        private List<ModelObject> mData;
    
        /**
         * our ctor for this adapter, we'll accept all the things 
         * we need here
         *
         * @param mData
         */
        public MyListAdapter(final Context context, final List<ModelObject> mData) {
            this.mData = mData;
            this.mContext = context;
        }
    
        public List<ModelObject> getData() {
            return mData;
        }
    
        // implement all abstract methods here
    }
    

    MainActivity中的代码

    public class MainActivity extends Activity {
    
        private MyListAdapter mAdapter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            ListView list = (ListView) findViewById(R.id.list);
    
            // create some dummy data here
            List<ModelObject> objects = getRandomData();
            // and put it into an adapter for the list
            mAdapter = new MyListAdapter(this, objects);
            list.setAdapter(mAdapter);
    
            // mAdapter is available in the helper methods below and the 
            // data will be updated based on action menu interactions
    
            // you could also keep the reference to the android ListView 
            // object instead and use the {@link ListView#getAdapter()} 
            // method instead. However you would have to cast that adapter 
            // to your own instance every time
        }
    
        /**
         * helper to show what happens when all data is new
         */
        private void reloadAllData(){
            // get new modified random data
            List<ModelObject> objects = getRandomData();
            // update data in our adapter
            mAdapter.getData().clear();
            mAdapter.getData().addAll(objects);
            // fire the event
            mAdapter.notifyDataSetChanged();
        }
    
        /**
         * helper to show how only changing properties of data 
         * elements also works
         */
        private void scrambleChecked(){
            Random random = new Random();
            // update data in our adapter, iterate all objects and 
            // resetting the checked option
            for( ModelObject mo : mAdapter.getData()) {
                mo.setChecked(random.nextBoolean());
            }
            // fire the event
            mAdapter.notifyDataSetChanged();
        }
    }
    

    更多信息

    关于listViews的强大功能的另一篇不错的帖子可以在这里找到:http://www.vogella.com/articles/AndroidListView/article.html

  • 507

    您需要使用该列表中的单个对象,该对象将在 ListView 上进行充气 . 如果参考是改变,那么 notifyDataSetChanged() 不起作用 . 无论何时你是从列表视图中删除元素也会从您正在使用的列表中删除它们,无论它是ArrayList <>还是其他,然后在您的适配器类的对象上调用 notifyDataSetChanged() .

    所以这里看看我如何在我的适配器中管理它,见下文

    public class CountryCodeListAdapter extends BaseAdapter implements OnItemClickListener{
    
    private Context context;
    private ArrayList<CountryDataObject> dObj;
    private ViewHolder holder;
    private Typeface itemFont;
    private int selectedPosition=-1;
    private ArrayList<CountryDataObject> completeList;
    
    public CountryCodeListAdapter(Context context, ArrayList<CountryDataObject> dObj) {
        this.context = context;
        this.dObj=dObj;
        completeList=new  ArrayList<CountryDataObject>();
        completeList.addAll(dObj);
        itemFont=Typeface.createFromAsset(context.getAssets(), "CaviarDreams.ttf");
    }
    
    @Override
    public int getCount() {
        return dObj.size();
    }
    
    @Override
    public Object getItem(int position) {
        return dObj.get(position);
    }
    
    @Override
    public long getItemId(int position) {
        return position;
    }
    @Override
    public View getView(int position, View view, ViewGroup parent) {
        if(view==null){
            holder = new ViewHolder();
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = inflater.inflate(R.layout.states_inflator_layout, null);
            holder.textView = ((TextView)view.findViewById(R.id.stateNameInflator));
            holder.checkImg=(ImageView)view.findViewById(R.id.checkBoxState);
            view.setTag(holder);
        }else{
            holder = (ViewHolder) view.getTag();
        }
        holder.textView.setText(dObj.get(position).getCountryName());
        holder.textView.setTypeface(itemFont);
    
        if(position==selectedPosition)
         {
             holder.checkImg.setImageResource(R.drawable.check);
         }
         else
         {
             holder.checkImg.setImageResource(R.drawable.uncheck);
         }
        return view;
    }
    private class ViewHolder{
        private TextView textView;
        private ImageView checkImg;
    }
    
    public void getFilter(String name) {
        dObj.clear();
        if(!name.equals("")){
        for (CountryDataObject item : completeList) {
            if(item.getCountryName().toLowerCase().startsWith(name.toLowerCase(),0)){
                dObj.add(item);
            }
        }
        }
        else {
            dObj.addAll(completeList);
        }
        selectedPosition=-1;
        notifyDataSetChanged();
        notifyDataSetInvalidated(); 
    }
    
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position,
            long id) {
        Registration reg=(Registration)context;
        selectedPosition=position;
        reg.setSelectedCountryCode("+"+dObj.get(position).getCountryCode());
        notifyDataSetChanged();
    }
    }
    
  • 0

    我无法通过notifyDataSetChanged()来更新我的SimpleAdapter,所以我尝试先删除所有使用removeAllViews()附加到父布局的视图,然后添加ListView,这样就可以了,这样我就可以更新了用户界面:

    LinearLayout results = (LinearLayout)findViewById(R.id.results);
    ListView lv = new ListView(this);
    ArrayList<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>();
    SimpleAdapter adapter = new SimpleAdapter( this, list, R.layout.directory_row, 
                    new String[] { "name", "dept" }, new int[] { R.id.name, R.id.dept } );
    
    for (...) { 
        HashMap<String, String> map = new HashMap<String, String>();
        map.put("name", name);
        map.put("dept", dept);
        list.add(map);
    }
    
    lv.setAdapter(adapter);
    results.removeAllViews();     
    results.addView(lv);
    
  • 0

    对于我在更改sql数据库中的信息之后,没有什么可以刷新列表视图(作为特定的可扩展列表视图),因此如果notifyDataSetChanged()没有帮助,您可以尝试先清除列表并在调用notifyDataSetChanged()之后再次添加它 . 例如

    private List<List<SomeNewArray>> arrayList;
    List<SomeNewArray> array1= getArrayList(...);
    List<SomeNewArray> array2= getArrayList(...);
    arrayList.clear();
    arrayList.add(array1);
    arrayList.add(array2);
    notifyDataSetChanged();
    

    希望它对你有意义 .

  • 11

    我在listview的动态刷新方面遇到了一些问题 .

    在适配器上调用notifyDataSetChanged() . 有关如何/何时调用notifyDataSetChanged()的一些其他细节可以在此Google I / O视频中查看 .

    在我的情况下,notifyDataSetChanged()无法正常工作[我从另一个类调用了notifyDataSetChanged] . 就在我在正在运行的Activity(Thread)中编辑ListView的情况下 . 该视频thanks to Christopher给出了最后的暗示 .

    在我使用的第二堂课中

    Runnable run = new Runnable(){
         public void run(){
             contactsActivity.update();
         }
    };
    contactsActivity.runOnUiThread(run);
    

    从我的Activity访问update() . 此更新包括

    myAdapter.notifyDataSetChanged();
    

    告诉适配器刷新视图 . 据我所知,工作得很好 .

  • 194

    在其他选项上是 onWindowFocusChanged 方法,但确定它敏感并需要一些额外的编码,对谁感兴趣

    override fun onWindowFocusChanged(hasFocus: Boolean) {
            super.onWindowFocusChanged(hasFocus)
    
           // some controls needed
            programList = usersDBHelper.readProgram(model.title!!)
            notesAdapter = DailyAdapter(this, programList)
            notesAdapter.notifyDataSetChanged()
            listview_act_daily.adapter = notesAdapter
        }
    
  • 145

    本帖子中人们提出的解决方案是否有效,主要取决于您设备的Android版本 . 例如,要使用AddAll方法,你必须在你的Android设备中放置android:minSdkVersion =“10” .

    为了解决所有设备的这个问题,我在我的适配器中创建了我自己的方法,并在add和remove方法中使用继承自ArrayAdapter,可以毫无问题地更新数据 .

    我的代码:使用我自己的数据类RaceResult,您可以使用自己的数据模型 .

    ResultGpRowAdapter.java

    public class ResultGpRowAdapter extends ArrayAdapter<RaceResult> {
    
        Context context;
        int resource;
        List<RaceResult> data=null;
    
            public ResultGpRowAdapter(Context context, int resource, List<RaceResult> objects)           {
            super(context, resource, objects);
    
            this.context = context;
            this.resource = resource;
            this.data = objects;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
    
            ........
            }
    
            //my own method to populate data           
            public void myAddAll(List<RaceResult> items) {
    
            for (RaceResult item:items){
                super.add(item);
            }
        }
    

    ResultsGp.java

    public class ResultsGp extends Activity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
        ...........
        ...........
        ListView list = (ListView)findViewById(R.id.resultsGpList); 
    
        ResultGpRowAdapter adapter = new ResultGpRowAdapter(this,  R.layout.activity_result_gp_row, new ArrayList<RaceResult>()); //Empty data
    
       list.setAdapter(adapter);
    
       .... 
       ....
       ....
       //LOAD a ArrayList<RaceResult> with data
    
       ArrayList<RaceResult> data = new ArrayList<RaceResult>();
       data.add(new RaceResult(....));
       data.add(new RaceResult(....));
       .......
    
       adapter.myAddAll(data); //Your list will be udpdated!!!
    
  • 0

    我只能通过获取新的适配器数据,然后重置列表视图的适配器,然后像这样进行调用来获取notifyDataSetChanged:

    expandableAdapter = baseFragmentParent.setupEXLVAdapter();
        baseFragmentParent.setAdapter(expandableAdapter);
        expandableAdapter.notifyDataSetChanged();
    
  • 5

    如果您对ListView Refreshment不满意,可以查看这个片段,这是用于从DB加载listView,实际上你要做的只是重新加载ListView,在你执行任何CRUD操作后它不是最好的方法代码,但它会根据您的意愿刷新ListView ..

    它对我有用....如果你找到更好的解决方案,请分享......

    .......
    ......
    do your CRUD Operations..
    ......
    .....
    DBAdapter.open();
    DBAdapter.insert_into_SingleList();
    // Bring that DB_results and add it to list as its contents....
                                                ls2.setAdapter(new ArrayAdapter(DynTABSample.this,
        android.R.layout.simple_list_item_1,                        DBAdapter.DB_ListView));
                                                DBAdapter.close();
    
  • 0

    你也可以用这个:

    myListView.invalidateViews();
    

    请享用!

  • 0

    在片段中,我想用一段时间扫描的BLE设备的mac地址填充ListView(在单个TextView中)时,我是一样的 .

    我做的是这样的:

    public class Fragment01 extends android.support.v4.app.Fragment implements ...
    {
        private ListView                listView;
        private ArrayAdapter<String>    arrayAdapter_string;
    
    ...
    
    @Override
    public void onActivityCreated(Bundle savedInstanceState)
    {
        ...
        this.listView= (ListView) super.getActivity().findViewById(R.id.fragment01_listView);
        ...
        this.arrayAdapter_string= new ArrayAdapter<String>(super.getActivity(), R.layout.dispositivo_ble_item, R.id.fragment01_item_textView_titulo);
        this.listView.setAdapter(this.arrayAdapter_string);
    }
    
    
    @Override
    public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord)
    {
        ...
        super.getActivity().runOnUiThread(new RefreshListView(device));
    }
    
    
    private class RefreshListView implements Runnable
    {
        private BluetoothDevice bluetoothDevice;
    
        public RefreshListView(BluetoothDevice bluetoothDevice)
        {
            this.bluetoothDevice= bluetoothDevice;
        }
    
        @Override
        public void run()
        {
            Fragment01.this.arrayAdapter_string.add(new String(bluetoothDevice.toString()));
            Fragment01.this.arrayAdapter_string.notifyDataSetChanged();
        }
    }
    

    然后ListView开始动态填充找到的设备的mac地址 .

  • 1

    随时调用runnable:

    runOnUiThread(run);
    

    OnCreate() ,你设置了你的runnable线程:

    run = new Runnable() {
        public void run() {
            //reload content
            arraylist.clear();
            arraylist.addAll(db.readAll());
            adapter.notifyDataSetChanged();
            listview.invalidateViews();
            listview.refreshDrawableState();
        }
    };
    
  • 0

    如果要从服务更新UI列表视图,请在Main活动中使适配器保持静态,并执行以下操作:

    @Override
    public void onDestroy() {
        if (MainActivity.isInFront == true) {
            if (MainActivity.adapter != null) {
                MainActivity.adapter.notifyDataSetChanged();
            }
    
            MainActivity.listView.setAdapter(MainActivity.adapter);
        }
    }
    
  • 1

    考虑您已将列表传递给适配器 .
    使用:

    list.getAdapter().notifyDataSetChanged()

    更新你的清单 .

  • 2

    而使用SimpleCursorAdapter可以在适配器上调用changeCursor(newCursor) .

  • 1

    如果要在刷新时保持滚动位置,可以执行以下操作:

    if (mEventListView.getAdapter() == null) {
        EventLogAdapter eventLogAdapter = new EventLogAdapter(mContext, events);
        mEventListView.setAdapter(eventLogAdapter);
    } else {
        ((EventLogAdapter)mEventListView.getAdapter()).refill(events);
    }
    
    public void refill(List<EventLog> events) {
        mEvents.clear();
        mEvents.addAll(events);
        notifyDataSetChanged();
    }
    

    有关详细信息,请参阅Android ListView: Maintain your scroll position when you refresh .

  • 0

    只需在监听器中使用 myArrayList.remove(position);

    myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override public void onItemClick(AdapterView<?> parent, android.view.View view, int position, long id) {
               myArrayList.remove(position);
               myArrayAdapter.notifyDataSetChanged();
            }
        });
    
  • 0

    最简单的方法是制作一个新的Adaper并删除旧的Adaper:

    myListView.setAdapter(new MyListAdapter(...));
    
  • 0

    如果您正在使用SimpleCursorAdapter尝试在Cursor对象上调用requery() .

  • 9

    如果您要通过android指南行,并且您正在使用 ContentProvidersDatabase 获取数据,并且您使用 CursorLoaderCursorAdaptersListView 中显示它,那么您对相关数据的所有更改将自动反映在ListView中 .

    ContentProvider 光标上的 getContext().getContentResolver().notifyChange(uri, null); 足以反映更改 . 无需额外的工作 .

    但是当你不使用这些时,你需要在数据集发生变化时告诉适配器 . 您还需要重新填充/重新加载数据集(例如列表),然后需要在适配器上调用 notifyDataSetChanged() .

    notifyDataSetChanged() 如果数据集中没有更改,则无法工作 . 以下是docs中方法的上述注释

    /**
     * Notifies the attached observers that the underlying data has been changed
     * and any View reflecting the data set should refresh itself.
     */
    

相关问题