空指针异常 - 在RecyclerView

时间:2018-02-06 07:55:20

标签: android android-recyclerview nullpointerexception searchview

我一直在我的recyclerview中使用我的搜索过滤器面对NPE。我的搜索过滤器在我的自定义工具栏中,每当我尝试在searchview中输入内容时,应用程序崩溃。我已经提供了以下代码..

空指针异常

java.lang.NullPointerException: Attempt to invoke virtual method 'android.widget.Filter com.cvsuimus.theflare.NewsArticleAdapter.getFilter()' on a null object reference
                                                                           at com.cvsuimus.theflare.MainPageActivity$1.onQueryTextChange(MainPageActivity.java:101)

MainPageActivity.Java

public class MainPageActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {

    public SearchView searchView;
    public NewsArticleAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_page);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);

        RecentNewsFragment recentNewsFragment = new RecentNewsFragment();
        FragmentManager manager = getSupportFragmentManager();
        manager.beginTransaction().replace(R.id.mainLayout, recentNewsFragment, recentNewsFragment.getTag()).commit();

    }

    long lastPress;
    @Override
    public void onBackPressed() {
        long currentTime = System.currentTimeMillis();
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        // close search view on back button pressed
        if (!searchView.isIconified()) {
            searchView.setIconified(true);
            return;
        }
        else if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        }
        else if (currentTime - lastPress > 5000) {
            Toast.makeText(getBaseContext(), "Press back once more to exit", Toast.LENGTH_LONG).show();
            lastPress = currentTime;
        }
        else {
            Intent homeIntent = new Intent(Intent.ACTION_MAIN);
            homeIntent.addCategory( Intent.CATEGORY_HOME );
            homeIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(homeIntent);
        }
        super.onBackPressed();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main_page, menu);
        // Associate searchable configuration with the SearchView
        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        MenuItem searchMenuItem = menu.findItem(R.id.action_search);
        searchView = (SearchView) MenuItemCompat.getActionView(searchMenuItem);
        if (searchManager != null) {
            searchView.setSearchableInfo(searchManager.getSearchableInfo(new ComponentName(getApplicationContext(), MainPageActivity.class)));
        }
        searchView.setMaxWidth(Integer.MAX_VALUE);

        // listening to search query text change
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                // filter recycler view when query submitted
                adapter.getFilter().filter(query);
                return false;
            }

            @Override
            public boolean onQueryTextChange(String query) {
                // filter recycler view when text is changed
                adapter.getFilter().filter(query);
                return false;
            }
        });
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_search) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();

        if (id == R.id.nav_recent) {
            setTitle("Recent News");
            RecentNewsFragment recentNewsFragment = new RecentNewsFragment();
            FragmentManager manager = getSupportFragmentManager();
            manager.beginTransaction().replace(R.id.mainLayout, recentNewsFragment, recentNewsFragment.getTag()).commit();
        } else if (id == R.id.nav_favorites) {
            setTitle("Favorite News");
            FavoriteNewsFragment favoriteNewsFragment = new FavoriteNewsFragment();
            FragmentManager manager = getSupportFragmentManager();
            manager.beginTransaction().replace(R.id.mainLayout, favoriteNewsFragment, favoriteNewsFragment.getTag()).commit();
        } else if (id == R.id.nav_forum) {
            setTitle("Recent Topics");
            ForumFragment forumFragment = new ForumFragment();
            FragmentManager manager = getSupportFragmentManager();
            manager.beginTransaction().replace(R.id.mainLayout, forumFragment, forumFragment.getTag()).commit();
        }   else if (id == R.id.nav_games) {
            setTitle("Games");
            GamesFragment gamesFragment = new GamesFragment();
            FragmentManager manager = getSupportFragmentManager();
            manager.beginTransaction().replace(R.id.mainLayout, gamesFragment, gamesFragment.getTag()).commit();
        }
        else if (id == R.id.nav_account) {
            AccountSettingsFragment accountSettingsFragment = new AccountSettingsFragment();
            FragmentManager manager = getSupportFragmentManager();
            manager.beginTransaction().replace(R.id.mainLayout, accountSettingsFragment, accountSettingsFragment.getTag()).commit();
        } else if (id == R.id.nav_settings) {


        } else if (id == R.id.nav_logout) {
            SharedPreferences pref = getSharedPreferences(StartupActivity.PREFS_NAME, MODE_PRIVATE);
            SharedPreferences.Editor editor = pref.edit();
            editor.remove("studentNumber");
            editor.remove("password");
            editor.apply();
            Intent in = new Intent(getApplicationContext(), StartupActivity.class);
            in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(in);

        } else if (id == R.id.nav_about_us) {
            Intent in = new Intent(getApplicationContext(), AboutUsActivity.class);
            startActivity(in);
        }

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }

}

RecentNewsFragment.Java

public class RecentNewsFragment extends Fragment implements ConnectivityReceiver.ConnectivityReceiverListener, NewsArticleAdapter.NewsArticleAdapterListener  {

    public RecentNewsFragment() {

    }

    final String TAG = "MainPageActivity";
    public RecyclerView rvNewsArticle;
    public ArrayList<News> newsArrayList;
    public NewsArticleAdapter.NewsArticleAdapterListener listener;
    public SwipeRefreshLayout swipeRefreshLayout;

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

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

        checkConnection();

        rvNewsArticle = (RecyclerView) view.findViewById(R.id.rvNewsArticle);
        rvNewsArticle.setHasFixedSize(true);
        final LinearLayoutManager manager = new LinearLayoutManager(getActivity());
        rvNewsArticle.setLayoutManager(manager);

        swipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipeRefresh);
        swipeRefreshLayout.setColorSchemeResources(R.color.primaryColor, R.color.primaryTextColor, R.color.secondaryColor);
        swipeRefreshLayout.setSize(SwipeRefreshLayout.LARGE);
        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        loadNews();
                    }
                }, 1000);
            }
        });

        return view;
    }

    private void loadNews() {
        String ip = IPAddress.ipaddress;
        String url = "http://" + ip + "/theflare/app/news.php";
        StringRequest stringRequest = new StringRequest(Request.Method.GET,
                url,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        Log.d(TAG, response);

                        ArrayList<News> newsList = new JsonConverter<News>()
                                .toArrayList(response, News.class);
                        final NewsArticleAdapter adapter = new NewsArticleAdapter(getActivity(), newsList, listener);
                        newsArrayList = new ArrayList<>();

                        // adding news to news list
                        newsArrayList.clear();
                        newsArrayList.addAll(newsList);
                        // refreshing recycler view
                        adapter.notifyDataSetChanged();

                        rvNewsArticle.setAdapter(adapter);

                        swipeRefreshLayout.setRefreshing(false);

                        FileCacher<ArrayList<News>> newsCacher = new FileCacher<>(getActivity(), "theflarenewscache.txt");
                        try {
                            newsCacher.appendOrWriteCache(newsList);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        if (error != null) {
                            Snackbar snackbar = Snackbar
                                    .make(getActivity().findViewById(android.R.id.content), "Something went wrong.", Snackbar.LENGTH_INDEFINITE)
                                    .setAction("Retry", new View.OnClickListener() {
                                        @Override
                                        public void onClick(View view) {
                                            loadNews();
                                        }
                                    });
                            View sbView = snackbar.getView();
                            sbView.setBackgroundColor(ContextCompat.getColor(getActivity(), R.color.maroon));
                            snackbar.show();
                        }
                    }
                }
        );

        MySingleton.getInstance(getActivity()).addToRequestQueue(stringRequest);
    }

    @Override
    public void onContactSelected(News news) {
        Toast.makeText(getContext(), "Selected: " + news.getTitle() + ", " + news.getAuthor(), Toast.LENGTH_LONG).show();
    }

    private void loadCachedNews() {
        FileCacher<ArrayList<News>> newsCacher = new FileCacher<>(getActivity(), "theflarenewscache.txt");
            if(newsCacher.hasCache()){
                try {
                    ArrayList<News> cachedNewsList = newsCacher.readCache();
                    final NewsArticleAdapter adapter = new NewsArticleAdapter(getActivity(), cachedNewsList, listener);
                    //rvNewsArticle.setAdapter(adapter);
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (ClassCastException e){
                    e.printStackTrace();
                }
            }
    }

    // Method to manually check connection status
    private void checkConnection() {
        boolean isConnected = isConnected();
        snackInternet(isConnected);
    }

    private void snackInternet(boolean isConnected) {
      if (isConnected) {
          Snackbar snackbar = Snackbar.make(getActivity().findViewById(android.R.id.content), "Connected to the Internet!", Snackbar.LENGTH_SHORT);
          View sbView = snackbar.getView();
          sbView.setBackgroundColor(ContextCompat.getColor(getActivity(), R.color.primaryDarkColor));
          snackbar.show();
          loadNews();
      }
      else {
          Snackbar snackbar = Snackbar
                  .make(getActivity().findViewById(android.R.id.content), "You seem to be offline.", Snackbar.LENGTH_INDEFINITE)
                  .setAction("Retry", new View.OnClickListener() {
                      @Override
                      public void onClick(View view) {
                          loadNews();
                      }
                  });
          View sbView = snackbar.getView();
          sbView.setBackgroundColor(ContextCompat.getColor(getActivity(), R.color.maroon));
          snackbar.show();
          loadCachedNews();
      }
    }

    @Override
    public void onResume() {
        super.onResume();

        // register connection status listener
        TheFlare.getInstance().setConnectivityListener(this);
    }

    /**
     * Callback will be triggered when there is change in
     * network connection
     */
    @Override
    public void onNetworkConnectionChanged(boolean isConnected) {
        snackInternet(isConnected);
    }

}

NewsArticleAdapter.Java

public class NewsArticleAdapter extends RecyclerView.Adapter<NewsArticleAdapter.NewsViewHolder> implements Filterable {

    private Context context;
    public ArrayList<News> news, filteredNews;
    private NewsArticleAdapterListener listener;

    //ViewHolder class
    public class NewsViewHolder extends RecyclerView.ViewHolder{

        public CardView cvNews;
        public TextView tvId;
        public ImageView ivImage;
        public TextView tvTitle;
        public TextView tvPubDate;

        public NewsViewHolder(View itemView) {
            super(itemView);
            cvNews = (CardView)itemView.findViewById(R.id.cvNews);
            tvId = (TextView) itemView.findViewById(R.id.tvId);
            ivImage = (ImageView)itemView.findViewById(R.id.ivImageUrl);
            tvTitle = (TextView)itemView.findViewById(R.id.tvTitle);
            tvPubDate = (TextView)itemView.findViewById(R.id.tvPubDate);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // send selected news article in callback
                    listener.onContactSelected(filteredNews.get(getAdapterPosition()));
                }
            });

        }
    }

    public NewsArticleAdapter(Context context, ArrayList<News> news, NewsArticleAdapterListener listener){
        this.context = context;
        this.news = news;
        this.filteredNews = news;
        this.listener = listener;
    }

    @Override
    public NewsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View view = inflater.from(parent.getContext())
                .inflate(R.layout.news_cardview_layout, parent, false);

        NewsViewHolder newsViewHolder = new NewsViewHolder(view);
        return newsViewHolder;
    }

    @Override
    public void onBindViewHolder(NewsViewHolder holder, int position) {
        final News selectedNews = news.get(position);
        holder.tvId.setText(String.valueOf(selectedNews.id));
        holder.tvTitle.setText(selectedNews.title);
        holder.tvPubDate.setText(selectedNews.publicationDate);

        String ip = IPAddress.ipaddress;
        String fullUrl = "http://" + ip + "/theflare/admin/img/newsbanner/" + selectedNews.imgBanner;

        Picasso.with(context)
                .load(fullUrl)
                .placeholder(R.drawable.bannerplaceholder)
                .error(android.R.drawable.stat_notify_error)
                .into(holder.ivImage);

        holder.ivImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick (View view) {
                Intent intent = new Intent(context, NewsDetailActivity.class);
                intent.putExtra("news", selectedNews);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(intent);
            }
        });

    }

    @Override
    public int getItemCount() {
        if(news != null){
            return news.size();
        }
        return 0;
    }

    @Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence charSequence) {
                String charString = charSequence.toString();
                if (charString.isEmpty()) {
                    filteredNews = news;
                } else {
                    ArrayList<News> filteredList = new ArrayList<>();
                    for (News row : news) {

                        // name match condition. this might differ depending on your requirement
                        // here we are looking for name or phone number match
                        if ( (row.getTitle().toLowerCase().contains(charString.toLowerCase())) ||
                                (row.getCategory().toLowerCase().contains(charString.toLowerCase())) ||
                                (row.getContent().toLowerCase().contains(charString.toLowerCase())) ||
                                (row.getAuthor().toLowerCase().contains(charString.toLowerCase())) ) {
                            filteredList.add(row);
                        }
                    }

                    filteredNews = filteredList;
                }

                FilterResults filterResults = new FilterResults();
                filterResults.values = filteredNews;
                return filterResults;
            }

            @Override
            protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
                filteredNews = (ArrayList<News>) filterResults.values;
                notifyDataSetChanged();
            }
        };
    }

    public interface NewsArticleAdapterListener {
        void onContactSelected(News news);
    }

}

main_page.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.cvsuimus.theflare.MainPageActivity">

    <item
        android:id="@+id/action_search"
        android:icon="@android:drawable/ic_menu_search"
        android:orderInCategory="100"
        android:title="@android:string/search_go"
        app:actionViewClass="android.support.v7.widget.SearchView"
        app:showAsAction="always" />
</menu>

fragment_recent_news.xml

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.cvsuimus.theflare.RecentNewsFragment">

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipeRefresh"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rvNewsArticle"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical" />

    </android.support.v4.widget.SwipeRefreshLayout>

</android.support.design.widget.CoordinatorLayout>

的AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.cvsuimus.theflare">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        android:name=".TheFlare"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher_round"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity
            android:name=".LoginActivity"
            android:label="@string/app_name" />
        <activity
            android:name=".RegisterActivity"
            android:label="Sign Up"
            android:parentActivityName=".LoginActivity" />
        <activity
            android:name=".MainPageActivity"
            android:label="@string/title_activity_main_page"
            android:theme="@style/AppTheme.NoActionBar">
            <meta-data
                android:name="android.app.searchable"
                android:resource="@xml/searchable"
                android:value=".activities.MainPageActivity" />
            <intent-filter>
                <action android:name="android.intent.action.SEARCH" />
            </intent-filter>
        </activity>
        <activity
            android:name=".StartupActivity"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".AboutUsActivity"
            android:label="@string/title_activity_about_us"
            android:parentActivityName=".MainPageActivity" />
        <activity
            android:name=".ResetPasswordActivity"
            android:label="@string/reset_pw"
            android:parentActivityName=".MainPageActivity" />
        <activity
            android:name=".NewsDetailActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:parentActivityName=".MainPageActivity"
            android:theme="@style/AppTheme.NoActionBar" />

        <receiver
            android:name=".ConnectivityReceiver"
            android:enabled="true">
            <intent-filter>
                <action
                    android:name="android.net.conn.CONNECTIVITY_CHANGE"
                    tools:ignore="BatteryLife" />
            </intent-filter>
        </receiver>

        <activity
            android:name=".TopicCommentsActivity"></activity>
    </application>

</manifest>

我还在proguard-rules.pro文件中添加了这一行:

-keep class android.support.v7.widget.SearchView { *; }

我也遵循了这个答案,不幸的是,我没有成功: https://stackoverflow.com/a/18942838/8586704

我错过了什么或者我的代码有问题吗?感谢。

1 个答案:

答案 0 :(得分:2)

只需更改您的getItemCount()代码,它就会对您有用。您正在使用filteredNews进行操作,因此您必须在所有地方使用filteredNews而不是news

 @Override
public int getItemCount() {
    if(filteredList != null){
        return filteredList.size();
    }
    return 0;
}

并在你的onBindViewHolder中:(使用filteredList代替news

 @Override
public void onBindViewHolder(NewsViewHolder holder, int position) {
    final News selectedNews = filteredList.get(position);
    holder.tvId.setText(String.valueOf(selectedNews.id));
    holder.tvTitle.setText(selectedNews.title);
    holder.tvPubDate.setText(selectedNews.publicationDate);

    String ip = IPAddress.ipaddress;
    String fullUrl = "http://" + ip + "/theflare/admin/img/newsbanner/" + selectedNews.imgBanner;

    Picasso.with(context)
            .load(fullUrl)
            .placeholder(R.drawable.bannerplaceholder)
            .error(android.R.drawable.stat_notify_error)
            .into(holder.ivImage);

    holder.ivImage.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick (View view) {
            Intent intent = new Intent(context, NewsDetailActivity.class);
            intent.putExtra("news", selectedNews);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent);
        }
    });

}