itabdullah
itabdullah

Reputation: 607

Android viewpager with recyclerview fragments laggs (low performance)

I have a viewpager with each of its fragments containing a recyclerview. However it takes a while to respond to click when viewpager fragments get created and also recyclerview scrolling lags. I return POSITION_NONE in the getItemPosition method in order for my fragments to get recreated when the user changes the text size/style.

  1. Problem 1: Viewpager fragments creation and swiping lags
  2. Problem 2: RecyclerView scrolling also lags
  3. Problem 3. The above two problems are more severe with low RAM devices, however with high RAM / CPU core devices, the response it acceptable but not up to the mark.

Here is my code: ViewPagerAdapter:

    private class ViewPagerAdapter extends FragmentStatePagerAdapter {

        public ViewPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {

            subTitlesFragment = new SubTitlesFragment();
            Bundle bundle = new Bundle();
            if (isRtl) {
                int positionRev;
                positionRev = position - (subTitlesList.size() - 1);
                bundle.putInt("view_pager_pos", Math.abs(positionRev));
            } else {
                bundle.putInt("view_pager_pos", position);
            }
            bundle.putString("type", type);
            bundle.putString("query", query);
            subTitlesFragment.setArguments(bundle);
            return subTitlesFragment;
        }

        @Override
        public int getItemPosition(Object object) {
            return POSITION_NONE;
        }

        @Override
        public int getCount() {
            return subTitlesList.size();
        }

    }

MyRecyclerViewAdapter:

    public RecyclerAdapterDetails(Context context, ArrayList<DataView> data, String query, String fav, String language,
                                  Typeface typefaceAR, Typeface typefaceTran,
                                  String textFontSize, String textFontSizeTran,
                                  boolean diacriticsON) {

        sharedPref = context.getSharedPreferences("sPreferencesFile", Context.MODE_PRIVATE);
        inflater = LayoutInflater.from(context);
        this.dataViews = data;
        this.context = context;
        this.query = query;
        this.fav = fav;
        this.language = language;
        this.typefaceAR = typefaceAR;
        this.typefaceTran = typefaceTran;
        this.textFontSize = textFontSize;
        this.textFontSizeTran = textFontSizeTran;
        this.diacriticsON = diacriticsON;
        currentNightMode = context.getResources().getConfiguration().uiMode
                & Configuration.UI_MODE_NIGHT_MASK;
    }

    @Override
    public int getItemViewType(int position) {
        int viewType = 0;
        if (position == 0 && fav == null) {
            viewType = 1;
        }
        if (position == dataViews.size()) {
            viewType = 2;
        }
        return viewType;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view;

        if (viewType == 0) {
            view = inflater.inflate(R.layout.single_item_recycler_details, parent, false);
            return new MyViewHolder(view);
        } else if (viewType == 1) {
            view = inflater.inflate(R.layout.single_item_title_details, parent, false);
            return new TitleViewHolder(view);
        } else {
            view = inflater.inflate(R.layout.blank_item, parent, false);
            return new BlankItemViewHolder(view);
        }
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

        if (holder.getItemViewType() == 0) {
            processMyViewHolder(holder, position);
        } else if (holder.getItemViewType() == 1) {
            TitleViewHolder titleViewHolder = (TitleViewHolder) holder;

            if (language.equals("en")) {
                titleViewHolder.duaTitle.setText(dataViews.get(1).getSubTitleEN());
                  titleViewHolder.duaTitle.setText(dataViews.get(1).getTranslation_Sub());
            } else {
                titleViewHolder.duaTitle.setText(dataViews.get(1).getSubTitleAR());
titleViewHolder.duaTitle.setText(dataViews.get(1).getSub_title());
            }

            if (language.equals("ar")) {
                    titleViewHolder.duaTitle.setTypeface(typefaceAR);
            } else {

                    titleViewHolder.duaTitle.setTypeface(typefaceTran);
            }

            if (language.equals("ar")) {
                titleViewHolder.duaTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, Float.parseFloat(textFontSize));
            } else {
                titleViewHolder.duaTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, Float.parseFloat(textFontSizeTran));
            }
            if (query != null) {
                String title = titleViewHolder.duaTitle.getText().toString().toLowerCase();
                int ofe3 = title.indexOf(query.toLowerCase(), 0);
                Spannable spannable3 = new SpannableString(titleViewHolder.duaTitle.getText());

                for (int ofs = 0; ofs < title.length() && ofe3 != -1; ofs = ofe3 + 1) {
                    ofe3 = title.indexOf(query, ofs);
                    if (ofe3 == -1)
                        break;
                    else {
                        if (currentNightMode == Configuration.UI_MODE_NIGHT_NO) {
                            spannable3.setSpan(new BackgroundColorSpan(0xFFFFFF00), ofe3, ofe3 + query.length(),
                                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                        } else {
                            spannable3.setSpan(new BackgroundColorSpan(0xffcccccc), ofe3, ofe3 + query.length(),
                                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                        }
                        titleViewHolder.duaTitle.setText(spannable3, TextView.BufferType.SPANNABLE);
                    }
                }
            }

        } else {
            Log.w(TAG, "BlankItemViewHolder called...");
            BlankItemViewHolder blankItemViewHolder = (BlankItemViewHolder) holder;
            if (language.equals("ar")) {
                try {
                    blankItemViewHolder.footnote.setText(dataViews.get(1).getFootnoteAR());
                } catch (Exception e) {

                }
            } else {
                try {
                    blankItemViewHolder.footnote.setText(dataViews.get(1).getFootnoteEN());
                } catch (Exception e) {

                }
            }
        }
    }

    public void processMyViewHolder(RecyclerView.ViewHolder viewHolder, int position) {

        MyViewHolder holder = (MyViewHolder) viewHolder;
        if (currentNightMode == Configuration.UI_MODE_NIGHT_NO) {
            holder.cardView.setCardBackgroundColor(Color.WHITE);
        }
        DataView current = dataViews.get(position);
            holder.contentsAr.setTypeface(typefaceAR);

        if (diacriticsON) {
            holder.contentsAr.setText(current.getContentsAR().trim());
        } else {
            Matcher matcher = pattern.matcher(current.getContentsAR().trim());
            matcher.reset();
            String contentsDiaRemoved = matcher.replaceAll("");
            holder.contentsAr.setText(contentsDiaRemoved);
        }

        if (language.equals("ar")) {
            holder.contentsTran.setVisibility(View.GONE);
            if (current.getDaleelAR().trim().length() > 0) {
                holder.divider.setVisibility(View.VISIBLE);
                holder.daleel.setVisibility(View.VISIBLE);
                if (diacriticsON) {
                    holder.daleel.setText(current.getDaleelAR().trim());
                } else {
                    if (language.equals("ar")) {
                        holder.contentsTran.setVisibility(View.GONE);
                    } else {
                        if (current.getContentsEN() != null && !current.getContentsEN().trim().isEmpty()) {
                            holder.contentsTran.setText(current.getContentsEN().trim());
                        } else {
                            holder.contentsTran.setVisibility(View.GONE);
                        }
                    }

                    Matcher matcher = pattern.matcher(current.getDaleelAR().trim());
                    matcher.reset();
                    String daleelText = matcher.replaceAll("");
                    holder.daleel.setText(daleelText);
                }
            } else {
                holder.divider.setVisibility(View.GONE);
                holder.daleel.setVisibility(View.GONE);
            }
        } else {
            if (current.getContentsEN() != null && !current.getContentsEN().trim().isEmpty()) {
                holder.contentsTran.setText(current.getContentsEN().trim());
            } else {
                holder.contentsTran.setVisibility(View.GONE);
            }
            if (current.getDaleelEN() != null && !current.getDaleelEN().trim().isEmpty()) {
                holder.daleel.setText(current.getDaleelEN().trim());
            } else {
                if (current.getDaleelAR().trim().length() > 0) {
                    holder.divider.setVisibility(View.VISIBLE);
                    holder.daleel.setVisibility(View.VISIBLE);
                    holder.daleel.setText(current.getDaleelAR().trim());
                } else {
                    holder.divider.setVisibility(View.GONE);
                    holder.daleel.setVisibility(View.GONE);
                }
            }
        }

        if (language.equals("ar")) {
            holder.contentsTran.setVisibility(View.GONE);
            holder.daleel.setTypeface(typefaceAR);
        } else {
                holder.contentsTran.setTypeface(typefaceTran);
                holder.daleel.setTypeface(typefaceTran);
        }

        holder.contentsAr.setTextSize(TypedValue.COMPLEX_UNIT_SP, Float.parseFloat(textFontSize));

        if (language.equals("ar")) {
            float newSize = Float.parseFloat(textFontSize) - 2;
            holder.daleel.setTextSize(TypedValue.COMPLEX_UNIT_SP, newSize);
        } else {
            holder.contentsTran.setTextSize(TypedValue.COMPLEX_UNIT_SP, Float.parseFloat(textFontSizeTran));
            holder.daleel.setTextSize(TypedValue.COMPLEX_UNIT_SP, Float.parseFloat(textFontSizeTran));
        }

        if (query != null) {
            String daleel = holder.daleel.getText().toString().toLowerCase();
            int ofe = daleel.indexOf(query.toLowerCase(), 0);
            Spannable spannable = new SpannableString(holder.daleel.getText());

            for (int ofs = 0; ofs < daleel.length() && ofe != -1; ofs = ofe + 1) {
                ofe = daleel.indexOf(query, ofs);
                if (ofe == -1)
                    break;
                else {
                    if (currentNightMode == Configuration.UI_MODE_NIGHT_NO) {
                        spannable.setSpan(new BackgroundColorSpan(0xFFFFFF00), ofe, ofe + query.length(),
                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    } else {
                        spannable.setSpan(new BackgroundColorSpan(0xffcccccc), ofe, ofe + query.length(),
                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    }
                    holder.daleel.setText(spannable, TextView.BufferType.SPANNABLE);
                }
            }

            String contents = holder.contentsAr.getText().toString().toLowerCase();
            int ofe2 = contents.indexOf(query.toLowerCase(), 0);
            Spannable spannable2 = new SpannableString(holder.contentsAr.getText());

            for (int ofs = 0; ofs < contents.length() && ofe2 != -1; ofs = ofe2 + 1) {
                ofe2 = contents.indexOf(query, ofs);
                if (ofe2 == -1)
                    break;
                else {
                    if (currentNightMode == Configuration.UI_MODE_NIGHT_NO) {
                        spannable2.setSpan(new BackgroundColorSpan(0xFFFFFF00), ofe2, ofe2 + query.length(),
                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    } else {
                        spannable2.setSpan(new BackgroundColorSpan(0xffcccccc), ofe2, ofe2 + query.length(),
                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    }
                    holder.contentsAr.setText(spannable2, TextView.BufferType.SPANNABLE);
                }
            }
        }

        if (sharedPref.getBoolean("bookmark" + dataViews.get(position).getHadithNo(), false)) {
            holder.bookmark.setImageResource(R.drawable.ic_bookmark_grey_600_24dp);
        } else {
            holder.bookmark.setImageResource(R.drawable.ic_bookmark_border_grey_600_24dp);
        }

    }    

    @Override
    public int getItemCount() {
        return dataViews.size() + 1;
    }

    public void setClickListener(ClickListener clickListener) {
        this.clickListener = clickListener;
    }

    public interface ClickListener {
        void itemClicked(int position);
    }

    private class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {

        TextView contentsAr, contentsTran;
        TextView daleel;
        ImageView bookmark, share;
        View divider;
        CardView cardView;

        public MyViewHolder(View itemView) {
            super(itemView);
            contentsAr = (TextView) itemView.findViewById(R.id.contents_ar);
            contentsTran = (TextView) itemView.findViewById(R.id.contents_tran);
            daleel = (TextView) itemView.findViewById(R.id.daleel);
            bookmark = (ImageView) itemView.findViewById(R.id.bookmark);
            share = (ImageView) itemView.findViewById(R.id.share);
            divider = itemView.findViewById(R.id.divider);
            cardView = (CardView) itemView.findViewById(R.id.card_view1);
            bookmark.setOnClickListener(this);
            bookmark.setOnLongClickListener(this);
            share.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            if (v.getId() == bookmark.getId()) {
                if (fav == null) {
                    modifySharedPref();
                }
                if (fav != null) {
                    Toast.makeText(context, R.string.long_press, Toast.LENGTH_SHORT).show();
                }
            }
            if (v.getId() == share.getId()) {
                clickListener.itemClicked(getLayoutPosition());
            }
        }

        @Override
        public boolean onLongClick(View v) {
            if (fav != null) {
                bookmark.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
                modifySharedPref();
                removeAt(getLayoutPosition());
            }
            return true;
        }

        void modifySharedPref() {
            SharedPreferences.Editor editor = sharedPref.edit();
            if (!sharedPref.getBoolean("bookmark" + dataViews.get(getLayoutPosition()).getHadithNo(), false)) {
                bookmark.setImageResource(R.drawable.ic_bookmark_grey_600_24dp);
                editor.putBoolean("bookmark" + dataViews.get(getLayoutPosition()).getHadithNo(), true);
            } else {
                bookmark.setImageResource(R.drawable.ic_bookmark_border_grey_600_24dp);
                editor.putBoolean("bookmark" + dataViews.get(getLayoutPosition()).getHadithNo(), false);
            }
            editor.apply();
        }
    }

    private class TitleViewHolder extends RecyclerView.ViewHolder {
        TextView duaTitle;

        public TitleViewHolder(View itemView) {
            super(itemView);
            duaTitle = (TextView) itemView.findViewById(R.id.dua_title);
        }
    }

    private class BlankItemViewHolder extends RecyclerView.ViewHolder {
        LinearLayout blankItem;
        TextView footnote;

        public BlankItemViewHolder(View itemView) {
            super(itemView);
            blankItem = (LinearLayout) itemView.findViewById(R.id.blank_item);
            footnote = (TextView) itemView.findViewById(R.id.foot_note);
        }
    }

    void removeAt(int position) {
        dataViews.remove(position);
        notifyItemRemoved(position);
        notifyItemRangeChanged(position, dataViews.size());
    }

SubTitlesFragment:

   public SubTitlesFragment() {

    }

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

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

        View view = inflater.inflate(R.layout.fragment_pager, container, false);
        recyclerView = (RecyclerView) view.findViewById(R.id.recycler_sub_titles);

        return view;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(TAG, "onCreate - SubTitlesFragment");
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        dataViewArrayList.clear();
        dataViewTempList.clear();
        config = getActivity().getResources().getConfiguration();
        sharedPref = getActivity().getSharedPreferences("sPreferencesFile", Context.MODE_PRIVATE);
        Bundle extras = getArguments();
        if (extras != null) {
            type = extras.getString("type");
            if (type != null) {
                if (type.equals("activity_search")) {
                    query = extras.getString("query");
                } else {
                    query = null;
                }
            } else {
                query = null;
            }
            position = extras.getInt("view_pager_pos");

            Type listOfObjects = new TypeToken<ArrayList<DataView>>() {
            }.getType();
            Gson gson = new Gson();
            String json = sharedPref.getString("gson_dataViews", "");

            if (!json.equals("")) {
                dataViewArrayList = gson.fromJson(json, listOfObjects);
            }

            Type listOfObjects2 = new TypeToken<ArrayList<SingleItemTitle>>() {
            }.getType();
            Gson gson2 = new Gson();
            String json2 = sharedPref.getString("subTitlesList", "");

            if (!json2.equals("")) {
                subTitlesList = gson2.fromJson(json2, listOfObjects2);
            }


            boolean groupEnd = false;
            boolean fetched = false;

            for (int i = 0; !groupEnd && i < dataViewArrayList.size(); i++) {
                if (LocaleHelper.getLanguage(context).equals("ar")) {
                    if (dataViewArrayList.get(i).getSubTitleAR().equals(subTitlesList.get(position).duaSubTitleAR)) {
                        dataViewTempList.add(dataViewArrayList.get(i));
                        fetched = true;
                    } else if (fetched) {
                        groupEnd = true;
                    }
                } else {
                    if (dataViewArrayList.get(i).getSubTitleEN().equals(subTitlesList.get(position).duaSubTitleEN)) {
                        dataViewTempList.add(dataViewArrayList.get(i));
                        fetched = true;
                    } else if (fetched) {
                        groupEnd = true;
                    }
                }
            }

        }

        if (savedInstanceState != null) {
            notFirstTime = true;
        }
        setAdapterData();
    }

    public void setAdapterData() {

        dataViewEachItemList.clear();
        dataViewEachItemList.add(0, null);

        for (int i = 0; i < dataViewTempList.size(); i++) {
            dataViewEachItemList.add(i + 1, dataViewTempList.get(i));
        }

        if (dataViewEachItemList.size() > 1) {
            if (query != null && !notFirstTime) {
                sharedPref.edit().putBoolean("diacritics_check", false).apply();
            }
            adapterMain = new RecyclerAdapterDetails(getActivity(), dataViewEachItemList, query,
                    null, Locale.getDefault().getLanguage());
            adapterMain.setClickListener(this);

            recyclerView.setAdapter(adapterMain);
            recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        }
    }


    @Override
    public void itemClicked(int position) {
        String duaTitle;
        if (LocaleHelper.getLanguage(context).equals("ar")) {
            if (dataViewEachItemList.get(1).getSubTitleAR() != null) {
                duaTitle = dataViewEachItemList.get(1).getSubTitleAR().trim();
            } else {
                duaTitle = dataViewEachItemList.get(1).getMainTitleAR().trim();
            }
        } else {
            if (dataViewEachItemList.get(1).getSubTitleEN() != null) {
                duaTitle = dataViewEachItemList.get(1).getSubTitleEN().trim();
            } else {
                duaTitle = dataViewEachItemList.get(1).getMainTitleEN().trim();
            }
        }

        String duaShare;
        if (LocaleHelper.getLanguage(context).equals("ar")) {
            if (sharedPref.getBoolean("diacritics_check", true)) {
                duaShare = getString(R.string.star_for_bold) + duaTitle + getString(R.string.star_for_bold) +
                        getString(R.string.two_line_breaks) + dataViewEachItemList.get(position).getContentsAR();
            } else {
                duaShare = getString(R.string.star_for_bold) + duaTitle + getString(R.string.star_for_bold) +
                        getString(R.string.two_line_breaks) + dataViewEachItemList.get(position).getContentsAR();
            }
        } else {
            if (dataViewEachItemList.get(position).getContentsEN() != null &&
                    !dataViewEachItemList.get(position).getContentsEN().trim().isEmpty()) {
                duaShare = getString(R.string.star_for_bold) + duaTitle + getString(R.string.star_for_bold) +
                        getString(R.string.two_line_breaks) + dataViewEachItemList.get(position).getContentsEN();
            } else {
                duaShare = getString(R.string.star_for_bold) + duaTitle + getString(R.string.star_for_bold) +
                        getString(R.string.two_line_breaks) + dataViewEachItemList.get(position).getContentsAR();
            }
        }
        shareIntent(duaTitle, duaShare);
    }

    private void shareIntent(String duaTitle, String s) {
        Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
        sharingIntent.setType("text/plain");
        String subject = duaTitle + getString(R.string.one_space) + getString(R.string.from_app);
        sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
        sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, s);
        sharingIntent.putExtra(android.content.Intent.EXTRA_TITLE, getResources().getString(R.string.app_name));
        startActivity(Intent.createChooser(sharingIntent, "Share via"));
    }

What I researched: 1. ViewPager PagerAdapter not updating the View 2. Update ViewPager dynamically?

However I could not succeed in improving the performance. Please suggest ways to improve performance of viewpager and recyclerview. Thanks.

Upvotes: 0

Views: 1005

Answers (1)

Artem Korolchuk
Artem Korolchuk

Reputation: 71

Seems that problem in your big onBindViewHolder and proccessMyViewHolder method. You should avoid performing any heavy operations such as reading data from shared preferences and creation fonts from assets inside that methods. It would be better to load that data once and use as needed.

Upvotes: 1

Related Questions