调用 notifyDataSetChanged 时未更新带有 customAdapter 的 ListView?

Posted

技术标签:

【中文标题】调用 notifyDataSetChanged 时未更新带有 customAdapter 的 ListView?【英文标题】:ListView with customAdapter not updated when notifyDataSetChanged is called? 【发布时间】:2016-01-20 17:52:38 【问题描述】:

我有一个带有两个适配器的 Listview。他们收到一个包含一些数据的数组,当滚动到列表视图的底部时,更多数据被加载到数组中,这里列表视图应该添加新数据。

问题: 如何使列表视图反映新数据,为什么在我调用 adAdapter.notifyDataSetChanged(); 时它没有更新?

我在 onPostExecute 中下载数据和更新 UI 的片段内部类:

@Override
    protected void onPostExecute(String result) 

        super.onPostExecute(result);

        numberofpagesshown = numberofpagesshown + 1;

        if(numberofpagesshown == 1 ) 

            list = (ListView) getActivity().findViewById(R.id.search_listview_movie);

            adapter = new SearchListViewAdapter(getActivity(), mylist);

            adAdapter = new BannerAdListView2(getActivity(), adapter);

           // list.setAdapter(adapter); this works when adapter.notifyDataSetChanged(); also is in the else statement but here I don't use the BannerAdListView2 adapter which i want to?

list.setAdapter(adAdapter);

            bar.setVisibility(View.GONE);
        
        else 

          //  adapter.notifyDataSetChanged();   // Here the listview should get refresh with the new data

              adAdapter.notifyDataSetChanged();

            bar.setVisibility(View.GONE);

        


        // Attach the listener to the AdapterView onCreate
        list.setOnScrollListener(new EndlessScrollListener() 
            @Override
            public void onLoadMore(int page, int totalItemsCount) 
                // Triggered only when new data needs to be appended to the list
                // Append new items to AdapterView
                if(pageNumberInt < totalPagesInt)

                   ++incre;
                   new DownloadJSON().execute();
                

            
        );


    

这是我的 SearchListViewAdapter:

public class SearchListViewAdapter extends BaseAdapter

Context context;
ArrayList<HashMap<String, String>> data;
HashMap<String, String> mylist = new HashMap<>();


static String url;
static String title;

public SearchListViewAdapter(Context a, ArrayList<HashMap<String, String>> d) 
    context = a;
    data = d;


public int getCount() 
    return data.size();


public HashMap<String, String> getItem(int position) 
    return data.get(position);


public long getItemId(int position) 
    return position;


public View getView(final int position, View convertView, ViewGroup parent) 

    // Avoid unneccessary calls to findViewById() on each row
    final ViewHolder holder;

    /*
     * If convertView is not null, reuse it directly, no inflation
     * Only inflate a new View when the convertView is null.
     */

        if (convertView == null) 
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.search_list_item, parent, false);

            holder = new ViewHolder();

            holder.poster = (ImageView) convertView.findViewById(R.id.list_image);

            holder.textForTitle = (TextView) convertView.findViewById(R.id.search_title);

            holder.textForTitle.setTag(position);

            convertView.setTag(holder);
        
        else

            // Get the ViewHolder back to get fast access to the TextView
            // and the ImageView.
            holder = (ViewHolder) convertView.getTag();
            holder.textForTitle.setTag(data.get(position));
        



        mylist = data.get(position);


        final String mediaType = mylist.get("media_type");

        if (mediaType.equals("movie")) 

            String posterPath = mylist.get("poster_path");
            url = "http://image.tmdb.org/t/p/w185" + posterPath;
            title = mylist.get("title");
         else if (mediaType.equals("tv")) 

            String posterPath = mylist.get("poster_path");
            url = "http://image.tmdb.org/t/p/w185" + posterPath;
            title = mylist.get("original_name");
         else 

            String posterPath = mylist.get("profile_path");
            url = "http://image.tmdb.org/t/p/w185" + posterPath;
            title = mylist.get("name");

        


        // set image url correctly
        // sizes for image 45, 92, 154, 185, 300, 500


        // load image url into poster
        Picasso.with(context).load(url).into(holder.poster);

        // set title to textview

        holder.textForTitle.setText(title);





    return convertView;






class ViewHolder 
    ImageView poster;
    TextView textForTitle;


 

还有我的 BannerAdListView2 适配器:

public class BannerAdListView2 extends BaseAdapter
     

Activity activity;
Context context;
BaseAdapter delegate;
int k = 7;
int baseItems;
int noAds;



// Constructor takes in a BaseAdapter
public BannerAdListView2(Activity activity, BaseAdapter delegate ) 

    this.activity = activity;
    this.delegate = delegate;

    baseItems = delegate.getCount();
    noAds  = baseItems / k;
    LayoutInflater mLayoutInflater = (LayoutInflater) this.activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);




         @Override
         public int getCount() 
             // Total count includes list items and ads.
             return baseItems + noAds;
         

         @Override
         public Object getItem(int position) 
             // Return null if an item is an ad.  Otherwise return the delegate item.
             if (isItemAnAd(position)) 
                 return null;
             
             return delegate.getItem(getOffsetPosition(position));
         

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

         @Override
         public int getViewTypeCount() 
             return delegate.getViewTypeCount() + noAds;
         

         @Override
         public int getItemViewType(int position) 
             if (isItemAnAd(position)) 
                 return delegate.getViewTypeCount();
              else 
                 return delegate.getItemViewType(getOffsetPosition(position));
             
         

         @Override
         public boolean areAllItemsEnabled() 
             return false;
         

         @Override
         public boolean isEnabled(int position) 
             return (!isItemAnAd(position)) && delegate.isEnabled(getOffsetPosition(position));
         

         private boolean isItemAnAd(int position) 


             if (position < k) return false;
             // Calculate current offset caused by ads already embedded

             if (position==k)
                 return true;
             
             else 
                 return isItemAnAd(position-k);
             


         

         // Get the position that is offset by the insertion of the ads
         private int getOffsetPosition(int position) 
             int currentNoAds = position / k;
             return position - currentNoAds;

         



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

             // Display every n list items
             if (isItemAnAd(position)) 
                 if (convertView instanceof AdView) 
                     // Don’t instantiate new AdView, reuse old one
                     return convertView;
                  else 
                     // Create a new AdView
                     AdView adView = new AdView(activity);
                     adView.setAdSize(AdSize.BANNER);
                     String bannerId = "My unit id";
                     adView.setAdUnitId(bannerId);

                     // Disable focus for sub-views of the AdView to avoid problems with
                     // trackpad navigation of the list.
                     for (int i = 0; i < adView.getChildCount(); i++)
                     
                         adView.getChildAt(i).setFocusable(false);
                     
                     adView.setFocusable(false);

                     // Convert the default layout parameters so that they play nice with
                     // ListView.

                     float density = activity.getResources().getDisplayMetrics().density;
                     int height = Math.round(AdSize.BANNER.getHeight() * density);
                     AbsListView.LayoutParams params = new AbsListView.LayoutParams(
                             AbsListView.LayoutParams.MATCH_PARENT,
                             height);
                     adView.setLayoutParams(params);
                     AdRequest bannerIntermediateReq = new AdRequest.Builder()
                             .addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
                             .addTestDevice("d9e108ab") //means that a test ad is shown on my phone
                             .build();

                     adView.loadAd(bannerIntermediateReq);

                     return adView;
                 
              else 

                 // Offload displaying other items to the delegate
                 return delegate.getView(getOffsetPosition(position) ,
                         convertView, parent);

             
         
     

编辑: 更改 BannerAdListView2 的构造函数

 // Constructor takes in a BaseAdapter
public BannerAdListView2(Activity activity, final BaseAdapter delegate ) 

    this.activity = activity;
    this.delegate = delegate;
    delegate.registerDataSetObserver(new DataSetObserver() 
        public void onChanged() 
            baseItems = delegate.getCount();
            noAds = baseItems / k;
        

        public void onInvalidated() 
            notifyDataSetInvalidated();
        
    );
    baseItems = delegate.getCount();
    noAds  = baseItems / k;
    LayoutInflater mLayoutInflater = (LayoutInflater) this.activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);


在片段中

@Override
    protected void onPostExecute(String result) 

        super.onPostExecute(result);

        numberofpagesshown = numberofpagesshown + 1;

        if(numberofpagesshown == 1 ) 

            list = (ListView) getActivity().findViewById(R.id.search_listview_movie);

            adapter = new SearchListViewAdapter(getActivity(), mylist);

            adAdapter = new BannerAdListView2(getActivity(), adapter);

            list.setAdapter(adAdapter);

            bar.setVisibility(View.GONE);
        
        else 

            adapter.notifyDataSetChanged();   

            bar.setVisibility(View.GONE);

        

     

【问题讨论】:

呵呵,你只是不放弃...这显然是因为public int getCount() return baseItems + noAds; ...这个实现仍然过于复杂,无法分析它...只需将它画在纸上... 不,我解决了我之前遇到的问题。但这应该包括两者? adview 计数和来自其他适配器的视图? baseItems 仅在 BannerAdListView2 构造函数中设置 .. 所以即使内部适配器更改了它的项目计数外部适配器也不知道它... 所以每次数据更改时我都应该调用 BannerAdListView2 吗?这似乎不太实用? no ... 而是在外部适配器中实现 DataSetObserver ... 并将其注册到内部适配器 ... 然后在外部适配器的 onChanged() 中执行与构造函数中相同的操作( baseItems = delegate. getCount(); noAds = baseItems / k;) 并使 self(notifyDataSetChanged - 外部适配器) 无效 ...现在当您更改内部适配器中的数据时,只需在内部适配器上调用 notifyDataSetChanged ...说真的,先在纸上画出来(我会这样做)这不是一件容易的事...... 【参考方案1】:

试试这个解决方案...DecorerAdapter:

public static abstract class DecorerAdapter extends BaseAdapter 

            public int DECORER_ITEM_TYPE;
            private final BaseAdapter mInnerAdapter;
            private final int mRepeatAfterEvery;
            private int mCount;

            public DecorerAdapter(BaseAdapter innerAdapter, int repeatAfterEvery) 
                mInnerAdapter = innerAdapter;
                mRepeatAfterEvery = repeatAfterEvery;
                mInnerAdapter.registerDataSetObserver(new DataSetObserver() 
                    @Override
                    public void onChanged() 
                        notifyDataSetChanged();
                    

                    @Override
                    public void onInvalidated() 
                        notifyDataSetInvalidated();
                    
                );
                setupAdapter();
            

            @Override
            public void notifyDataSetChanged() 
                setupAdapter();
                super.notifyDataSetChanged();
            

            private void setupAdapter()
                mCount = mInnerAdapter.getCount();
                mCount += (mCount  + mRepeatAfterEvery - 2) / (mRepeatAfterEvery - 1);
                DECORER_ITEM_TYPE = mInnerAdapter.getViewTypeCount();
            

            @Override
            public int getCount() 
                return mCount;
            

            @Override
            public Object getItem(int position) 
                if(position % mRepeatAfterEvery == 0)
                    return null;
                return mInnerAdapter.getItem(calculateInnerPosition(position));
            

            private int calculateInnerPosition(int position) 
                return position - (position  + mRepeatAfterEvery - 1) / mRepeatAfterEvery;
            

            @Override
            public long getItemId(int position) 
                if(position % mRepeatAfterEvery == 0)
                    return -1;
                return mInnerAdapter.getItemId(calculateInnerPosition(position));
            

            @Override
            public int getItemViewType(int position) 
                if(position % mRepeatAfterEvery == 0)
                    return DECORER_ITEM_TYPE;
                return mInnerAdapter.getItemViewType(calculateInnerPosition(position));
            

            @Override
            public boolean hasStableIds() 
                return mInnerAdapter.hasStableIds();
            

            @Override
            public int getViewTypeCount() 
                return mInnerAdapter.getViewTypeCount() + 1;
            

            @Override
            public View getView(int position, View convertView, ViewGroup parent) 
                if(position % mRepeatAfterEvery == 0)
                    return getDecorerView((position  + mRepeatAfterEvery - 1) / mRepeatAfterEvery, convertView, parent);
                return mInnerAdapter.getView(calculateInnerPosition( position), convertView, parent);
            

            public abstract View getDecorerView(int position, View convertView, ViewGroup parent);
        

使用代码:

package pl.selvin.decoreradapter;

import android.database.DataSetObserver;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;

public class MainActivity extends AppCompatActivity 

    /*** paste the DecorerAdapter class here ***/

    public static class MyListFragment extends ListFragment
        final ArrayList<String> strings = new ArrayList<>();
        private BaseAdapter innerAdapter;

        @Override
        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) 
            menu.add("Add rnd(10) more");

            super.onCreateOptionsMenu(menu, inflater);
        

        final Random rnd = new Random();
        @Override
        public boolean onOptionsItemSelected(MenuItem item) 
            final String[] toAdd = new String[rnd.nextInt(10)];
            int start = strings.size();
            for(int i = 0; i < toAdd.length; i++)
                toAdd[i] = (start + i) + "";
            Collections.addAll(strings, toAdd);
            Toast.makeText(getActivity(), "Added " + toAdd.length + " count: " + strings.size(), Toast.LENGTH_SHORT).show();
            innerAdapter.notifyDataSetChanged();
            return true;
        

        @Override
        public void onCreate(Bundle savedInstanceState) 
            super.onCreate(savedInstanceState);
            setHasOptionsMenu(true);
            for(int i = 0; i < 4; i++) 
                strings.add(i + "");
            
            innerAdapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, strings);
            setListAdapter(new DecorerAdapter(innerAdapter, 5) 
                @Override
                public View getDecorerView(int position, View convertView, ViewGroup parent) 
                    if(convertView == null) 
                        convertView = new TextView(getActivity());
                        convertView.setBackgroundColor(Color.RED);
                    
                    ((TextView)convertView).setText("AdView: " + position);
                    return convertView;
                
            );
        

        @Override
        public void onListItemClick(ListView l, View v, int position, long id) 
            String item = (String)l.getItemAtPosition(position);
            Toast.makeText(getActivity(), "Item click: " + (item == null ? "ADVIEW" : item), Toast.LENGTH_LONG).show();
        
    

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        if (savedInstanceState == null) 
            getSupportFragmentManager().beginTransaction().replace(android.R.id.content, new MyListFragment(), "LIST").commit();
        
    

它在传递给构造函数的每个元素上重复装饰器视图 ...

你需要做的就是以与普通适配器中的getView类似的方式实现getDecorerView

【讨论】:

这样流畅多了!您能解释一下为什么将 (mCount + mRepeatAfterEvery - 2) / (mRepeatAfterEvery - 1) 添加到 mCount (在 void setup adapter 中)吗?我不太明白那里的数学? 智能天花板......它只计算 mCount 的装饰器视图的数量,如果你在每个 mRepeatAfterEvery 重复它 ***.com/questions/7139382/… ... (int) Math.ceil((double) mCount / (mRepeatAfterEvery -1))) 同样的故事:return position - (position + mRepeatAfterEvery - 1) / mRepeatAfterEvery; 我们已经添加了多少 decore 视图? (int) Math.ceil((double) mCount / mRepeatAfterEvery)) 如您所见,无需递归即可完成 好的,非常感谢。可以将类似的方法用于网格视图吗?或者是否需要使用交错的网格视图(更类似于列表视图)?

以上是关于调用 notifyDataSetChanged 时未更新带有 customAdapter 的 ListView?的主要内容,如果未能解决你的问题,请参考以下文章

当调用 RecyclerView 适配器的“notifyDataSetChanged()”时,RecyclerView 抛出“java.lang.Throwable: addInArray”

带有ListView的Android ViewPager在调用notifyDatasetchanged时无法记住最后一个位置

Android 从 AsyncTask 调用 notifyDataSetChanged

如何为自定义 ExpandableListView 调用 notifyDataSetChanged?

每当在 recyclerview 中为 base64 图像调用 notifydatasetchanged() 时,图像就会闪烁

在 notifydatasetchanged 后未调用 getview