偶然之间发现探探的左右滑动的图片挺好玩,试着去做了下,再到后来,看到许多大神也推出了同样仿探探效果的博客,从头到尾阅读下来,写得通俗易懂,基本上没什么问题。于是,实现仿探探效果的想法再次出现在脑海中。那么,还犹豫什么,趁热来一发吧!就这么愉快地决定了。

RecyclerView 是最佳选择!

RecyclerView 是最佳选择!

RecyclerView 是最佳选择!

重要的话讲三遍!!!

究其原因,第一,RecyclerView 是自带 Item View 回收和重用功能的,就不需要我们考虑这个问题了;第二,RecyclerView 的布局方式是通过设置 LayoutManager 来实现的,这样就充分地把布局和 RecyclerView “解耦”开来了。而 LayoutManager 是可以通过自定义的方式来实现的。这恰恰是我们想要的!!!再说一点,这也正是不选用 ListView 的原因之一。

下面,我们就开始动手了。带你见证奇迹的时刻。

1. 创建 CardLayoutManager 并继承自 RecyclerView.LayoutManager 。需要我们自己实现 generateDefaultLayoutParams() 方法:

@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
}

2. 一般情况下,像上面这样写即可。

下面这个方法就是我们的重点了。 onLayoutChildren(final RecyclerView.Recycler recycler, RecyclerView.State state) 方法就是用来实现 Item View 布局的:

@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {super.onLayoutChildren(recycler, state);
    removeAllViews();
    detachAndScrapAttachedViews(recycler);
    int itemCount = getItemCount();
    if(itemCount>CardConfig.DEFAULT_SHOW_ITEM){for(int position = CardConfig.DEFAULT_SHOW_ITEM;position>=0;position--){final View view = recycler.getViewForPosition(position);
            addView(view);
            measureChildWithMargins(view,0,0);
            int widthSpace = getWidth() -getDecoratedMeasuredWidth(view);
            int heightSpace = getHeight() - getBottomDecorationHeight(view);
            layoutDecoratedWithMargins(view,widthSpace/2,heightSpace/2,widthSpace/2+getDecoratedMeasuredWidth(view),heightSpace/2+getBottomDecorationHeight(view));
            if(position == CardConfig.DEFAULT_SHOW_ITEM){view.setScaleX(1-(position-1)*CardConfig.DEFAULT_SCALE);
                view.setScaleY(1-(position-1)*CardConfig.DEFAULT_SCALE);
                view.setTranslationY((position-1)*view.getMeasuredHeight()/CardConfig.DEFAULT_TRANSLATE_Y);
            }else if(position>0){view.setScaleX(1 - position * CardConfig.DEFAULT_SCALE);

                view.setScaleY(1 - position * CardConfig.DEFAULT_SCALE);

                view.setTranslationY(position * view.getMeasuredHeight() / CardConfig.DEFAULT_TRANSLATE_Y);

            }else{view.setOnTouchListener(mOnTouchListener);
            }}}else{// 当数据源个数小于或等于最大显示数时
        for (int position = itemCount - 1; position >= 0; position--) {final View view = recycler.getViewForPosition(position);
            addView(view);
            measureChildWithMargins(view, 0, 0);
            int widthSpace = getWidth() - getDecoratedMeasuredWidth(view);
            int heightSpace = getHeight() - getDecoratedMeasuredHeight(view);
            // recyclerview 布局
            layoutDecoratedWithMargins(view, widthSpace / 2, heightSpace / 2,
                    widthSpace / 2 + getDecoratedMeasuredWidth(view),
                    heightSpace / 2 + getDecoratedMeasuredHeight(view));

            if (position > 0) {view.setScaleX(1 - position * CardConfig.DEFAULT_SCALE);
                view.setScaleY(1 - position * CardConfig.DEFAULT_SCALE);
                view.setTranslationY(position * view.getMeasuredHeight() / CardConfig.DEFAULT_TRANSLATE_Y);
            } else {view.setOnTouchListener(mOnTouchListener);
            }}}}

3.创建onTouchListener

private View.OnTouchListener mOnTouchListener = new View.OnTouchListener() {@Override
    public boolean onTouch(View v, MotionEvent event) {RecyclerView.ViewHolder childViewHolder = mRecyclerView.getChildViewHolder(v);
        if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {mItemTouchHelper.startSwipe(childViewHolder);
        }return false;
    }
};

4.完整CardLayoutManager代码如下

public class CardLayoutManager extends RecyclerView.LayoutManager {private RecyclerView mRecyclerView;
    private ItemTouchHelper mItemTouchHelper;
    public CardLayoutManager(@NonNull RecyclerView recyclerView, @NonNull ItemTouchHelper itemTouchHelper) {this.mRecyclerView = recyclerView;
        this.mItemTouchHelper = itemTouchHelper;
    }@Override
    public RecyclerView.LayoutParams generateDefaultLayoutParams() {return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
    }@Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {super.onLayoutChildren(recycler, state);
        removeAllViews();
        detachAndScrapAttachedViews(recycler);
        int itemCount = getItemCount();
        if(itemCount>CardConfig.DEFAULT_SHOW_ITEM){for(int position = CardConfig.DEFAULT_SHOW_ITEM;position>=0;position--){final View view = recycler.getViewForPosition(position);
                addView(view);
                measureChildWithMargins(view,0,0);
                int widthSpace = getWidth() -getDecoratedMeasuredWidth(view);
                int heightSpace = getHeight() - getBottomDecorationHeight(view);
                layoutDecoratedWithMargins(view,widthSpace/2,heightSpace/2,widthSpace/2+getDecoratedMeasuredWidth(view),heightSpace/2+getBottomDecorationHeight(view));
                if(position == CardConfig.DEFAULT_SHOW_ITEM){view.setScaleX(1-(position-1)*CardConfig.DEFAULT_SCALE);
                    view.setScaleY(1-(position-1)*CardConfig.DEFAULT_SCALE);
                    view.setTranslationY((position-1)*view.getMeasuredHeight()/CardConfig.DEFAULT_TRANSLATE_Y);
                }else if(position>0){view.setScaleX(1 - position * CardConfig.DEFAULT_SCALE);

                    view.setScaleY(1 - position * CardConfig.DEFAULT_SCALE);

                    view.setTranslationY(position * view.getMeasuredHeight() / CardConfig.DEFAULT_TRANSLATE_Y);

                }else{view.setOnTouchListener(mOnTouchListener);
                }}}else{// 当数据源个数小于或等于最大显示数时
            for (int position = itemCount - 1; position >= 0; position--) {final View view = recycler.getViewForPosition(position);
                addView(view);
                measureChildWithMargins(view, 0, 0);
                int widthSpace = getWidth() - getDecoratedMeasuredWidth(view);
                int heightSpace = getHeight() - getDecoratedMeasuredHeight(view);
                // recyclerview 布局
                layoutDecoratedWithMargins(view, widthSpace / 2, heightSpace / 2,
                        widthSpace / 2 + getDecoratedMeasuredWidth(view),
                        heightSpace / 2 + getDecoratedMeasuredHeight(view));

                if (position > 0) {view.setScaleX(1 - position * CardConfig.DEFAULT_SCALE);
                    view.setScaleY(1 - position * CardConfig.DEFAULT_SCALE);
                    view.setTranslationY(position * view.getMeasuredHeight() / CardConfig.DEFAULT_TRANSLATE_Y);
                } else {view.setOnTouchListener(mOnTouchListener);
                }}}}private View.OnTouchListener mOnTouchListener = new View.OnTouchListener() {@Override
        public boolean onTouch(View v, MotionEvent event) {RecyclerView.ViewHolder childViewHolder = mRecyclerView.getChildViewHolder(v);
            if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {mItemTouchHelper.startSwipe(childViewHolder);
            }return false;
        }};
}

可以看出,大致的效果已经有了。缺少的就是处理触摸滑动事件了。

5.CardConfig

public class CardConfig {/**
     * 显示可见的卡片数量
     */
    public static final int DEFAULT_SHOW_ITEM = 3;
    /**
     * 默认缩放的比例
     */
    public static final float DEFAULT_SCALE = 0.1f;
    /**
     * 卡片Y轴偏移量时按照14等分计算
     */
    public static final int DEFAULT_TRANSLATE_Y = 14;
    /**
     * 卡片滑动时默认倾斜的角度
     */
    public static final float DEFAULT_ROTATE_DEGREE = 15f;
    /**
     * 卡片滑动时不偏左也不偏右
     */
    public static final int SWIPING_NONE = 1;
    /**
     * 卡片向左滑动时
     */
    public static final int SWIPING_LEFT = 1 << 2;
    /**
     * 卡片向右滑动时
     */
    public static final int SWIPING_RIGHT = 1 << 3;
    /**
     * 卡片从左边滑出
     */
    public static final int SWIPED_LEFT = 1;
    /**
     * 卡片从右边滑出
     */
    public static final int SWIPED_RIGHT = 1 << 2;

}

6.OnSwipeListener

在看滑动事件的代码之前,我们先定义一个监听器。主要用于监听卡片滑动事件,代码就如下所示,注释也给出来了。应该都看得懂吧:

public interface OnSwipeListener<T> {/**
     * 卡片还在滑动时回调
     *
     * @param viewHolder 该滑动卡片的viewHolder
     * @param ratio      滑动进度的比例
     * @param direction  卡片滑动的方向,CardConfig.SWIPING_LEFT 为向左滑,CardConfig.SWIPING_RIGHT 为向右滑,
     *                   CardConfig.SWIPING_NONE 为不偏左也不偏右
     */
    void onSwiping(RecyclerView.ViewHolder viewHolder, float ratio, int direction);

    /**
     * 卡片完全滑出时回调
     *
     * @param viewHolder 该滑出卡片的viewHolder
     * @param t          该滑出卡片的数据
     * @param direction  卡片滑出的方向,CardConfig.SWIPED_LEFT 为左边滑出;CardConfig.SWIPED_RIGHT 为右边滑出
     */
    void onSwiped(RecyclerView.ViewHolder viewHolder, T t, int direction);

    /**
     * 所有的卡片全部滑出时回调
     */
    void onSwipedClear();
}

7. CardItemTouchHelperCallback

现在,我们可以回过头来看看卡片滑动了。对于 ItemTouchHelper 来处理 Item View 的触摸滑动事件相必都不陌生吧!

我们暂且命名为 CardItemTouchHelperCallback 。对于 ItemTouchHelper.Callback 而言,需要在 getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) 方法中配置 swipeFlags 和 dragFlags 。

public class CardItemTouchHelperCallback<T> extends ItemTouchHelper.Callback {private final RecyclerView.Adapter adapter;
    private List<T> dataList;
    private OnSwipeListener<T> mListener;

    public CardItemTouchHelperCallback(@NonNull RecyclerView.Adapter adapter, @NonNull List<T> dataList) {this.adapter = adapter;
        this.dataList = dataList;
    }public CardItemTouchHelperCallback(@NonNull RecyclerView.Adapter adapter, @NonNull List<T> dataList, OnSwipeListener<T> listener) {this.adapter = adapter;
        this.dataList = dataList;
        this.mListener = listener;
    }@Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {int dragFlags = 0;

        int swipeFlags = 0;

        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();

        if (layoutManager instanceof CardLayoutManager) {swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;

        }return makeMovementFlags(dragFlags, swipeFlags);
    }@Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {return false;
    }@Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {// 移除之前设置的 onTouchListener, 否则触摸滑动会乱了

        viewHolder.itemView.setOnTouchListener(null);

        // 删除相对应的数据

        int layoutPosition = viewHolder.getLayoutPosition();

        T remove = dataList.remove(layoutPosition);

        adapter.notifyDataSetChanged();

        // 卡片滑出后回调 OnSwipeListener 监听器

        if (mListener != null) {mListener.onSwiped(viewHolder, remove, direction == ItemTouchHelper.LEFT ? CardConfig.SWIPED_LEFT : CardConfig.SWIPED_RIGHT);

        }// 当没有数据时回调 OnSwipeListener 监听器

        if (adapter.getItemCount() == 0) {if (mListener != null) {mListener.onSwipedClear();

            }}}public void setOnSwipedListener(OnSwipeListener<T> mListener) {this.mListener = mListener;
    }@Override

    public boolean isItemViewSwipeEnabled() {return false;

    }@Override

    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,

                            float dX, float dY, int actionState, boolean isCurrentlyActive) {super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);

        View itemView = viewHolder.itemView;

        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {// 得到滑动的阀值

            float ratio = dX / getThreshold(recyclerView, viewHolder);

            // ratio 最大为 1 或 -1

            if (ratio > 1) {ratio = 1;

            } else if (ratio < -1) {ratio = -1;

            }// 默认最大的旋转角度为 15 度

            itemView.setRotation(ratio * CardConfig.DEFAULT_ROTATE_DEGREE);

            int childCount = recyclerView.getChildCount();

            // 当数据源个数大于最大显示数时

            if (childCount > CardConfig.DEFAULT_SHOW_ITEM) {for (int position = 1; position < childCount - 1; position++) {int index = childCount - position - 1;

                    View view = recyclerView.getChildAt(position);

                    // 和之前 onLayoutChildren 是一个意思,不过是做相反的动画

                    view.setScaleX(1 - index * CardConfig.DEFAULT_SCALE + Math.abs(ratio) * CardConfig.DEFAULT_SCALE);

                    view.setScaleY(1 - index * CardConfig.DEFAULT_SCALE + Math.abs(ratio) * CardConfig.DEFAULT_SCALE);

                    view.setTranslationY((index - Math.abs(ratio)) * itemView.getMeasuredHeight() / CardConfig.DEFAULT_TRANSLATE_Y);

                }} else {// 当数据源个数小于或等于最大显示数时

                for (int position = 0; position < childCount - 1; position++) {int index = childCount - position - 1;

                    View view = recyclerView.getChildAt(position);

                    view.setScaleX(1 - index * CardConfig.DEFAULT_SCALE + Math.abs(ratio) * CardConfig.DEFAULT_SCALE);

                    view.setScaleY(1 - index * CardConfig.DEFAULT_SCALE + Math.abs(ratio) * CardConfig.DEFAULT_SCALE);

                    view.setTranslationY((index - Math.abs(ratio)) * itemView.getMeasuredHeight() / CardConfig.DEFAULT_TRANSLATE_Y);

                }}// 回调监听器

            if (mListener != null) {if (ratio != 0) {mListener.onSwiping(viewHolder, ratio, ratio < 0 ? CardConfig.SWIPING_LEFT : CardConfig.SWIPING_RIGHT);

                } else {mListener.onSwiping(viewHolder, ratio, CardConfig.SWIPING_NONE);

                }}}}private float getThreshold(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {return recyclerView.getWidth() * getSwipeThreshold(viewHolder);

    }@Override

    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {super.clearView(recyclerView, viewHolder);

        viewHolder.itemView.setRotation(0f);

    }
}

8.在Activity中引用

public class MainActivity extends AppCompatActivity {private List<Integer> list = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initData();
    }private void initView() {final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.setAdapter(new MyAdapter());
        CardItemTouchHelperCallback cardCallback = new CardItemTouchHelperCallback(recyclerView.getAdapter(), list);
        cardCallback.setOnSwipedListener(new OnSwipeListener<Integer>() {@Override
            public void onSwiping(RecyclerView.ViewHolder viewHolder, float ratio, int direction) {MyAdapter.MyViewHolder myHolder = (MyAdapter.MyViewHolder) viewHolder;
                viewHolder.itemView.setAlpha(1 - Math.abs(ratio) * 0.2f);
                if (direction == CardConfig.SWIPING_LEFT) {myHolder.dislikeImageView.setAlpha(Math.abs(ratio));
                } else if (direction == CardConfig.SWIPING_RIGHT) {myHolder.likeImageView.setAlpha(Math.abs(ratio));
                } else {myHolder.dislikeImageView.setAlpha(0f);
                    myHolder.likeImageView.setAlpha(0f);
                }}@Override
            public void onSwiped(RecyclerView.ViewHolder viewHolder, Integer o, int direction) {MyAdapter.MyViewHolder myHolder = (MyAdapter.MyViewHolder) viewHolder;
                viewHolder.itemView.setAlpha(1f);
                myHolder.dislikeImageView.setAlpha(0f);
                myHolder.likeImageView.setAlpha(0f);
                Toast.makeText(MainActivity.this, direction == CardConfig.SWIPED_LEFT ? "swiped left" : "swiped right", Toast.LENGTH_SHORT).show();
            }@Override
            public void onSwipedClear() {Toast.makeText(MainActivity.this, "data clear", Toast.LENGTH_SHORT).show();
                recyclerView.postDelayed(new Runnable() {@Override
                    public void run() {initData();
                        recyclerView.getAdapter().notifyDataSetChanged();
                    }}, 3000L);
            }});
        final ItemTouchHelper touchHelper = new ItemTouchHelper(cardCallback);
        final CardLayoutManager cardLayoutManager = new CardLayoutManager(recyclerView, touchHelper);
        recyclerView.setLayoutManager(cardLayoutManager);
        touchHelper.attachToRecyclerView(recyclerView);
    }private void initData() {list.add(R.drawable.img_avatar_01);
        list.add(R.drawable.img_avatar_02);
        list.add(R.drawable.img_avatar_03);
        list.add(R.drawable.img_avatar_04);
        list.add(R.drawable.img_avatar_05);
        list.add(R.drawable.img_avatar_06);
        list.add(R.drawable.img_avatar_07);
    }private class MyAdapter extends RecyclerView.Adapter {@Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
            return new MyViewHolder(view);
        }@Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {ImageView avatarImageView = ((MyViewHolder) holder).avatarImageView;
            avatarImageView.setImageResource(list.get(position));
        }@Override
        public int getItemCount() {return list.size();
        }class MyViewHolder extends RecyclerView.ViewHolder {ImageView avatarImageView;
            ImageView likeImageView;
            ImageView dislikeImageView;

            MyViewHolder(View itemView) {super(itemView);
                avatarImageView = (ImageView) itemView.findViewById(R.id.iv_avatar);
                likeImageView = (ImageView) itemView.findViewById(R.id.iv_like);
                dislikeImageView = (ImageView) itemView.findViewById(R.id.iv_dislike);
            }}}

9.MainActiviy布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
   >

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

10.item布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="336dp"
    android:layout_height="426dp"
    android:background="@drawable/img_card_background"
    android:gravity="center"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

        <com.example.admin.tantanuse.RoundImageView
            android:id="@+id/iv_avatar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="centerCrop"
            app:radius="7.5dp"
            android:src="@drawable/img_avatar_01" />

        <ImageView
            android:id="@+id/iv_dislike"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_marginRight="15dp"
            android:layout_marginTop="15dp"
            android:alpha="0"
            android:src="@drawable/img_dislike" />

        <ImageView
            android:id="@+id/iv_like"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="15dp"
            android:layout_marginTop="15dp"
            android:alpha="0"
            android:src="@drawable/img_like" />

    </RelativeLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:paddingLeft="14dp"
        android:paddingTop="15dp">

        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="小姐姐"
            android:textColor="@android:color/black"
            android:textSize="16sp" />

        <TextView
            android:id="@+id/tv_age"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/tv_name"
            android:layout_marginTop="5dp"
            android:background="@drawable/shape_age"
            android:gravity="center"
            android:text="♀ 23"
            android:textColor="#FFFFFF"
            android:textSize="14sp" />

        <TextView
            android:id="@+id/tv_constellation"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/tv_name"
            android:layout_marginLeft="4dp"
            android:layout_marginTop="5dp"
            android:layout_toRightOf="@id/tv_age"
            android:background="@drawable/shape_constellation"
            android:gravity="center"
            android:text="狮子座"
            android:textColor="#FFFFFF"
            android:textSize="14sp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/tv_age"
            android:layout_marginTop="5dp"
            android:gravity="center"
            android:text="IT/互联网"
            android:textColor="#cbcbcb" />

    </RelativeLayout>

</LinearLayout>

11.带圆角的ImagView

public class RoundImageView extends ImageView {private Path mPath;
    private RectF mRectF;
    private float[] rids = new float[8];
    private PaintFlagsDrawFilter paintFlagsDrawFilter;

    public RoundImageView(Context context) {this(context, null);
    }public RoundImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);
    }public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RoundImageView);
        float mRadius = array.getDimension(R.styleable.RoundImageView_radius, 10);
        rids[0] = mRadius;
        rids[1] = mRadius;
        rids[2] = mRadius;
        rids[3] = mRadius;
        rids[4] = 0f;
        rids[5] = 0f;
        rids[6] = 0f;
        rids[7] = 0f;
        array.recycle();
        mPath = new Path();
        paintFlagsDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
        setLayerType(View.LAYER_TYPE_HARDWARE, null);
    }@Override
    protected void onDraw(Canvas canvas) {mPath.reset();
        mPath.addRoundRect(mRectF, rids, Path.Direction.CW);
        canvas.setDrawFilter(paintFlagsDrawFilter);
        canvas.save();
        canvas.clipPath(mPath);
        super.onDraw(canvas);
        canvas.restore();
    }@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);
        mRectF = new RectF(0, 0, w, h);
    }
}

12.attrs.XML

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="RoundImageView">
        <attr name="radius" format="reference|dimension" />
    </declare-styleable>
</resources>

模仿探探的左右滑动切换卡片功能相关推荐

  1. android 卡片滑动详情页,在Mugeda中制作顺畅的左右滑动切换卡片效果的教程

    之前在做<刁角武汉>的时候对如何选择景点这个问题做了好几个方案,一个是画一张大地图,另一个是做垂直的列表选择,但我还是选择了左右滑动来切换景点.因为在多次尝试之后发现发现在手机屏上似乎不适 ...

  2. Android中实现类似探探中图片左右滑动切换效果

    偶然之间发现探探的左右滑动的图片挺好玩,试着去做了下,再到后来,看到许多大神也推出了同样仿探探效果的博客,从头到尾阅读下来,写得通俗易懂,基本上没什么问题.于是,实现仿探探效果的想法再次出现在脑海中. ...

  3. android滑动切换卡片,一步步实现Viewpager卡片翻页效果

    这个CardStackViewpager的灵感来自Github上面的 FlippableStackView开源项目,而我想实现的效果方向上刚好与FlippableStackView相反,而且细节上也有 ...

  4. android tab之间滑动切换界面功能

    1. onTouchListener();                       //捕捉touch事件,比如说onDown 需要将可滑动的控件加上两个方法:(1)view.setOnTouch ...

  5. 【Unity实战100例】Unity循环UI界面切换卡片功能

    目录 ​编辑 一:制作UI界面 二:代码逻辑 1.定义基础变量

  6. android ViewPager页面左右滑动切换

    我们日常生活中用到的微博,QQ,微信等app在进行页面左右滑动的时候,很多都可以用ViewPager来实现.可以说,ViewPager在android开发中十分常见并且特别实用. Viewpager在 ...

  7. 为什么快手不能左右滑了_为什么有的手机不支持快手滑动切换

    为什么有的手机不支持快手滑动切换 为了更方便地看快手上的视频,很多人都用了上滑切换作品的功能.然而有些用户的快手并没有上滑切换的功能,要怎么调用呢?一起来看看吧. 方法如下: 一.首先,更新快手APP ...

  8. vue实现卡片式上下滑动_基于Vue.js仿制探探卡片左右滑动特效

    说明 > 最近一直在捣鼓Nuxt.js项目,项目中有个需求是实现类似探探左右滑动切换功能.要求能实现手指拖拽切换.点击按钮进行切换.拖拽回弹等功能. 如上图:最终展示效果 emmm~~ 是不是感 ...

  9. 五行代码实现 炫动滑动 卡片层叠布局,仿探探、人人影视订阅界面 简单优雅:LayoutManager+ItemTouchHelper

    本篇文章已授权微信公众号 hongyangAndroid (鸿洋)独家发布 转载请标明出处: http://blog.csdn.net/zxt0601/article/details/53730908 ...

最新文章

  1. 创建Silverlight自定义启动画面
  2. 《系统集成项目管理工程师》必背100个知识点-96我国企业信息化发展的战略要点...
  3. javascript的性能优化tips
  4. java.io.eof_java.io.IOException: java.io.EOFException: Unexpected end of input stream错误
  5. python引用传递产生的问题_python关于lambda、引用传递等易犯的错误
  6. Java之Callable和Runnable
  7. 仅展示近三天的动态设置_抱歉,朋友仅展示最近三天的朋友圈
  8. 微信小程序单选框radio使用实例
  9. (1)DBA查询:数据库
  10. pytorch_gpu案例及gpu使用
  11. 用计算机打字教案,使用打字软件练指法教案
  12. 股票投资(炒股)之入门基础知识
  13. SpringBoot 请求参数包含 [] 特殊符号
  14. 手机测试人员的思维过程
  15. 苹果开放降级_苹果新系统上线,只为M1芯片?
  16. 如何给安卓设备一键截图到电脑
  17. java/php/net/python教学评价系统设计
  18. 5款国产ARM芯片替代ST
  19. 期末作业成品代码——威海影视(1页) HTML+CSS+JavaScript 学生DW网页设计作业成品 web课程设计网页规划与设计 计算机毕设网页设计源码
  20. 牛投客- 股票与股权投资的区别是什么?

热门文章

  1. VS.Net 2005 Beta2连接Team Foundation Server的问题
  2. Linux下gcc入门
  3. 吴恩达 coursera AI 专项四第三课总结+作业答案
  4. Python学习笔记:线程和进程(合),分布式进程
  5. Python:高阶函数
  6. Crazepony的理念
  7. VIBE复现过程,使用nvidia和libOpenGL.so渲染出错及解决方案
  8. 网页服务器知识,HTML网页服务器的知识点
  9. 2016年春季计算机应用基础,东北师范2016年春季《计算机应用基础》期末考核
  10. Softmax vs. SoftmaxWithLoss 推导过程