Я в основном пытаюсь реализовать функцию поиска для моего приложения, которая показывает некоторые ресурсы. Когда пользователь вводит текстовый запрос, представление Recycler прокручивается вниз до первого появления запроса. Есть 2 кнопки со стрелками, которые перемещают пользователя назад и вперед между следующим и предыдущим случаями.
Теперь здесь возникает проблема . Я хочу, чтобы представление элемента, которое соответствует запросу, не только прокручивалось, но и выделялось. Когда я пытаюсь поместить анимацию в любой вид элемента, который не отображается на экране, я получаю NPE , что понятно, поскольку представление Recycler создает только часть всего списка. У меня вопрос, как мне обойти это?
Один из способов (я думал) - сначала прокрутить вниз, а затем применить анимацию. Но, как выясняется, анимация начинает происходить первой (, может быть, прокрутка происходит в фоновом потоке? ) и сразу же выбрасывает NPE. Как мне это решить?
Полный код фрагмента, о котором я говорю, приведен ниже, а код анимации - последний (, где проблема ):
package com.coffeetech.kittycatch;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.content.Intent;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.TransitionDrawable;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.cardview.widget.CardView;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
public class InventoryFragment extends Fragment {
//GLOBAL VARIABLES
File file;
RecyclerView recyclerView;
FoodAdapter foodAdapter;
FloatingActionButton add_button, search_button;
FoodViewModel foodViewModel;
public static final int REQUEST_NEW_FOOD = 25;
public static final int REQUEST_OLD_FOOD = 21;
//FOOD LIST
private List<Food> foodList = new ArrayList<>();
Food food;
CardView search_space;
EditText search_text;
ImageButton upSearchButton, downSearchButton;
private ArrayList<Integer> searchedPositions = new ArrayList<>();
String substring;
int iterator;
LinearLayoutManager linearLayoutManager;
ObjectAnimator animator;
public InventoryFragment() {}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//GETTING THE FOOD VIEW MODEL
foodViewModel = new ViewModelProvider(getActivity(),ViewModelProvider.AndroidViewModelFactory.getInstance(getActivity().getApplication())).get(FoodViewModel.class); //TODO:HERE
foodViewModel.getFoods().observe(getViewLifecycleOwner(), new Observer<List<Food>>() {
@Override
public void onChanged(List<Food> foods) {
foodList = foods;
foodAdapter.setFoods(foods);
foodAdapter.notifyDataSetChanged(); //TODO:MAKE THIS BETTER
}
});
recyclerView.setAdapter(foodAdapter);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_inventory, container, false);
search_button = v.findViewById(R.id.search_foods_button);
search_space = v.findViewById(R.id.search_space);
search_text = v.findViewById(R.id.search_text);
upSearchButton = v.findViewById(R.id.up_search_button);
downSearchButton = v.findViewById(R.id.down_search_button);
search_space.setVisibility(View.GONE);
//GETTING THE FOOD VIEW MODEL
//foodAdapter = new FoodAdapter();
recyclerView=v.findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
linearLayoutManager = new LinearLayoutManager(getContext());
recyclerView.setLayoutManager(linearLayoutManager);
//setting up the Add button
add_button=v.findViewById(R.id.add_button);
add_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) { //for new food addition to list
Intent intent = new Intent(getActivity(),FoodEditorActivity.class);
startActivityForResult(intent,REQUEST_NEW_FOOD);
}
});
//BELOW CODE FOR SEARCH BUTTON
search_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(search_space.getVisibility() == View.GONE){
search_space.setVisibility(View.VISIBLE);
}else{
search_space.setVisibility(View.GONE);
linearLayoutManager.scrollToPositionWithOffset(0,0);
}
}
});
search_text.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) {
substring = s.toString().trim().toLowerCase();
searchedPositions.clear();
if(substring!=""){
iterator=0;
for(Food f : foodList){
if(f.getName().toLowerCase().contains(substring)){
searchedPositions.add(iterator);
}
iterator+=1;
}
}
iterator=0;
if(searchedPositions.size() !=0){
//linearLayoutManager.scrollToPositionWithOffset(searchedPositions.get(0),0);
findItemAndHighlight(searchedPositions.get(0));
}else{
linearLayoutManager.scrollToPositionWithOffset(0,0);
Toast.makeText(getContext(),"Not found",Toast.LENGTH_SHORT).show();
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
downSearchButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(!substring.trim().isEmpty()){
if(iterator == searchedPositions.size()-1) { //IF LAST OF SEARCHED LIST IS REACHED
iterator=0;
}else{ //IF THERE ARE STILL SOME ITEMS LEFT
iterator+=1;
}
//linearLayoutManager.scrollToPositionWithOffset(searchedPositions.get(iterator),0);
findItemAndHighlight(searchedPositions.get(iterator));
//recyclerView.smoothScrollToPosition(searchedPositions.get(iterator));
}
}
});
upSearchButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(!substring.trim().isEmpty()){
if(iterator == 0) { //IF LAST OF SEARCHED LIST IS REACHED
iterator = searchedPositions.size()-1;
}else{ //IF THERE ARE STILL SOME ITEMS LEFT
iterator-=1;
}
//linearLayoutManager.scrollToPositionWithOffset(searchedPositions.get(iterator),0);
findItemAndHighlight(searchedPositions.get(iterator));
//recyclerView.smoothScrollToPosition(searchedPositions.get(iterator));
}
}
});
foodAdapter = new FoodAdapter(1);
foodAdapter.setOnFoodcardClickListener(new FoodAdapter.OnFoodcardClickListener() {
@Override
public void deleteFood(int position) { //code that deletes current food
if(position!=RecyclerView.NO_POSITION){
//DELETE IMAGE
if(foodList.get(position).getPhoto() != "null" && foodList.get(position).getThumbnail() != "null"){
file = new File (foodList.get(position).getPhoto());
if (file.exists()){file.delete();}
file = new File (foodList.get(position).getThumbnail());
if (file.exists()){file.delete();}
}
//delete from database
foodViewModel.delete(foodList.get(position));
}
}
@Override
public void onEdit(int position) { //code that runs the edit of each food
if(position!=RecyclerView.NO_POSITION) {
Intent intent = new Intent(getActivity(),FoodEditorActivity.class);
//PUT THE DATA CORRECTLY
intent.putExtra(FoodEditorActivity.EXTRA_NAME, foodList.get(position).getName());
intent.putExtra(FoodEditorActivity.EXTRA_ID, foodList.get(position).getID());
intent.putExtra(FoodEditorActivity.EXTRA_TYPE, foodList.get(position).getType());
intent.putExtra(FoodEditorActivity.EXTRA_THUMBNAIL, foodList.get(position).getThumbnail());
intent.putExtra(FoodEditorActivity.EXTRA_PHOTO, foodList.get(position).getPhoto());
intent.putExtra(FoodEditorActivity.EXTRA_QUANTITY, foodList.get(position).getQuantity());
intent.putExtra(FoodEditorActivity.EXTRA_MIN_QUANTITY, foodList.get(position).getMin_quantity());
startActivityForResult(intent,REQUEST_OLD_FOOD);
}
}
@Override
public void increaseValue(int position) {
if(position!=RecyclerView.NO_POSITION){
foodList.get(position).increase();
foodList.get(position).setDate(new Date());
foodViewModel.update(foodList.get(position));
}
}
@Override
public void decreaseValue(int position) {
if(position!=RecyclerView.NO_POSITION){
foodList.get(position).decrease();
foodList.get(position).setDate(new Date());
foodViewModel.update(foodList.get(position));
}
}
@Override
public void setSeekBar(int position,int progress) {
if(position!=RecyclerView.NO_POSITION) {
foodList.get(position).setQuantity(progress);
foodList.get(position).setDate(new Date());
foodViewModel.update(foodList.get(position));
Toast.makeText(getContext(),"Quantity set to "+progress+"%",Toast.LENGTH_SHORT).show();
}
}
@Override
public void setCheckBoxTrue() {
}
@Override
public void setCheckBoxFalse() {
}
});
return v;
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == REQUEST_NEW_FOOD && resultCode == Activity.RESULT_OK){
food = new Food();
food.setID(data.getIntExtra(FoodEditorActivity.EXTRA_ID,-1));
food.setName(data.getStringExtra(FoodEditorActivity.EXTRA_NAME));
food.setType(data.getIntExtra(FoodEditorActivity.EXTRA_TYPE,0));
food.setQuantity(data.getIntExtra(FoodEditorActivity.EXTRA_QUANTITY,0));
food.setMin_quantity(data.getIntExtra(FoodEditorActivity.EXTRA_MIN_QUANTITY,0));
food.setPhoto(data.getStringExtra(FoodEditorActivity.EXTRA_PHOTO));
food.setThumbnail(data.getStringExtra(FoodEditorActivity.EXTRA_THUMBNAIL));
food.setDate(new Date());
foodViewModel.insert(food);
Toast.makeText(getContext(),"Saved",Toast.LENGTH_SHORT).show();
}else if(requestCode == REQUEST_OLD_FOOD && resultCode == Activity.RESULT_OK){
food = new Food();
food.setID(data.getIntExtra(FoodEditorActivity.EXTRA_ID,-1));
food.setName(data.getStringExtra(FoodEditorActivity.EXTRA_NAME));
food.setType(data.getIntExtra(FoodEditorActivity.EXTRA_TYPE,0));
food.setQuantity(data.getIntExtra(FoodEditorActivity.EXTRA_QUANTITY,0));
food.setMin_quantity(data.getIntExtra(FoodEditorActivity.EXTRA_MIN_QUANTITY,0));
food.setPhoto(data.getStringExtra(FoodEditorActivity.EXTRA_PHOTO));
food.setThumbnail(data.getStringExtra(FoodEditorActivity.EXTRA_THUMBNAIL));
food.setDate(new Date());
foodViewModel.update(food);
Toast.makeText(getContext(),"Saved",Toast.LENGTH_SHORT).show();
}else if(resultCode == FoodEditorActivity.PHOTO_DELETED){
food = new Food();
food.setID(data.getIntExtra(FoodEditorActivity.EXTRA_ID,-1));
food.setName(data.getStringExtra(FoodEditorActivity.EXTRA_NAME));
food.setType(data.getIntExtra(FoodEditorActivity.EXTRA_TYPE,0));
food.setQuantity(data.getIntExtra(FoodEditorActivity.EXTRA_QUANTITY,0));
food.setMin_quantity(data.getIntExtra(FoodEditorActivity.EXTRA_MIN_QUANTITY,0));
food.setPhoto(data.getStringExtra(FoodEditorActivity.EXTRA_PHOTO));
food.setThumbnail(data.getStringExtra(FoodEditorActivity.EXTRA_THUMBNAIL));
food.setDate(new Date()); //TODO: REMOVE THIS AFTER WRITING CODE TO FIND FOOD BY ID
foodViewModel.update(food);
}
}
void findItemAndHighlight(int position){
linearLayoutManager.scrollToPositionWithOffset(position,0);
ColorDrawable[] color = {new ColorDrawable(getResources().getColor(R.color.colorAlternateTwo)),new ColorDrawable(getResources().getColor(R.color.elevatedBackgroundColor))};
TransitionDrawable transitionDrawable = new TransitionDrawable(color);
//recyclerView.getChildAt(position).setBackground(transitionDrawable);
recyclerView.findViewHolderForAdapterPosition(position).itemView.setBackground(transitionDrawable);
transitionDrawable.startTransition(1000);
}
}