Страницы

Поиск по вопросам

воскресенье, 22 декабря 2019 г.

Долгое выполнение ресайза изображения при скроллинге RecyclerView

#android #recyclerview


Есть RecylerView в который динамически добавляется по 10 объектов (CardView).
Каждый объект содержит FrameLayout, в который в свою очередь добавляется LinearLayout.
LinearLayout может содержать до трех дочерних LinearLayout с ImageView. Часть функции
формирования представлена ниже:  

case 4:
    linearLayout1 = new LinearLayout(activity);
    linearLayout2 = new LinearLayout(activity);
    layoutParams = new LinearLayout.LayoutParams(ViewGroup
            .LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    layoutParams.weight = 1.0f;
    linearLayout1.setLayoutParams(layoutParams);
    linearLayout2.setLayoutParams(layoutParams);
    linearLayout1.setOrientation(LinearLayout.HORIZONTAL);
    linearLayout2.setOrientation(LinearLayout.HORIZONTAL);
    linearLayout.setOrientation(LinearLayout.VERTICAL);
    for (int i = 0; i < 2; i++) {
        if (i == 0) {
            layoutParams = new LinearLayout.LayoutParams(ViewGroup
                    .LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
            layoutParams.weight = 1.0f;
            layoutParams.setMargins(0, 0, Utils.pxToDp(5, activity), Utils.dpToPx(3,
activity));
            postImageView = new PostImageView(activity, layoutParams);
            postImageView.setPlaceholder(post, i);
            postImageView.setOnClick(listUrls, i);
            linearLayout1.addView(postImageView);
            postImageView.downloadImage(listUrls, i);
        } else {
            layoutParams = new LinearLayout.LayoutParams(ViewGroup
                    .LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
            layoutParams.weight = 1.0f;
            layoutParams.setMargins(0, 0, 0, Utils.pxToDp(5, activity));
            postImageView = new PostImageView(activity, layoutParams);
            postImageView.setPlaceholder(post, i);
            postImageView.setOnClick(listUrls, i);
            linearLayout1.addView(postImageView);
            postImageView.downloadImage(listUrls, i);
        }
    }
    for (int i = 2; i < 4; i++) {
        if (i == 2) {
            layoutParams = new LinearLayout.LayoutParams(ViewGroup
                    .LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
            layoutParams.weight = 1.0f;
            layoutParams.setMargins(0, 0, Utils.pxToDp(5, activity), 0);
            postImageView = new PostImageView(activity, layoutParams);
            postImageView.setPlaceholder(post, i);
            postImageView.setOnClick(listUrls, i);
            linearLayout2.addView(postImageView);
            postImageView.downloadImage(listUrls, i);
        } else {
            layoutParams = new LinearLayout.LayoutParams(ViewGroup
                    .LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
            layoutParams.weight = 1.0f;
            postImageView = new PostImageView(activity, layoutParams);
            postImageView.setPlaceholder(post, i);
            postImageView.setOnClick(listUrls, i);
            linearLayout2.addView(postImageView);
            postImageView.downloadImage(listUrls, i);
        }
    }
    linearLayout.addView(linearLayout1);
    linearLayout.addView(linearLayout2);
    break;


Я знаю, что функция ужасна.  

postImageView.setPlaceholder(post, i); //метод формирования заглушки


Данный метод выглядит следующим образом:

public void setPlaceholder(Posts post, int position) {
        width = Integer.parseInt(post.getAttachmentsPhotoWidth().get(position));
//Исходный
        height = Integer.parseInt(post.getAttachmentsPhotoHeight().get(position));
//Исходный
        sideValue = post.getImageWidth().get(position); //Известна высота или ширина
запрашиваемого изображения
        int destWidth = CalculateAspectRatio.getDownloadPhotoSize(width, height,
sideValue).x; //таргет
        int destHeight = CalculateAspectRatio.getDownloadPhotoSize(width, height,
sideValue).y; //таргет

            Picasso.with(context)
            .load(R.drawable.img_holder3)
            .noPlaceholder()
            .noFade()
            .memoryPolicy(MemoryPolicy.NO_CACHE)
            .resize(destWidth, destHeight)
            .into(this);
    }


Все это дело успешно тормозить скроллинг RecyclerView, а так же из-за потери фреймов
можно увидеть как изменяется размер у ImageView при скроллинге, как добавляются ImageView.
Как избавиться от такого эффекта? Приму любые предложения по рефракторингу кода.

UPDATE
Инициализация RecyclerView:

private void setupRecyclerView(View view) {
        RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.container_for_home_posts);
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        postAdapter = new PostAdapter(getActivity(), recyclerView);
        postAdapter.setHasStableIds(true);
        recyclerView.setAdapter(postAdapter);
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        postAdapter.setOnLoadMoreListener(new PostAdapter.OnLoadMoreListener() {
            @Override
            public void onLoadMore() {
                postAdapter.addProgressBar();
                jsonLoader.loadPostsForFeed(countItemsLoad);
            }
        });
    }


Добавление новых объектов (подгружаем по 10 постов):

jsonLoader.setOnFeedLoadedListener(new JsonLoader.OnFeedLoadedListener() {
            @Override
            public void onFeedLoaded() {
                listPosts = jsonLoader.getPostsForFeed();
                postAdapter.removeProgressBar();
                postAdapter.add(listPosts);
                postAdapter.setLoaded();
            }
        });


Метод postAdapter.add();:

public void add(ArrayList listPosts) {
    int startIndex = mData.size();
    mData.addAll(startIndex, listPosts);
    notifyItemRangeInserted(startIndex, listPosts.size());
}


Где Posts - класс, хранящий данные, распарсенные из JsonObject.
Собственно сам ViewHolder:

    private static class ViewHolderPost extends RecyclerView.ViewHolder {
    ImageView mPostAvatar;
    ImageView mPostLike;
    TextView mPostTitle;
    TextView mPostDate;
    TextView mPostText;
    FrameLayout frameLayout;
    ImageAdapter imageAdapter;

    public ViewHolderPost(View view) {
        super(view);
        mPostAvatar = (ImageView) view.findViewById(R.id.post_avatar);
        mPostLike = (ImageView) view.findViewById(R.id.post_like);
        mPostTitle = (TextView) view.findViewById(R.id.post_title);
        mPostDate = (TextView) view.findViewById(R.id.post_date);
        mPostText = (TextView) view.findViewById(R.id.post_text);
        frameLayout = (FrameLayout) view.findViewById(R.id.post_image_layout);
    }
}


Методы OnCreateViewHolder и OnBindViewHolder:

    //--Создание View--//
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    RecyclerView.ViewHolder viewHolder;
    if (viewType == POST_CARD_VIEW) {
        View view = inflater.inflate(R.layout.post_card, parent, false);
        viewHolder = new ViewHolderPost(view);
    } else {
        View view = inflater.inflate(R.layout.progres_bar, parent, false);
        viewHolder = new ProgressViewHolder(view);
    }
    return viewHolder;
}

//--Метод onBind--//
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
    if (holder instanceof ViewHolderPost) {
        holder.itemView.setTag(mData.get(position));
        this.holder = (ViewHolderPost) holder;
        addData((ViewHolderPost) holder, mData.get(position));
    } else {
        ((ProgressViewHolder) holder).progressBar.setIndeterminate(true);
    }
}


Метод добавление данных в view (addData()):

private void addData(final PostAdapter.ViewHolderPost holder, final Posts post) {
    holder.mPostTitle.setText(post.getGroupName());
    //Todo: Изменить время публикования поста
    long dateValue = Long.valueOf(post.getDate()) * 1000L;
    Date date = new java.util.Date(dateValue);
    String time = new SimpleDateFormat("dd MMMM, в HH:mm", new Locale("ru")).format(date);
    holder.mPostDate.setText(time);

    if (!post.getText().isEmpty()) {
        holder.mPostText.setVisibility(View.VISIBLE);
        holder.mPostText.setText(post.getText());
    } else
        holder.mPostText.setVisibility(View.GONE);

    Picasso.with(activity)
            .load(post.getGroupPhoto_50())
            .into(holder.mPostAvatar);

    if (post.getIsUserLike().equals("1"))
        holder.mPostLike.setImageResource(R.drawable.ic_liked);
    else
        holder.mPostLike.setImageResource(R.drawable.ic_like);

    //--Если пост имеет вложения, нужно их отобразить--//
    if (post.getIsAttachments()) {
        holder.frameLayout.setVisibility(View.VISIBLE);
        setImageToLayout(holder, post);
    } else {
        holder.frameLayout.setVisibility(View.GONE);
        holder.frameLayout.invalidate();
    }
}


Метод setImageToLayout() как раз и выполняет функции добавленияLinearLayout:

private void setImageToLayout(PostAdapter.ViewHolderPost holder, Posts post) {
    //--Из массива вложений берем только изображения--//
    final ArrayList listUrls = new ArrayList<>();
    for (int i = 0; i < post.getJsonObjectAttachments().length(); i++) {
        try {
            String t = post.getJsonObjectAttachments().getJSONObject(i).getString("type");
            if (t.equals("photo")) {
                listUrls.add(post.getURLImage(MainActivity.SCREEN_WIDTH, i));
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
    //--Добавляем полученный layout--//
    if (listUrls.get(0) != "") {
        holder.frameLayout.removeAllViews();
        holder.frameLayout.addView(Utils.getImageForPost(activity, post, listUrls,
listUrls.size()));
    } else {
        holder.frameLayout.removeAllViews();
        holder.frameLayout.setVisibility(View.GONE);
    }
}


LinearLayout с ImageView должен выглядеть так, как показано на изображении ниже.
Количество изображений варьируется от 0 до 10.

    


Ответы

Ответ 1



Из вашего кода не очень ясен контекст того как он используется. Каким образом создаются View для RecyclerView, как вы используете ViewHolder, вы просто показали кусок кода, который создает некий лейаут через Java код, но не то как он применяется. Сложно что-либо посоветовать, не имея понятия о том как он используется. Сам код выглядит нормально (конечно не считая того, что вы создаете view без участия xml лейаутов), но не думаю, что проблема именно в нем, а в том как используются View в RecyclerView, как в дальнейшем обновляются и т.д.

Ответ 2



Я вижу сдесь 2 главных причины медленной работы RecyclerView. Во первых вложенные LinearLayout работают очень медленно, не могу представить какой именно разметки вы хотите добиться но предположу что это можно довольно просто сделать с помощью RelativeLayout(Либо GridLayoutManager). Буду рад более подробно ответить по этому пункту если вы прикрепите изображение с желаемым результатом. Создание новых объектов сильно тормозит прокрутку. Для более эффективного использования ViewCache в RecyclerView(в приведенном примере использоваие ViewCache невозможно из за создания разной разметки для каждого элемента). В общем есть как минимум 4 способа решения данной проблемы: Универсальный, эффективный по памяти и объему вычислений, сложный в реализации - написать свой LayoutManager который будет размещать ваши динамические View как вам захочется, при этом эффективно использовать ViewCache. Простой в реализации, но расходующий дополнительные ресурсы при вычислениях невидимых View. Cоздать xml файл(или написаь руками создание разметки) с разметкой где количество ImageView соответствует максимально возможному и для ImageView которые не yчавствуют в отображении элемента выставить visibility = View.GONE. Немного сложнее в реализации чем 2 и расходующий доп. память на ViewCache, но не расходующий доп ресурсы на просчет невидимых View. Создать несколько разметок для разного количества ImageView(в вашем случае) который в свою очередь зависит от значения возвращаемого из getItemViewType() метода. -единственно верный по мнению @pavlofff Комбинации 3х перечисленных выше способов.

Комментариев нет:

Отправить комментарий