为什么会发生Android空指针异常错误?

huangapple go评论131阅读模式
英文:

why android null pointer exception error occur?

问题

It seems like you are encountering a java.lang.NullPointerException error in your Java code. The error message indicates that you are trying to invoke a method on a null object reference. Specifically, the error occurs in your HomeFragment.java file, in the onTextChanged method of the TextWatcher when you try to call adapterAd.getFilter().filter(query).

To resolve this issue, you need to make sure that adapterAd is properly initialized before you use it. Here's a modification to your code to ensure adapterAd is initialized in the onViewCreated method:

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    // Initialize the adapter here
    adapterAd = new AdapterAd(mContext, adArrayList);
    binding.adsRv.setAdapter(adapterAd);

    // ... Your other code ...

    binding.searchEt.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            Log.d(TAG, "onTextChanged: Query: " + s);

            try {
                String query = s.toString();
                adapterAd.getFilter().filter(query);
            } catch (Exception e) {
                Log.e(TAG, "onTextChanged: ", e);
            }
        }

        @Override
        public void afterTextChanged(Editable s) {
        }
    });
}

Make sure to initialize adapterAd as shown above, and this should resolve the NullPointerException error you were encountering.

英文:

java.lang.NullPointerException: Attempt to invoke virtual method 'android.widget.Filter com.example.olx_app.AdapterAd.getFilter()' on a null object reference
at com.example.olx_app.HomeFragment$1.onTextChanged(HomeFragment.java:113)

HomeFragment.java

public class HomeFragment extends Fragment {

private FragmentHomeBinding binding;

private static final String TAG="HOME_TAG";

private static final int MAX_DISTANCE_TO_LOAD_ADS_KM =10;

//Context for this fragment class
private Context mContext;

//adArrayList to hold ads list to show in RecycleView
private ArrayList<ModelAd> adArrayList;

//AdapterAd class instance to set to RecycleView to show Ads list
private AdapterAd adapterAd;

//show nearby location
private SharedPreferences locationSp;

private double currentLatitude =0.0;
private double currentLongitude = 0.0;
private String currentAddress ="";

@Override
public void onAttach(@NonNull Context context) {
    mContext = context;
    super.onAttach(context);
}

public HomeFragment() {
    // Required empty public constructor
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    binding = FragmentHomeBinding.inflate(LayoutInflater.from(mContext),container,false);
    return binding.getRoot();
}

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);


    //init the shared Preferences p1. name of SharedPreferences file , p2. mode of SharedPreferences
    locationSp = mContext.getSharedPreferences("LOCATION_SP", Context.MODE_PRIVATE);
    //get saved current Latitude , Longitude ,address from SharedPreferences .
    //In next we will pick these info from map and save in it
    currentLatitude = locationSp.getFloat("CURRENT_LATITUDE",0.0f);
    currentLongitude = locationSp.getFloat("CURRENT_LONGITUDE",0.0f);
    currentAddress = locationSp.getString("CURRENT_ADDRESS","");

    //if current location is not 0
    if(currentLatitude !=0.0 && currentLongitude !=0.0)
    {
        binding.locationTv.setText(currentAddress);
    }

    //call , load categories
    loadCategories();
    //call , load all ads
    loadAds("All");

    binding.searchEt.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            Log.d(TAG, "onTextChanged: Query: "+s);

            try {
               String query = s.toString();
                    adapterAd.getFilter().filter(query);
            }
            catch (Exception e)
            {
                Log.e(TAG, "onTextChanged: ",e);
            }
        }

        @Override
        public void afterTextChanged(Editable s) {

        }
    });

    binding.locationCv.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            Intent intent = new Intent(mContext,LocationPickerActivity.class);
            locationPickerActivityResult.launch(intent);
        }
    });

}


private final ActivityResultLauncher<Intent> locationPickerActivityResult = registerForActivityResult(
        new ActivityResultContracts.StartActivityForResult(),
        new ActivityResultCallback<>() {
            @Override
            public void onActivityResult(ActivityResult result) {
                //check if from map , location is picked or not
                if (result.getResultCode() == Activity.RESULT_OK) {
                    Log.d(TAG, "onActivityResult: RESULT_OK");
                    Intent data = result.getData();

                    if (data != null) {
                        Log.d(TAG, "onActivityResult: Location picked");
                        //get location info from intent
                        currentLatitude = data.getDoubleExtra("latitude", 0.0);
                        currentLongitude = data.getDoubleExtra("longitude", 0.0);
                        currentAddress = data.getStringExtra("address");
                        //save location info to shared preferences to when we launch app next time we don't need to pick again
                        locationSp.edit()
                                .putFloat("CURRENT_LATITUDE", Float.parseFloat("" + currentLatitude))
                                .putFloat("CURRENT_LONGITUDE", Float.parseFloat("" + currentLongitude))
                                .putString("CURRENT_ADDRESS", currentAddress)
                                .apply();
                        //set the picked address
                        binding.locationTv.setText(currentAddress);
                        //after picking address reload all ads again based on newly picked location
                        loadAds("All");
                    }
                } else {
                    Log.d(TAG, "onActivityResult: Cancelled!");
                    Utils.toast(mContext, "Cancelled!");
                }
            }
        }
);

private void loadCategories()
{
    //init categoryArrayList
    ArrayList<ModelCategory> categoryArrayList =  new ArrayList<>();
    //ModelCategory instance to show all products
    ModelCategory modelCategoryAll = new ModelCategory("All" , R.drawable.all);
    categoryArrayList.add(modelCategoryAll);

    //get categories from utils class and add in categoryArrayList
    for(int i=0; i <Utils.categories.length ;i++)
    {
        //hold category from current index
        ModelCategory modelCategory = new ModelCategory(Utils.categories[i], Utils.categoryIcons[i]);
        //add modelCategory to categoryArrayList
        categoryArrayList.add(modelCategory);
    }

    //setup AdapterCategory
    AdapterCategory adapterCategory = new AdapterCategory(mContext, categoryArrayList, new RvListenerCategory() {
        @Override
        public void onCategoryClick(ModelCategory modelCategory) {
            loadAds(modelCategory.getCategory());
        }
    });

    //set adapter to the RecycleView like categoriesRv
    binding.categoriesRv.setAdapter(adapterCategory);
}

private void loadAds(String category) {
Log.d(TAG, "loadAds: Category: "+category);

    /*//init adArrayList before starting adding data into it
    adArrayList = new ArrayList<>();*/

    //Firebase DB listener to load ads based on category & distance
    DatabaseReference ref = FirebaseDatabase.getInstance().getReference("Ads");
    ref.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot snapshot) {
            //clear adArrayList each time starting adding data into it
            adArrayList.clear();


            //load ads list
            for(DataSnapshot ds: snapshot.getChildren()) {
                //prepare modelAd with all data from Firebase DB
                ModelAd modelAd = ds.getValue(ModelAd.class);
                //function call with returned value as distance in km
                assert modelAd != null;
                double distance = calculateDistanceKm(modelAd.getLatitude(), modelAd.getLongitude());
                Log.d(TAG, "onDataChange: distance: " + distance);
                //filter
                if (category.equals("All")) {
                    //category All is selected , now check distance if is <= required e.x. 10km then show
                    if (distance <= MAX_DISTANCE_TO_LOAD_ADS_KM) {
                        //distance is <= required e.x. 10km Add to list
                        adArrayList.add(modelAd);
                    }
                } else {
                    //some category is selected e.x. mobile
                    if (modelAd.getCategory().equals(category)) {
                        if (distance <= MAX_DISTANCE_TO_LOAD_ADS_KM) {
                            //distance is <= required e.x. 10km Add to list
                            adArrayList.add(modelAd);
                        }
                    }
                }
            }

           adapterAd = new AdapterAd(mContext,adArrayList);
            binding.adsRv.setAdapter(adapterAd);
        }

        @Override
        public void onCancelled(@NonNull DatabaseError error) {

        }
    });
}

private double calculateDistanceKm(double adLatitude, double adLongitude)
{
    Log.d(TAG, "calculateDistanceKm: currentLatitude: "+ currentLatitude);
    Log.d(TAG, "calculateDistanceKm: currentLongitude: "+currentLongitude);
    Log.d(TAG, "calculateDistanceKm: adLatitude: "+adLatitude);
    Log.d(TAG, "calculateDistanceKm: adLongitude: "+adLongitude);

    //source Location i.e. user's current location
    Location startPoint = new Location(LocationManager.NETWORK_PROVIDER);
    startPoint.setLatitude(currentLatitude);
    startPoint.setLongitude(currentLongitude);

    //Destination Location i.e. Ad's Location
    Location endPoint = new Location(LocationManager.NETWORK_PROVIDER);
    endPoint.setLatitude(adLatitude);
    endPoint.setLongitude(adLongitude);

    //calculate distance
    double distanceInMeters = startPoint.distanceTo(endPoint);
    double distanceInKm = distanceInMeters / 1000 ;

    return distanceInKm;
}

}

AdapterAd.java

public class AdapterAd extends RecyclerView.Adapter<AdapterAd.HolderAd> implements Filterable
{
private RowAdBinding binding;

private static final String TAG = &quot;ADAPTER_AD_TAG&quot;;

private FirebaseAuth firebaseAuth;

private Context context;

//adArrayList the list of the Ads
public ArrayList&lt;ModelAd&gt; adArrayList;

private ArrayList&lt;ModelAd&gt; filterList;

private FilterAd filter;

public AdapterAd(Context context, ArrayList&lt;ModelAd&gt; adArrayList) {
    this.context = context;
    this.adArrayList = adArrayList;
    this.filterList = adArrayList;

    firebaseAuth = FirebaseAuth.getInstance();
}

@NonNull
@Override
public HolderAd onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    //inflate the row_ad.xml
    binding = RowAdBinding.inflate(LayoutInflater.from(context),parent,false);
    return new HolderAd(binding.getRoot());
}

@Override
public void onBindViewHolder(@NonNull HolderAd holder, int position) {
    //get data from particular position of list and set to UI
    ModelAd modelAd = adArrayList.get(position);

    String title = modelAd.getTitle();
    String description = modelAd.getDescription();
    String address = modelAd.getAddress();
    String price = modelAd.getPrice();
    String condition = modelAd.getCondition();

    //function call : load first image from available images of Ad
    loadAdFirstImage(modelAd,holder);

    //set data to UI views of row_ad.xml
    holder.titleTv.setText(title);
    holder.descriptionTv.setText(description);
    holder.addressTv.setText(address);
    holder.priceTv.setText(price);
    holder.conditionTv.setText(condition);

}

private void loadAdFirstImage(ModelAd modelAd, HolderAd holder) {
    Log.d(TAG, &quot;loadAdFirstImage: &quot;);
    //load first image from available images of Ad

    //adId to get image of it
    String adId = modelAd.getId();
    DatabaseReference reference = FirebaseDatabase.getInstance().getReference(&quot;Ads&quot;);
    reference.child(adId).child(&quot;Images&quot;).limitToFirst(1)
            .addValueEventListener(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot)
                {
                    //it return 1 image as we have used query .limitToFirst(1)
                    for(DataSnapshot ds:snapshot.getChildren())
                    {
                        //get Url of image
                        String imageUrl =&quot;&quot;+ds.child(&quot;imageUrl&quot;).getValue();
                        Log.d(TAG, &quot;onDataChange: imageUrl: &quot;+imageUrl);
                        //set image to Image view
                        try
                        {
                            Glide.with(context)
                                    .load(imageUrl)
                                    .placeholder(R.drawable.ic_image_gray)
                                    .into(holder.imageIv);
                        }catch (Exception e)
                        {
                            Log.e(TAG, &quot;onDataChange: &quot;,e );
                        }
                    }
                }

                @Override
                public void onCancelled(@NonNull DatabaseError error) {

                }
            });
}

@Override
public int getItemCount() {
    return adArrayList.size();
}

@Override
public Filter getFilter() {
    //init the filter obj only if it is null
    if(filter == null)
    {
        filter = new FilterAd(this,adArrayList);
    }
    return filter;
}


class HolderAd extends RecyclerView.ViewHolder
{
    //UI views of the row_ad.xml
    ShapeableImageView imageIv;
    TextView titleTv, descriptionTv , addressTv , conditionTv , priceTv;
    ImageButton favBtn;

    public HolderAd(@NonNull View itemView) {
        super(itemView);

        //init UI views of the row_ad.xml
        imageIv = binding.imageIv;
        titleTv = binding.titleTv;
        descriptionTv = binding.descriptionTv;
        favBtn = binding.favBtn;
        addressTv = binding.addressTv;
         conditionTv = binding.conditionTv;
        priceTv = binding.priceTv;
    }
}

}

FilterAd.java

public class FilterAd extends Filter
{
public AdapterAd adapter;

public ArrayList&lt;ModelAd&gt; filterList;

public FilterAd(AdapterAd adapter, ArrayList&lt;ModelAd&gt; filterList) {
    this.adapter = adapter;
    this.filterList = filterList;
}

@Override
protected FilterResults performFiltering(CharSequence constraint) {
    //perform filter based on what user type

    FilterResults results = new FilterResults();

    if(constraint != null &amp;&amp; constraint.length() &gt; 0)
    {
        //search query is not null and not empty , we can perform filter
        //convert query to upper case to make search not case sensitive
        constraint = constraint.toString().toUpperCase();

        //hold the filtered list of ads based on user searched query
        ArrayList&lt;ModelAd&gt; filteredModels = new ArrayList&lt;&gt;();
        for(int i=0; i&lt;filterList.size(); i++)
        {
            //Ad filter based on Brand,Category,Condition,Title . if any of these matches add it to the filterModels list
            if(filterList.get(i).getBrand().toUpperCase().contains(constraint) ||
                    filterList.get(i).getCategory().toUpperCase().contains(constraint) ||
                    filterList.get(i).getCondition().toUpperCase().contains(constraint) ||
                    filterList.get(i).getTitle().toUpperCase().contains(constraint)) {
                //Filter matched add to filterModels list
                filteredModels.add(filterList.get(i));
            }
        }
        results.count = filteredModels.size();
        results.values = filteredModels;
    }
    else
    {
        //the search query is either null or empty . we can&#39;t perform filter . return full list
        results.count = filterList.size();
        results.values = filterList;
    }
    return results;
}

@SuppressLint(&quot;NotifyDataSetChanged&quot;)
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
    //publish the filtered result

    adapter.adArrayList = (ArrayList&lt;ModelAd&gt;) results.values;
   adapter.notifyDataSetChanged();
}

}

// Declare the AdapterAd reference globally in your HomeFragment class
private AdapterAd adapter;

// ...

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);

// Initialize the adapter here
adapter = new AdapterAd(mContext, adArrayList);
binding.adsRv.setAdapter(adapter);

// ... Your other code ...

binding.searchEt.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        Log.d(TAG, &quot;onTextChanged: Query: &quot; + s);

        try {
            query = s.toString();
            // Now you can safely call getFilter() on the initialized adapter
            adapter.getFilter().filter(query);
        } catch (Exception e) {
            Log.e(TAG, &quot;onTextChanged: &quot;, e);
        }
    }

    @Override
    public void afterTextChanged(Editable s) {
    }
});

}

private void loadAds(String category) {

ref.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        // ...
        adArrayList.clear();
        // ...
        adapter.notifyDataSetChanged(); // Notify the adapter that the data has changed
    }

    @Override
    public void onCancelled(@NonNull DatabaseError error) {
    }
});

}

I can try this but not solved problem

答案1

得分: 1

因为您正在访问 adapterAd,但它可能尚未初始化,因为您是在 Firebase 数据库的成功回调中初始化它。

方法1:在访问适配器之前进行null检查

try {
    String query = s.toString();
    if(adapterAd != null){
        adapterAd.getFilter().filter(query);
    }
}

方法2:在使用 Firebase 获取数据后,使用空列表初始化适配器,然后更新适配器。

// 在 onCreateView() 中初始化适配器
adArrayList = new ArrayList<>();
adapterAd = new AdapterAd(mContext, adArrayList);
binding.adsRv.setAdapter(adapterAd);

// 在成功回调中通知适配器列表已更改

ref.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        // 每次开始向其中添加数据之前清除 adArrayList
        adArrayList.clear();

        // 加载广告列表
        for(DataSnapshot ds: snapshot.getChildren()) {
            // 您的代码
        }
        
        // 使用以下代码来更新适配器
        adapterAd.notifyDataSetChanged();
    }
}
英文:

It is because you are accessing adapterAd, but it may not have been initialized, as you are initializing it in your success callback of Firebase database.

Approach 1 : Do a null check before accessing your adapter

try {
    String query = s.toString();
    if(adapterAd != null){
        adapterAd.getFilter().filter(query);
    }
}

Approach 2: Initialize your adapter with empty list and then update the adapter after you get data from Firebase.

// initialize the adapter in your onCreateView()
adArrayList = new ArrayList&lt;&gt;();
adapterAd = new AdapterAd(mContext,adArrayList);
binding.adsRv.setAdapter(adapterAd);

// notify that adapter that list is changed in your success callback

ref.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot snapshot) {
            //clear adArrayList each time starting adding data into it
            adArrayList.clear();


            //load ads list
            for(DataSnapshot ds: snapshot.getChildren()) {
            // your code
            }
           // use this to update your adapter
           adapterAd.notifyDataSetChanged();
 }

huangapple
  • 本文由 发表于 2023年8月11日 00:00:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/76877473.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定