Фильтр SearchView работает по назначению, но извлекает позицию старого элемента в RecyclerView во фрагменте - PullRequest
0 голосов
/ 21 февраля 2020

Последние несколько дней я искал решение моей проблемы, но, похоже, все другие подобные проблемы имеют проблему с onClickListener, или их решение не предназначено для рассматриваемой проблемы. Похоже, что onClickListeners, который я использую в своем проекте, работает так, как задумано, только SearchView - нет.

Мой стартовый список состоит из некоторых элементов и их ингредиентов / материалов, и все работает по назначению. , При фильтрации с помощью SearchView в новом отфильтрованном списке отображаются правильные названия элементов, но неправильные ингредиенты. Буквально старая позиция до фильтрации, и я не могу отладить, откуда возникла проблема.

MainAdapter.java

public class MainAdapter extends RecyclerView.Adapter<MainAdapter.ViewHolder> implements Filterable {
    private List<Item> mItemCard;
    private List<Item> mItemCardFull; //Needed for the getFilter() method

    static class ViewHolder extends RecyclerView.ViewHolder {


        // region Variables (Item Name, Icon, Expand Layout & Expand button)
        ImageView mItemIcon;
        TextView mItemName;
        ImageButton mExpandButton;
        // etc...

        // region Variables (Ingredients' Icons, Names & Amount)
        ImageView mIngredient_1_Icon;
        TextView mIngredient_1_Name;
        TextView mIngredient_1_amountTV;
        float mIngredient_1_amount;
        // etc...

        // region misc Variables
        boolean _isFirstTime = true; // Used in initialization & crafting method switch
        EditText editFactoryAmount;
        ImageView factoryAmountIcon;
        Button factoryAmountButton;
        // etc...


        ViewHolder(@NonNull View itemView) {
            super(itemView);
            mItemIcon = itemView.findViewById(R.id.itemIcon);
            mItemName = itemView.findViewById(R.id.itemName);
            mIngredient_1_Icon = itemView.findViewById(R.id.ingredient_1_icon);
            mIngredient_1_Name = itemView.findViewById(R.id.ingredient_1_name);
            mIngredient_1_amountTV = itemView.findViewById(R.id.ingredient_1_amount);
        // etc...
        }
    }

    public MainAdapter(List<Item> itemCard) {
        this.mItemCard = itemCard;
        this.mItemCardFull = new ArrayList<>(itemCard);
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
        final ViewHolder holder = new ViewHolder(v);
        return holder;
    }

    @Override
    public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {
        final Item currentItem = mItemCard.get(position);

        final float craftingTime = currentItem.getCraftingTime();
        final float outputAmount = currentItem.getOutputAmount();

        holder.mItemIcon.setImageResource(currentItem.getItemIcon());
        holder.mItemName.setText(currentItem.getItemName());
        holder.mExpandButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                boolean show = toggleLayout(!currentItem.isExpanded(), view, holder.mExpandedLayout);
                currentItem.setExpanded(show);
            }
        });

        // region Textwatchers
        // region Editor Listeners
        /* Editor Listener: When actionDone is clicked on the soft keyboard, clear focus from editText */

        // other random methods included in the project which arent relevant
        // ......
    }

    /* Expands or collapses depending on item state */
    private boolean toggleLayout(boolean isExpanded, View v, ConstraintLayout expandedLayout) {
        Animations.toggleArrow(v, isExpanded);
        if (isExpanded) {
            Animations.expand(expandedLayout);
        } else {
            Animations.collapse(expandedLayout);
        }
        return isExpanded;
    }

    @Override
    public int getItemViewType(int position) {
        return position;
    }

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

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

    /*~~~~~~~~~~~~~~~~~~~~~~~~~~ Search Filter Setup ~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    @Override
    public Filter getFilter() {
        return itemFilter;
    }

    private Filter itemFilter = new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            List<Item> filteredList = new ArrayList<>();

            if (constraint == null || constraint.length() == 0) {
                filteredList.addAll(mItemCardFull);
            } else {
                String filterPattern = constraint.toString().toLowerCase().trim();

                for (Item item : mItemCardFull) {
                    if (item.getItemName().toLowerCase().contains(filterPattern)) {
                        filteredList.add(item);
                    }
                }
            }

            FilterResults results = new FilterResults();
            results.values = filteredList;
            return results;
        }

        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            mItemCard.clear();
            mItemCard.addAll((List) results.values);
            notifyDataSetChanged();
        }
    };
}

IntermediatesFragment.java

public class IntermediatesFragment extends Fragment {

    private RecyclerView mRecyclerView;
    private MainAdapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;


    public IntermediatesFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View thisFragment = inflater.inflate(R.layout.fragment_layout, container, false);
        setHasOptionsMenu(true);

        mRecyclerView = thisFragment.findViewById(R.id.recyclerView);
        mRecyclerView.setHasFixedSize(true);
        mLayoutManager = new LinearLayoutManager(getContext());


        createItemList();
        mRecyclerView.setLayoutManager(mLayoutManager);
        mRecyclerView.setAdapter(mAdapter);
        return thisFragment;
    }

    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);

        MenuItem searchItem = menu.findItem(R.id.action_search);
        SearchView searchView = (SearchView) searchItem.getActionView();

        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                mAdapter.getFilter().filter(newText);
                return false;
            }
        });
    }

    private void createItemList() {
        ArrayList<Item> itemList = new ArrayList<>();
        // region Intermediates Items
        // etc items ...

        mAdapter = new MainAdapter(itemList);
    }
}

Проблема, похоже, исчезнет go, если я включу "setIsRecyclable(false)" в Viewholder, но тогда производительность будет на ужасных телефонах низкого уровня, и карты будут уничтожены / сброшены при выходе из вида, который не желаемое поведение.

Редактировать с полным onBindViewHolder:

@Override
    public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {
        final Item currentItem = mItemCard.get(position);

        final float craftingTime = currentItem.getCraftingTime();
        final float outputAmount = currentItem.getOutputAmount();

        holder.mItemIcon.setImageResource(currentItem.getItemIcon());
        holder.mItemName.setText(currentItem.getItemName());
        holder.mExpandButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                boolean show = toggleLayout(!currentItem.isExpanded(), view, holder.mExpandedLayout);
                currentItem.setExpanded(show);
            }
        });

        // region Ingredients' Icons, Names & Amounts
        holder.mIngredient_1_Icon.setImageResource(currentItem.getIngredientIcon_1());
        holder.mIngredient_2_Icon.setImageResource(currentItem.getIngredientIcon_2());
        holder.mIngredient_3_Icon.setImageResource(currentItem.getIngredientIcon_3());
        holder.mIngredient_4_Icon.setImageResource(currentItem.getIngredientIcon_4());
        holder.mIngredient_5_Icon.setImageResource(currentItem.getIngredientIcon_5());
        holder.mIngredient_6_Icon.setImageResource(currentItem.getIngredientIcon_6());

        holder.mIngredient_1_Name.setText(currentItem.getIngredientName_1());
        holder.mIngredient_2_Name.setText(currentItem.getIngredientName_2());
        holder.mIngredient_3_Name.setText(currentItem.getIngredientName_3());
        holder.mIngredient_4_Name.setText(currentItem.getIngredientName_4());
        holder.mIngredient_5_Name.setText(currentItem.getIngredientName_5());
        holder.mIngredient_6_Name.setText(currentItem.getIngredientName_6());

        holder.mIngredient_1_amountTV.setText(String.valueOf(currentItem.getIngredientAmount_1()));
        holder.mIngredient_2_amountTV.setText(String.valueOf(currentItem.getIngredientAmount_2()));
        holder.mIngredient_3_amountTV.setText(String.valueOf(currentItem.getIngredientAmount_3()));
        holder.mIngredient_4_amountTV.setText(String.valueOf(currentItem.getIngredientAmount_4()));
        holder.mIngredient_5_amountTV.setText(String.valueOf(currentItem.getIngredientAmount_5()));
        holder.mIngredient_6_amountTV.setText(String.valueOf(currentItem.getIngredientAmount_6()));


        if (currentItem.getIngredientName_1() == R.string.item_null) {
            holder.mIngredient_1_Icon.setVisibility(View.GONE);
            holder.mIngredient_1_Name.setVisibility(View.GONE);
            holder.mIngredient_1_amountTV.setVisibility(View.GONE);\

        // if statements continue for the rest of the 5 ingredients
        // endregion Ingredients' Icons, Names & Amounts

        holder.factoryAmountButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                holder.button_f++;
                float factoryAmount = Float.parseFloat(holder.editFactoryAmount.getText().toString());
                float outputSpeed = Float.parseFloat((holder.editItemOutput.getText().toString()));
                if (currentItem.getCraftingMethod().equals("Assembler")) {
                    switch (holder.button_f) {
                        case 0:
                            holder.factoryAmountIcon.setImageResource(R.drawable.pf_assembling_machine_1);
                            holder.craftingSpeed = 0.50f;
                            if (holder.editItemOutput.isFocused()) {
                                holder.factoryAmount = (outputSpeed * craftingTime) / ((outputAmount * holder.craftingSpeed) * (1 + (holder.speedModuleAmount * holder.speed)) * (1 + (holder.prodModuleAmount * holder.productivity)));
                                holder.editFactoryAmount.setText(String.valueOf(holder.factoryAmount));
                                // region Edit Ingredients' Amount
                                holder.factoryAmount = Float.parseFloat(holder.editFactoryAmount.getText().toString());
                                holder.mIngredient_1_amount = (holder.factoryAmount * holder.craftingSpeed * currentItem.getIngredientAmount_1()) / craftingTime;
                                holder.mIngredient_1_amountTV.setText(String.valueOf(holder.mIngredient_1_amount));

                                holder.mIngredient_2_amount = (holder.factoryAmount * holder.craftingSpeed * currentItem.getIngredientAmount_2()) / craftingTime;
                                holder.mIngredient_2_amountTV.setText(String.valueOf(holder.mIngredient_2_amount));

                                holder.mIngredient_3_amount = (holder.factoryAmount * holder.craftingSpeed * currentItem.getIngredientAmount_3()) / craftingTime;
                                holder.mIngredient_3_amountTV.setText(String.valueOf(holder.mIngredient_3_amount));

                                holder.mIngredient_4_amount = (holder.factoryAmount * holder.craftingSpeed * currentItem.getIngredientAmount_4()) / craftingTime;
                                holder.mIngredient_4_amountTV.setText(String.valueOf(holder.mIngredient_4_amount));

                                holder.mIngredient_5_amount = (holder.factoryAmount * holder.craftingSpeed * currentItem.getIngredientAmount_5()) / craftingTime;
                                holder.mIngredient_5_amountTV.setText(String.valueOf(holder.mIngredient_5_amount));

                                holder.mIngredient_6_amount = (holder.factoryAmount * holder.craftingSpeed * currentItem.getIngredientAmount_6()) / craftingTime;
                                holder.mIngredient_6_amountTV.setText(String.valueOf(holder.mIngredient_6_amount));


            // it goes on in the same pattern depending on other circumstances


        /*~~~~~~~~~~~~ Initialization & Crafting Method Switch ~~~~~~~~~~~~~~~~~~*/
        if (holder.editFactoryAmount.getText().toString().isEmpty() && !holder.editFactoryAmount.isFocused()) {
            holder.editFactoryAmount.setText(String.valueOf(1.0f));
            holder.outputSpeed = ((Float.parseFloat(holder.editFactoryAmount.getText().toString()) * holder.craftingSpeed) * (1 + (holder.speedModuleAmount * holder.speed))) / craftingTime * (1 + (holder.prodModuleAmount * holder.productivity)) * outputAmount;
            holder.editItemOutput.setText(String.valueOf(holder.outputSpeed));
        }

        float factoryAmount = Float.parseFloat(holder.editFactoryAmount.getText().toString());
        switch (currentItem.getCraftingMethod()) {
            case "Assembler":
                // If it's the first time the app starts, set default crafting speed, else keep as it is.
                if (holder._isFirstTime) {
                    holder.craftingSpeed = 0.50f;
                    holder._isFirstTime = false;
                }
                if (holder.craftingSpeed == 0.50f) {
                    holder.factoryAmountIcon.setImageResource(R.drawable.pf_assembling_machine_1);
                    holder.button_f = 0;
                } else if (holder.craftingSpeed == 0.75f) {
                    holder.factoryAmountIcon.setImageResource(R.drawable.pf_assembling_machine_2);
                    holder.button_f = 1;
                } else {
                    holder.factoryAmountIcon.setImageResource(R.drawable.pf_assembling_machine_3);
                }
                holder.outputSpeed = ((factoryAmount * holder.craftingSpeed) * (1 + (holder.speedModuleAmount * holder.speed))) / craftingTime * (1 + (holder.prodModuleAmount * holder.productivity)) * outputAmount;
                holder.editItemOutput.setText(String.valueOf(holder.outputSpeed));
                // region Edit Ingredients' Amount
                holder.factoryAmount = Float.parseFloat(holder.editFactoryAmount.getText().toString());
                holder.mIngredient_1_amount = (holder.factoryAmount * holder.craftingSpeed * currentItem.getIngredientAmount_1()) / craftingTime;
                holder.mIngredient_1_amountTV.setText(String.valueOf(holder.mIngredient_1_amount));

                holder.mIngredient_2_amount = (holder.factoryAmount * holder.craftingSpeed * currentItem.getIngredientAmount_2()) / craftingTime;
                holder.mIngredient_2_amountTV.setText(String.valueOf(holder.mIngredient_2_amount));

                holder.mIngredient_3_amount = (holder.factoryAmount * holder.craftingSpeed * currentItem.getIngredientAmount_3()) / craftingTime;
                holder.mIngredient_3_amountTV.setText(String.valueOf(holder.mIngredient_3_amount));

                holder.mIngredient_4_amount = (holder.factoryAmount * holder.craftingSpeed * currentItem.getIngredientAmount_4()) / craftingTime;
                holder.mIngredient_4_amountTV.setText(String.valueOf(holder.mIngredient_4_amount));

                holder.mIngredient_5_amount = (holder.factoryAmount * holder.craftingSpeed * currentItem.getIngredientAmount_5()) / craftingTime;
                holder.mIngredient_5_amountTV.setText(String.valueOf(holder.mIngredient_5_amount));

                holder.mIngredient_6_amount = (holder.factoryAmount * holder.craftingSpeed * currentItem.getIngredientAmount_6()) / craftingTime;
                holder.mIngredient_6_amountTV.setText(String.valueOf(holder.mIngredient_6_amount));
                // endregion Edit Ingredients' Amount
                break;
            case "Smelter":
            // thing happening here and etc for all the other cases
        }
    }

Ответы [ 3 ]

0 голосов
/ 21 февраля 2020

Передача отфильтрованного списка адаптеру решит проблему.

0 голосов
/ 21 февраля 2020

Трудно сказать точно , что здесь не так без полной картинки, но ...

Вы держите много состояния в вашем ViewHolder, например, в первый раз количество ингредиента, et c. Вы действительно хотите, чтобы только держали там View с, потому что их можно непредсказуемо поменять местами между элементами. Вот почему это работает, когда вы говорите, что они не подлежат вторичной переработке - это больше не делает.

Вы также устанавливаете вещи в состояние УДАЛЕНО, но не ВИДИМО, если они должны быть показаны. Это проблема для утилизации:

holder.mIngredient_1_Icon.setVisibility(View.GONE);

Ваша кнопка прослушивания onClick изменяет состояние в держателе - это плохо, потому что держатели будут меняться местами между элементами, и, возможно, отскочить. Вы действительно хотите все это состояние в вашем Item классе. Как только вы это сделаете, и ваш RecyclerView станет односторонним преобразованием из Item s в onBindViewHolder, который все настраивает, вам будет гораздо легче рассуждать.

0 голосов
/ 21 февраля 2020

Можете ли вы попробовать это

class SectionRecyclerViewAdapter(
    var sectionModelArrayList: ArrayList<SectionModel>,
    val context: Activity
) : RecyclerView.Adapter<SectionRecyclerViewAdapter.MyViewHolder>(), Filterable {
    private val mSectionList: List<SectionModel>? = sectionModelArrayList

    override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): MyViewHolder {
        val view = LayoutInflater.from(viewGroup.context).inflate(R.layout.section_custom_row_layout, viewGroup, false)
        return MyViewHolder(view)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val sectionModel = sectionModelArrayList[position]
        holder.tvStations.text = sectionModel.stations
        holder.ivLogo.setImageDrawable(sectionModel.logo)
        holder.ivArrow.setImageResource(R.mipmap.ic_arrow_dark)
        holder.view.setOnClickListener {
            when (position) {
                0 -> {
                    val intent = Intent(context, AllUserActivity::class.java)
                    context.startActivity(intent)
                    context.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left)
                }
                1 -> {
                    Toast.makeText(context, "Nothing to Show", Toast.LENGTH_SHORT).show()
                }


            }
        }
    }

    override fun getItemCount(): Int {
        return sectionModelArrayList.size
    }

    inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val ivLogo: ImageView
        val tvStations: TextView
        val ivArrow: ImageView
        val view: View


        init {
            ivLogo = itemView.findViewById(R.id.ivLogo)
            tvStations = itemView.findViewById(R.id.tvStations)
            ivArrow = itemView.findViewById(R.id.ivArrow)
            view = itemView
        }
    }

    override fun getFilter(): Filter {
        return object : Filter() {
            protected override fun performFiltering(charSequence: CharSequence): FilterResults {
                val charString = charSequence.toString()
                if (charString.isEmpty()) {
                    sectionModelArrayList = mSectionList as ArrayList<SectionModel>
                } else {
                    var filteredList = ArrayList<SectionModel>()
                    if (mSectionList != null) {
                        for (row in mSectionList) {

                            // name match condition. this might differ depending on your requirement
                            // here we are looking for name or phone number match
                            if (row.stations.toLowerCase().contains(charString.toLowerCase())) {
                                filteredList.add(row)
                            }
                        }
                    }
                    sectionModelArrayList = filteredList
                }

                val filterResults = FilterResults()
                filterResults.values = sectionModelArrayList
                return filterResults
            }

            override fun publishResults(charSequence: CharSequence, filterResults: FilterResults) {
                sectionModelArrayList = filterResults.values as ArrayList<SectionModel>
                notifyDataSetChanged()
            }
        }
    }
}
...