在刷过RecyclerView项目下显示自定义视图

时间:2015-08-25 13:07:26

标签: android android-recyclerview

首先,我看到了这个问题:Adding a colored background with text/icon under swiped row when using Android's RecyclerView

但是,即使标题用文本/图标"表示,但答案仅涵盖使用Canvas对象绘制矩形。

现在我已经绘制了一个绿色矩形;但是我想通过添加"完成"来更明显地发生什么?按钮,就像下面的屏幕截图一样。

enter image description here

我创建了一个自定义视图:

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),但有两个问题。

首先,它只画了一个小绿色方块包裹我的&#34;完成&#34;图标(下面屏幕截图中显示的视图的红色部分来自注释行choosePlanView.setBackgroundResource(R.color.delete_red);)。在调试器中检查时,视图具有正确的宽度。

第二件事是放置图标的位置。我指定我希望它在水平方向上居中并且始终&#34;坚持&#34;在视图的右侧。然而,当滑动RecyclerView项目并且红色背景显示它不在中心时,它会移动。

我尝试添加行choosePlanView.invalidate(),因为我认为会发生这种情况,因为视图是先前创建的,但似乎重绘不会改变任何内容。

enter image description here

5 个答案:

答案 0 :(得分:16)

使用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>

答案 1 :(得分:1)

我相信你应该使用viewType

而不是画画

在适配器中实现getItemViewType(int position),返回不同类型,例如0表示正常,1表示翻转

并更改onCreateViewHolder等以在不同的viewType上返回不同的布局

答案 2 :(得分:1)

对于仍在查找此默认值的人,这是最简单的方法。

一个简单的实用程序类,可在向左或向右滑动时向RecyclerView项添加背景,图标和标签。

enter image description here enter image description here

插入Gradle

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

重写ItemTouchHelper类的onChildDraw方法

@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

答案 3 :(得分:0)

我设法通过扩展Sanvywell在Adding a colored background with text/icon under swiped row when using Android's RecyclerView

提供的答案来使用颜色和图像。

我不会重新创建完整帖子(您可以点击链接)。简而言之,我没有创建一个完整的视图来在滑动过程中渲染,但只是在画布上添加了一个位图,适当地定位到RecyclerView项目。

答案 4 :(得分:-2)

我正在调查同样的问题。我最终做的是,在包含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);
        }
  };

不知道这是不是最好的方式,但似乎是最简单的方式。