Подтвердить что ты не робот

Показывать пользовательский вид в swiped RecyclerView item

Прежде всего, я увидел этот вопрос: Добавление цветного фона с текстом/значком в строке подкачки при использовании утилиты Android RecyclerView

Однако, хотя заголовок содержит "текст/значок", ответ покрывает только объект Canvas для рисования прямоугольника.

Теперь я рисую зеленый прямоугольник; однако я хочу сделать более очевидным, что произойдет, добавив кнопку "done", как на следующем снимке экрана.

введите описание изображения здесь

Я создал пользовательский вид:

public class ChoosePlanView extends LinearLayout{

    public ChoosePlanView(Context context) {
        super(context);
        init();
    }

    public ChoosePlanView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ChoosePlanView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init(){
        View v = inflate(getContext(), R.layout.choose_plan_view, null);
        addView(v);
    }
}

И макет очень прост, состоящий из зеленого фона и значка:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" 
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/choose_green">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/doneIcon"
        android:layout_gravity="center_horizontal|right"
        android:src="@mipmap/done"/>

</LinearLayout>

Я попробовал переопределить метод OnChildDraw:

// ChoosePlanView choosePlanView; <- that was declared and initialized earlier, so I don't have to create a new ChoosePlanView everytime in OnChildDraw();

ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {    

/*
    onMove, onSwiped omitted
*/
  public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
       View itemView = viewHolder.itemView;

       if(dX < 0){
            choosePlanView.invalidate();
            //choosePlanView.setBackgroundResource(R.color.delete_red);
            choosePlanView.measure(itemView.getWidth(), itemView.getHeight());
            choosePlanView.layout(itemView.getRight() + (int) dX, itemView.getTop(), itemView.getRight(), itemView.getBottom());
            c.save();
            c.translate(choosePlanView.getRight() + (int) dX, viewHolder.getAdapterPosition()*itemView.getHeight());

            choosePlanView.draw(c);
            c.restore();
       }

       super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
  }
}

И это работает (рисует мой ChoosePlanView), но есть две проблемы.

Прежде всего, он рисует только маленькую зеленую квадрат, обертывая мою "сделанную" иконку (красная часть вида, изображенная на скриншоте ниже, взята из прокомментированной строки choosePlanView.setBackgroundResource(R.color.delete_red);). Представление имеет правильную ширину, хотя при проверке в отладчике.

Во-вторых, там, где находится значок. Я указал, что хочу, чтобы он был центрирован по горизонтали и всегда "прилипал" к правой стороне представления. Однако он перемещается, когда элемент RecyclerView прокручивается, а с красным фоном показывает, что он также не помещается в центр.

Я попытался добавить строку choosePlanView.invalidate(), потому что я думал, что это происходит, потому что представление было создано ранее, но, похоже, перерисовывание ничего не меняет.

введите описание изображения здесь

4b9b3361

Ответ 1

Использование ItemTouchUiUtil обеспечивает надежное решение. Это позволяет вам иметь представление переднего плана и фона в вашем ViewHolder и делегировать всю обработку салфетки на представление переднего плана. Вот пример, который я использую для прокрутки, чтобы удалить.

В ItemTouchHelper.Callback:

public class ClipItemTouchHelper extends ItemTouchHelper.SimpleCallback {

    ...

    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        if (viewHolder != null){
            final View foregroundView = ((ClipViewHolder) viewHolder).clipForeground;

            getDefaultUIUtil().onSelected(foregroundView);
        }
    }

    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView,
                            RecyclerView.ViewHolder viewHolder, float dX, float dY,
                            int actionState, boolean isCurrentlyActive) {
        final View foregroundView = ((ClipViewHolder) viewHolder).clipForeground;

        drawBackground(viewHolder, dX, actionState);

        getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY,
                actionState, isCurrentlyActive);
     }

    @Override
    public void onChildDrawOver(Canvas c, RecyclerView recyclerView,
                                RecyclerView.ViewHolder viewHolder, float dX, float dY,
                                int actionState, boolean isCurrentlyActive) {
        final View foregroundView = ((ClipViewHolder) viewHolder).clipForeground;

        drawBackground(viewHolder, dX, actionState);

        getDefaultUIUtil().onDrawOver(c, recyclerView, foregroundView, dX, dY,
                actionState, isCurrentlyActive);
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder){
        final View backgroundView = ((ClipViewHolder) viewHolder).clipBackground;
        final View foregroundView = ((ClipViewHolder) viewHolder).clipForeground;

        // TODO: should animate out instead. how?
        backgroundView.setRight(0);

        getDefaultUIUtil().clearView(foregroundView);
    }

    private static void drawBackground(RecyclerView.ViewHolder viewHolder, float dX, int actionState) {
        final View backgroundView = ((ClipViewHolder) viewHolder).clipBackground;

        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
            //noinspection NumericCastThatLosesPrecision
            backgroundView.setRight((int) Math.max(dX, 0));
        }
    }
}

И в вашем файле макета определите представления переднего плана и фона:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/clipRow"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

        <View style="@style/Divider"/>

        <FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/row_selector"
        >

        <RelativeLayout
            android:id="@+id/clipBackground"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:background="@color/swipe_bg"
            tools:layout_width="match_parent">

            <ImageView
                android:layout_width="32dp"
                android:layout_height="32dp"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_centerVertical="true"
                android:layout_marginLeft="12dp"
                android:layout_marginStart="12dp"
                android:focusable="false"
                android:src="@drawable/ic_delete_24dp"
                tools:ignore="ContentDescription"/>

        </RelativeLayout>

        <RelativeLayout
            android:id="@+id/clipForeground"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingBottom="@dimen/clip_vertical_margin"
            android:paddingLeft="@dimen/clip_horizontal_margin"
            android:paddingRight="@dimen/clip_horizontal_margin"
            android:paddingTop="@dimen/clip_vertical_margin" >

            <CheckBox
                android:id="@+id/favCheckBox"
                android:layout_width="@dimen/view_image_size"
                android:layout_height="@dimen/view_image_size"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:background="@android:color/transparent"
                android:button="@drawable/ic_star_outline_24dp"
                android:clickable="true"
                android:contentDescription="@string/content_favorite"
                />

             ...

        </RelativeLayout>

    </FrameLayout>

</LinearLayout>

Ответ 2

Вместо того, чтобы нарисовать, я считаю, что вы должны использовать viewType

Реализует getItemViewType(int position) в адаптере, возвращая различные типы, например. 0 для нормального, 1 для развертки

и измените onCreateViewHolder и т.д., чтобы вернуть разный макет на другой тип view

Ответ 3

Для людей, которые все еще находят это значение по умолчанию, это самый простой способ.

Простой служебный класс для добавления фона, значка и метки к элементу RecyclerView, проводя его влево или вправо.

enter image description here enter image description here

вставить в Gradle

implementation 'it.xabaras.android:recyclerview-swipedecorator:1.2.1'

Переопределить метод onChildDraw класса ItemTouchHelper

@Override
public void onChildDraw (Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,float dX, float dY,int actionState, boolean isCurrentlyActive){

    new RecyclerViewSwipeDecorator.Builder(MainActivity.this, c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
            .addBackgroundColor(ContextCompat.getColor(MainActivity.this, R.color.my_background))
            .addActionIcon(R.drawable.my_icon)
            .create()
            .decorate();

    super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}

для получения дополнительной информации → https://github.com/xabaras/RecyclerViewSwipeDecorator

Ответ 4

Мне удалось заставить это работать с цветом и изображением, расширив ответ, предоставленный Sanvywell, по адресу: Добавление цветного фона с текстом/значком под развёрнутой строкой при использовании Android RecyclerView

Я не буду воссоздать полный пост (вы можете перейти по ссылке). Короче говоря, я не создал полный просмотр для визуализации во время салфетки, но только добавил растровое изображение к холсту, соответствующим образом расположенному в элементе RecyclerView.

Ответ 5

Я исследовал ту же проблему. То, что я закончил делать, было в макете, содержащем recyclerView, я добавил простой FrameLayout с именем "swipe_bg" той же высоты, что и один из моих элементов RecyclerView.ViewHolder. Я установил его видимость "ушел" и поместил его под RecyclerView

Затем в моей работе, когда я устанавливаю ItemTouchHelper, я переопределяю onChildDraw так.

final ItemTouchHelper.SimpleCallback swipeCallback = new ItemTouchHelper.SimpleCallback(0,
                ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT){


            @Override
            public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
                 View itemView = viewHolder.itemView;
                 swipe_bg.setY(itemView.getTop());
                 if(isCurrentlyActive) {
                     swipe_bg.setVisibility(View.VISIBLE);
                 }else{
                     swipe_bg.setVisibility(View.GONE);
                 }
                 super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        }
  };

Не знаю, лучший ли это способ, но казался самым простым способом.