mardi 18 mai 2021

How to setup a custom recyclerview with result counter?

I´m working on a project where I´m trying to add an options filter. For more individual results I started to implement an options filter which gives a user the ability to choose their prefered options and got individual results displayed in a paged recyclerview. First I started to use relative layouts with checkboxes inside and a text view which shows the number of results a user gets for a certain option.

Cause there were to many views in my layout I switched to a custom recycler list view which displays the option items. I gave the user the ability to choose different music genres but cause there are to many genres out there I started to implement a sub recyclerview list for the items of the genre recyclerview list.

The items and sub items should display the number of results which a user got if they choose that option but I got stuck with 500 lines boilerplate and to many web request to get the number of results for each option.

Everything is working fine till I tried to set also counter for the sublist. I didn´t find any better solutions for my problem. I got different models for my recycler and sublist recycler and don´t know how to successfull implement a counter for all categorys and sub categorys.

How it looks like

When I´m trying to implement a counter for the sub categorys the app crashs with the error

 java.lang.IndexOutOfBoundsException: Index: 4, Size: 4
        at java.util.ArrayList.get(ArrayList.java:437)
        at 
de.n.UI.Fragments.Config.Config_Filter.lambda$filtercount$14$Config_Filter(Config_Filter.java:305)`

I set the counter for the sub categorys on that way:

viewModel.getFilteredData(lat, lng, firebaseAuth.getUid(), Art, new ArrayList<>(Collections.singletonList(getString(R.string.Dance))), Extra, privat, age, radius, starttime, endtime, StartPrice, EndPrice, Date).observe(getViewLifecycleOwner(), integer -> {
        if (integer != null) {
            filterItemsMainstream.set(0, new FilterItem(filterItems.get(0).state, filterItems.get(0).ItemName, integer));
            filterSubItems.set(0, new FilterSubItem(filterItemsMainstream, filterSubItems.get(0).state, filterSubItems.get(0).ItemName, integer));
            filterListRecycler.notifyItemChanged(0);
        }
    });
    viewModel.getFilteredData(lat, lng, firebaseAuth.getUid(), Art, new ArrayList<>(Collections.singletonList(getString(R.string.Disco))), Extra, privat, age, radius, starttime, endtime, StartPrice, EndPrice, Date).observe(getViewLifecycleOwner(), integer -> {
        if (integer != null) {
            filterItemsMainstream.set(1, new FilterItem(filterItems.get(1).state, filterItems.get(1).ItemName, integer));
            filterSubItems.set(0, new FilterSubItem(filterItemsMainstream, filterSubItems.get(0).state, filterSubItems.get(0).ItemName, integer));
            filterListRecycler.notifyItemChanged(0);
        }
    });
    viewModel.getFilteredData(lat, lng, firebaseAuth.getUid(), Art, new ArrayList<>(Collections.singletonList(getString(R.string.Pop))), Extra, privat, age, radius, starttime, endtime, StartPrice, EndPrice, Date).observe(getViewLifecycleOwner(), integer -> {
        if (integer != null) {
            filterItemsMainstream.set(2, new FilterItem(filterItems.get(2).state, filterItems.get(2).ItemName, integer));
            filterSubItems.set(0, new FilterSubItem(filterItemsMainstream, filterSubItems.get(0).state, filterSubItems.get(0).ItemName, integer));
            filterListRecycler.notifyItemChanged(0);
        }
    });
    viewModel.getFilteredData(lat, lng, firebaseAuth.getUid(), Art, new ArrayList<>(Collections.singletonList(getString(R.string.RNB))), Extra, privat, age, radius, starttime, endtime, StartPrice, EndPrice, Date).observe(getViewLifecycleOwner(), integer -> {
        if (integer != null) {
            filterItemsMainstream.set(3, new FilterItem(filterItems.get(3).state, filterItems.get(3).ItemName, integer));
            filterSubItems.set(0, new FilterSubItem(filterItemsMainstream, filterSubItems.get(0).state, filterSubItems.get(0).ItemName, integer));
            filterListRecycler.notifyItemChanged(0);
        }
    });
    viewModel.getFilteredData(lat, lng, firebaseAuth.getUid(), Art, new ArrayList<>(Collections.singletonList(getString(R.string.Club))), Extra, privat, age, radius, starttime, endtime, StartPrice, EndPrice, Date).observe(getViewLifecycleOwner(), integer -> {
        if (integer != null) {
            filterItemsMainstream.set(4, new FilterItem(filterItems.get(4).state, filterItems.get(4).ItemName, integer));
            filterSubItems.set(0, new FilterSubItem(filterItemsMainstream, filterSubItems.get(0).state, filterSubItems.get(0).ItemName, integer));
            filterListRecycler.notifyItemChanged(0);
        }
    });
    viewModel.getFilteredData(lat, lng, firebaseAuth.getUid(), Art, new ArrayList<>(Collections.singletonList(getString(R.string.Retro))), Extra, privat, age, radius, starttime, endtime, StartPrice, EndPrice, Date).observe(getViewLifecycleOwner(), integer -> {
        if (integer != null) {
            filterItemsMainstream.set(5, new FilterItem(filterItems.get(5).state, filterItems.get(5).ItemName, integer));
            filterSubItems.set(0, new FilterSubItem(filterItemsMainstream, filterSubItems.get(0).state, filterSubItems.get(0).ItemName, integer));
            filterListRecycler.notifyItemChanged(0);
        }
    });
    viewModel.getFilteredData(lat, lng, firebaseAuth.getUid(), Art, new ArrayList<>(Collections.singletonList(getString(R.string.Schlager))), Extra, privat, age, radius, starttime, endtime, StartPrice, EndPrice, Date).observe(getViewLifecycleOwner(), integer -> {
        if (integer != null) {
            filterItemsMainstream.set(6, new FilterItem(filterItems.get(6).state, filterItems.get(6).ItemName, integer));
            filterSubItems.set(0, new FilterSubItem(filterItemsMainstream, filterSubItems.get(0).state, filterSubItems.get(0).ItemName, integer));
            filterListRecycler.notifyItemChanged(0);
        }
    });

But everytime I started it once again its shows me a out of bounds at a new line I think its because to many web requests or to much new arrays or something like that.

A short version of my code without the web requests for the sublist:

public class Config_Filter extends DialogFragment implements FilterItemInterface {


FilterItem filterItem;
ArrayList<FilterItem> filterItems, filterItemsMainstream, filterItemsHipHop, filterItemsTechno, filterItemsRock, filterItemsSprache, filterItemsSonstig;
ArrayList<FilterSubItem> filterSubItems;
Bundle filterbundle;
FilterStates filterStates;
String adress;
MainViewModel viewModel;
LocationViewModel locationViewModel;
private final FirebaseAuth firebaseAuth = FirebaseAuth.getInstance();
ArrayList<String> Genre, Art, Extra;
int radius = 25, age, privat, StartPrice, EndPrice;
String starttime, endtime;
FilterListAdapter filterListRecycler;

double lat, lng;
public String Date;
public static final String TAG = "config_dialog";
FragmentConfigBinding binding;

@Override
public void onStart() {
    super.onStart();
    //dialog settings
    Dialog dialog = getDialog();
    if (dialog != null) {
        int width = ViewGroup.MarginLayoutParams.MATCH_PARENT;
        int height = ViewGroup.LayoutParams.MATCH_PARENT;
        dialog.getWindow().setLayout(width, height);
        ColorDrawable back = new ColorDrawable(Color.TRANSPARENT);
        InsetDrawable inset = new InsetDrawable(back, 0, 60, 0, 0);
        dialog.getWindow().setBackgroundDrawable(inset);
        dialog.getWindow().setWindowAnimations(R.style.AppTheme_Slide);
    }
}

@Override
public void onCreate(Bundle savedInstanceState) {
    setStyle(DialogFragment.STYLE_NO_FRAME, R.style.AppTheme_FullScreenDialog);
    super.onCreate(savedInstanceState);
    //current adress of user and lat and lng to get location oriented results
    lat = requireArguments().getDouble(Config.KEY_EMP_LAT);
    lng = requireArguments().getDouble(Config.KEY_EMP_LNG);
}

@SuppressLint({"SetTextI18n", "DefaultLocale"})
@Override
public View onCreateView(@NotNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);
    binding = FragmentConfigBinding.inflate(inflater, container, false);
    //setup ArrayLists
    Genre = new ArrayList<>();
    Art = new ArrayList<>();
    Extra = new ArrayList<>();
    filterItems = new ArrayList<>();
    filterItemsMainstream = new ArrayList<>();
    filterItemsHipHop = new ArrayList<>();
    filterItemsTechno = new ArrayList<>();
    filterItemsRock = new ArrayList<>();
    filterItemsSprache = new ArrayList<>();
    filterItemsSonstig = new ArrayList<>();
    filterSubItems = new ArrayList<>();
    filterartitems();
    expandLayout();
    return binding.getRoot();
}

public void filterartitems() {
    filterSubItems.add(new FilterSubItem(filterItemsMainstream, false, getString(R.string.Mainstream), 0));
    filterSubItems.add(new FilterSubItem(filterItemsHipHop, false, getString(R.string.Hip_Hop), 0));
    filterSubItems.add(new FilterSubItem(filterItemsTechno, false, getString(R.string.Electro), 0));
    filterSubItems.add(new FilterSubItem(filterItemsRock, false, getString(R.string.Rock), 0));
    filterSubItems.add(new FilterSubItem(filterItemsSprache, false, getString(R.string.Language), 0));
    filterSubItems.add(new FilterSubItem(filterItemsSonstig, false, getString(R.string.Sonstiges), 0));

    filterItems.add(new FilterItem(false, getString(R.string.Bar), 0));
    filterItems.add(new FilterItem(false, getString(R.string.Club), 0));
    filterItems.add(new FilterItem(false, getString(R.string.Festival), 0));
    filterItems.add(new FilterItem(false, getString(R.string.OpenAir), 0));

    filterItemsMainstream.add(new FilterItem(false, getString(R.string.Dance), 0));
    filterItemsMainstream.add(new FilterItem(false, getString(R.string.Disco), 0));
    filterItemsMainstream.add(new FilterItem(false, getString(R.string.Pop), 0));
    filterItemsMainstream.add(new FilterItem(false, getString(R.string.RNB), 0));
    filterItemsMainstream.add(new FilterItem(false, getString(R.string.Club), 0));
    filterItemsMainstream.add(new FilterItem(false, getString(R.string.Retro), 0));
    filterItemsMainstream.add(new FilterItem(false, getString(R.string.Schlager), 0));

    filterItemsHipHop.add(new FilterItem(false, getString(R.string.Rap), 0));
    filterItemsHipHop.add(new FilterItem(false, getString(R.string.Drill), 0));
    filterItemsHipHop.add(new FilterItem(false, getString(R.string.Trap), 0));
    filterItemsHipHop.add(new FilterItem(false, getString(R.string.CloudRap), 0));
    filterItemsHipHop.add(new FilterItem(false, getString(R.string.Underground), 0));
    filterItemsHipHop.add(new FilterItem(false, getString(R.string.LofiHipHop), 0));

    filterItemsTechno.add(new FilterItem(false, getString(R.string.Techno), 0));
    filterItemsTechno.add(new FilterItem(false, getString(R.string.EuroTechno), 0));
    filterItemsTechno.add(new FilterItem(false, getString(R.string.House), 0));
    filterItemsTechno.add(new FilterItem(false, getString(R.string.Techhouse), 0));
    filterItemsTechno.add(new FilterItem(false, getString(R.string.Fusion), 0));
    filterItemsTechno.add(new FilterItem(false, getString(R.string.Trance), 0));
    filterItemsTechno.add(new FilterItem(false, getString(R.string.Psychadelic), 0));
    filterItemsTechno.add(new FilterItem(false, getString(R.string.Rave), 0));
    filterItemsTechno.add(new FilterItem(false, getString(R.string.Goa), 0));
    filterItemsTechno.add(new FilterItem(false, getString(R.string.DrumandBass), 0));
    filterItemsTechno.add(new FilterItem(false, getString(R.string.Progressive), 0));

    filterItemsRock.add(new FilterItem(false, getString(R.string.RockRoll), 0));
    filterItemsRock.add(new FilterItem(false, getString(R.string.HardRock), 0));
    filterItemsRock.add(new FilterItem(false, getString(R.string.Metal), 0));
    filterItemsRock.add(new FilterItem(false, getString(R.string.DeathMetal), 0));
    filterItemsRock.add(new FilterItem(false, getString(R.string.Punk), 0));
    filterItemsRock.add(new FilterItem(false, getString(R.string.AcidPunk), 0));
    filterItemsRock.add(new FilterItem(false, getString(R.string.GothicRock), 0));
    filterItemsRock.add(new FilterItem(false, getString(R.string.PunkRock), 0));
    filterItemsRock.add(new FilterItem(false, getString(R.string.ClassicRock), 0));
    filterItemsRock.add(new FilterItem(false, getString(R.string.PsychadelicRock), 0));

    filterItemsSprache.add(new FilterItem(false, getString(R.string.Balkan), 0));
    filterItemsSprache.add(new FilterItem(false, getString(R.string.Latin), 0));
    filterItemsSprache.add(new FilterItem(false, getString(R.string.BootyBass), 0));
    filterItemsSprache.add(new FilterItem(false, getString(R.string.Funk), 0));
    filterItemsSprache.add(new FilterItem(false, getString(R.string.Samba), 0));
    filterItemsSprache.add(new FilterItem(false, getString(R.string.Tango), 0));
    filterItemsSprache.add(new FilterItem(false, getString(R.string.Jazz), 0));
    filterItemsSprache.add(new FilterItem(false, getString(R.string.JazzFunk), 0));
    filterItemsSprache.add(new FilterItem(false, getString(R.string.AcidJazz), 0));
    filterItemsSprache.add(new FilterItem(false, getString(R.string.Reggae), 0));
    filterItemsSprache.add(new FilterItem(false, getString(R.string.Folk), 0));
    filterItemsSprache.add(new FilterItem(false, getString(R.string.PopFunk), 0));
    filterItemsSprache.add(new FilterItem(false, getString(R.string.Blues), 0));
    filterItemsSprache.add(new FilterItem(false, getString(R.string.Soul), 0));
    filterItemsSprache.add(new FilterItem(false, getString(R.string.RhythmicSoul), 0));
    filterItemsSprache.add(new FilterItem(false, getString(R.string.Swing), 0));

    filterItemsSonstig.add(new FilterItem(false, getString(R.string.Classical), 0));
    filterItemsSonstig.add(new FilterItem(false, getString(R.string.Oldies), 0));
    filterItemsSonstig.add(new FilterItem(false, getString(R.string.Alternative), 0));
    filterItemsSonstig.add(new FilterItem(false, getString(R.string.Ska), 0));
    filterItemsSonstig.add(new FilterItem(false, getString(R.string.Vocal), 0));
    filterItemsSonstig.add(new FilterItem(false, getString(R.string.InstrumentalPop), 0));
    filterItemsSonstig.add(new FilterItem(false, getString(R.string.Bluegrass), 0));
    filterItemsSonstig.add(new FilterItem(false, getString(R.string.Primus), 0));
    filterItemsSonstig.add(new FilterItem(false, getString(R.string.SlowJam), 0));

    ViewModel();
    recycler();
    RecyclerAdapter();


}

public void ViewModel() {
    viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class);
    locationViewModel = new ViewModelProvider(requireActivity()).get(LocationViewModel.class);
}

public void recycler() {
    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
    linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    binding.expandart.setLayoutManager(new LinearLayoutManager(getContext()));
    binding.expandgenre.setLayoutManager(new LinearLayoutManager(getContext()));

}

public boolean cleanParams() {
    return Art.size() == 0 || Genre.size() == 0 || privat == 0 || age == 0 || radius == 25 || starttime.equals(getString(R.string.start)) || endtime.equals(getString(R.string.end)) || Date.equals(getString(R.string.Datum));
}

public void RecyclerAdapter()
{
    FilterGenreListAdapter filterGenreListAdapter=new FilterGenreListAdapter(requireContext(),filterSubItems,this);
    filterListRecycler=new FilterListAdapter(filterItems,this);
    binding.expandart.setAdapter(filterListRecycler);
    binding.expandgenre.setAdapter(filterGenreListAdapter);
    filtercount();
}

public void expandLayout()
{
    binding.ArtConfig.setOnClickListener(v -> {
        if (binding.expandart.getVisibility()==View.GONE) {
            expand(binding.expandart);
            binding.ArtConfig.setActivated(true);
        }
        else {
            collapse(binding.expandart);
            binding.ArtConfig.setActivated(false);
        }
    });
    binding.GenreConfigNew.setOnClickListener(v -> {
        if (binding.expandgenre.getVisibility()==View.GONE) {
            expand(binding.expandgenre);
            binding.GenreConfigNew.setActivated(true);
        }
        else {
            collapse(binding.expandgenre);
            binding.GenreConfigNew.setActivated(false);
        }
    });
}

@Override
public void getFilterItem(FilterItem filterItem) {
}
public static void expand(final View v) {
    int matchParentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) v.getParent()).getWidth(), View.MeasureSpec.EXACTLY);
    int wrapContentMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    v.measure(matchParentMeasureSpec, wrapContentMeasureSpec);
    final int targetHeight = v.getMeasuredHeight();
    // Older versions of android (pre API 21) cancel animations for views with a height of 0.
    v.getLayoutParams().height = 1;
    v.setVisibility(View.VISIBLE);
    Animation a = new Animation()
    {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            v.getLayoutParams().height = interpolatedTime == 1
                    ? LinearLayout.LayoutParams.WRAP_CONTENT
                    : (int)(targetHeight * interpolatedTime);
            v.requestLayout();
        }
        @Override
        public boolean willChangeBounds() {
            return true;
        }
    };
    // Expansion speed of 1dp/ms
    a.setDuration((int)(targetHeight / v.getContext().getResources().getDisplayMetrics().density));
    v.startAnimation(a);
}
public static void collapse(final View v) {
    final int initialHeight = v.getMeasuredHeight();
    Animation a = new Animation()
    {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            if(interpolatedTime == 1){
                v.setVisibility(View.GONE);
            }else{
                v.getLayoutParams().height = initialHeight - (int)(initialHeight * interpolatedTime);
                v.requestLayout();
            }
        }
        @Override
        public boolean willChangeBounds() {
            return true;
        }
    };
    // Collapse speed of 1dp/ms
    a.setDuration((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density));
    v.startAnimation(a);
}


//count result size for the single options
@SuppressLint("SetTextI18n")
public void filtercount() {
    viewModel.getFilteredData(lat, lng, firebaseAuth.getUid(), new ArrayList<>(Collections.singletonList(getString(R.string.Bar))), Genre, Extra, privat, age, radius, starttime, endtime, StartPrice, EndPrice, Date).observe(getViewLifecycleOwner(), integer -> {
        if (integer != null) {
            filterItems.set(0, new FilterItem(filterItems.get(0).state, filterItems.get(0).ItemName, integer));
            filterListRecycler.notifyItemChanged(0);
        }
    });
    viewModel.getFilteredData(lat, lng, firebaseAuth.getUid(), new ArrayList<>(Collections.singletonList(getString(R.string.Club))), Genre, Extra, privat, age, radius, starttime, endtime, StartPrice, EndPrice, Date).observe(getViewLifecycleOwner(), integer -> {
        if (integer != null) {
            filterItems.set(1, new FilterItem(filterItems.get(1).state, filterItems.get(1).ItemName, integer));
            filterListRecycler.notifyItemChanged(1);
        }
    });
    viewModel.getFilteredData(lat, lng, firebaseAuth.getUid(), new ArrayList<>(Collections.singletonList(getString(R.string.Festival))), Genre, Extra, privat, age, radius, starttime, endtime, StartPrice, EndPrice, Date).observe(getViewLifecycleOwner(), integer -> {
        if (integer != null) {
            filterItems.set(2, new FilterItem(filterItems.get(2).state, filterItems.get(2).ItemName, integer));
            filterListRecycler.notifyItemChanged(2);
        }
    });
    viewModel.getFilteredData(lat, lng, firebaseAuth.getUid(), new ArrayList<>(Collections.singletonList(getString(R.string.OpenAir))), Genre, Extra, privat, age, radius, starttime, endtime, StartPrice, EndPrice, Date).observe(getViewLifecycleOwner(), integer -> {
        if (integer != null) {
            filterItems.set(3, new FilterItem(filterItems.get(3).state, filterItems.get(3).ItemName, integer));
            filterListRecycler.notifyItemChanged(3);
        }
    });
    viewModel.getFilteredData(lat, lng, firebaseAuth.getUid(), Art, new ArrayList<>(Collections.singletonList(getString(R.string.Mainstream))), Extra, privat, age, radius, starttime, endtime, StartPrice, EndPrice, Date).observe(getViewLifecycleOwner(), integer -> {
        if (integer != null) {
            filterSubItems.set(0, new FilterSubItem(filterSubItems.get(0).filterItem, filterSubItems.get(0).state, filterSubItems.get(0).ItemName, integer));
            filterListRecycler.notifyItemChanged(0);
        }
    });
    viewModel.getFilteredData(lat, lng, firebaseAuth.getUid(), Art, new ArrayList<>(Collections.singletonList(getString(R.string.Hip_Hop))), Extra, privat, age, radius, starttime, endtime, StartPrice, EndPrice, Date).observe(getViewLifecycleOwner(), integer -> {
        if (integer != null) {
            filterSubItems.set(1, new FilterSubItem(filterSubItems.get(1).filterItem, filterSubItems.get(1).state, filterSubItems.get(1).ItemName, integer));
            filterListRecycler.notifyItemChanged(1);
        }
    });
    viewModel.getFilteredData(lat, lng, firebaseAuth.getUid(), Art, new ArrayList<>(Collections.singletonList(getString(R.string.Techno))), Extra, privat, age, radius, starttime, endtime, StartPrice, EndPrice, Date).observe(getViewLifecycleOwner(), integer -> {
        if (integer != null) {
            filterSubItems.set(2, new FilterSubItem(filterSubItems.get(2).filterItem, filterSubItems.get(2).state, filterSubItems.get(2).ItemName, integer));
            filterListRecycler.notifyItemChanged(2);
        }
    });
    viewModel.getFilteredData(lat, lng, firebaseAuth.getUid(), Art, new ArrayList<>(Collections.singletonList(getString(R.string.Rock))), Extra, privat, age, radius, starttime, endtime, StartPrice, EndPrice, Date).observe(getViewLifecycleOwner(), integer -> {
        if (integer != null) {
            filterSubItems.set(3, new FilterSubItem(filterSubItems.get(3).filterItem, filterSubItems.get(3).state, filterSubItems.get(3).ItemName, integer));
            filterListRecycler.notifyItemChanged(3);
        }
    });
    viewModel.getFilteredData(lat, lng, firebaseAuth.getUid(), Art, new ArrayList<>(Collections.singletonList(getString(R.string.Sprache))), Extra, privat, age, radius, starttime, endtime, StartPrice, EndPrice, Date).observe(getViewLifecycleOwner(), integer -> {
        if (integer != null) {
            filterSubItems.set(4, new FilterSubItem(filterSubItems.get(4).filterItem, filterSubItems.get(4).state, filterSubItems.get(4).ItemName, integer));
            filterListRecycler.notifyItemChanged(4);
        }
    });
    viewModel.getFilteredData(lat, lng, firebaseAuth.getUid(), Art, new ArrayList<>(Collections.singletonList(getString(R.string.Sonstiges))), Extra, privat, age, radius, starttime, endtime, StartPrice, EndPrice, Date).observe(getViewLifecycleOwner(), integer -> {
        if (integer != null) {
            filterSubItems.set(5, new FilterSubItem(filterSubItems.get(5).filterItem, filterSubItems.get(5).state, filterSubItems.get(5).ItemName, integer));
            filterListRecycler.notifyItemChanged(5);
        }
    });
}
}

For the sublist I got another 400 lines like the filtercount. I would be thankful for any help.




Aucun commentaire:

Enregistrer un commentaire