可拖拽式层叠卡片效果

本文介绍如何创建一个可拖拽、层叠的卡片切换交互页面,适用于社交交友应用,提供左右滑动和点击交互,同时支持内容加载更多功能。通过简单的代码,实现炫酷的视觉和操作体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

现在很多社交交友类的APP首页都会有一个类似于卡片式切换的交互页,提高和用户的互动性。下面我们也做一个类似于这样的效果,它实现了用户的左右滑动和点击交互监听功能,同时也可设置其剩下多少内容时加载更多,简单的代码实现炫酷的操作,定会为你的应用增色不少。

效果图

使用

1、Layout布局

<com.wiggins.cardcontainer.widget.CardContainerView
    android:id="@+id/card_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

2、添加卡片View

@Override
public void addView(View card) {
    if (isLoadMore) {
        this.mCardList.add(ListUtil.getSize(mCardList), card);
    } else {
        this.mCardList.add(card);
    }
    LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
    this.addView(card, 0, layoutParams);
    card.setOnTouchListener(onTouchListener);
    if (!isLoadMore) {
        this.setLayoutParams(card, mCardList.size());
    }
}

3、设置卡片相对位置

private void setLayoutParams(View card, int index) {
    LayoutParams params = new LayoutParams(card.getLayoutParams());
    params.topMargin = dip2px(marginTop) + getResources().getDimensionPixelSize(R.dimen.margin_tiny) * index;
    params.bottomMargin = dip2px(marginBottom) - getResources().getDimensionPixelSize(R.dimen.margin_tiny) * index;
    params.leftMargin = dip2px(marginLeft);
    params.rightMargin = dip2px(marginRight);
    card.setLayoutParams(params);
}

4、触摸拖拽监听

private OnTouchListener onTouchListener = new OnTouchListener() {

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (mOnTouch && v.equals(mCardList.get(0))) {
            int rawY = (int) event.getRawY();
            int rawX = (int) event.getRawX();
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    getLayout();
                    mLastY = (int) event.getRawY();
                    mLastX = (int) event.getRawX();
                    break;
                case MotionEvent.ACTION_MOVE:
                    int offsetY = rawY - mLastY;
                    int offsetX = rawX - mLastX;
                    mCardList.get(0).layout(mCardList.get(0).getLeft() + offsetX, mCardList.get(0).getTop() + offsetY, mCardList.get(0).getRight() + offsetX, mCardList.get(0).getBottom() + offsetY);
                    mRightOut = mCardList.get(0).getLeft() > getDisplayMetrics(mContext).widthPixels / 2;
                    mLeftOut = mCardList.get(0).getRight() < getDisplayMetrics(mContext).widthPixels / 2;
                    mLastY = rawY;
                    mLastX = rawX;
                    break;
                case MotionEvent.ACTION_UP:
                    change();
                    break;
            }
        }
        return true;
    }
};

5、滑出及复位

private void change() {
    if (mLeftOut) {
        // 向左边滑出
        out(true);
    } else if (mRightOut) {
        // 向右边滑出
        out(false);
    } else {
        // 复位
        reset();
    }
}

6、加载及滑动接口

public interface LoadMore {
    void load();
}

public interface LeftOrRight {
    void leftOrRight(boolean left);
}

7、滑动动画

private void animator(CardIndex newCard, CardIndex oldCard) {
    ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(), newCard, oldCard);
    animator.setDuration(200);
    animator.setInterpolator(new AccelerateDecelerateInterpolator());
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mOnTouch = false;
            CardIndex value = (CardIndex) animation.getAnimatedValue();
            mCardList.get(0).layout(value.left, value.top, value.right, value.bottom);
        }
    });
    animator.addListener(new AnimatorListenerAdapter() {

        @Override
        public void onAnimationEnd(Animator animation) {
            if (mRightOut || mLeftOut) {
                removeTopCard();
                if (mLeftOrRight != null) {
                    mLeftOrRight.leftOrRight(mLeftOut);
                }
            }
            mOnTouch = true;
        }
    });
    animator.start();
}

8、调用代码实现

public class MainActivity extends BaseActivity {

    private TitleView titleView;
    private CardContainerView mCardContainerView;
    private int[] imageIcon = {R.drawable.pic1, R.drawable.pic2, R.drawable.pic3, R.drawable.pic4,
            R.drawable.pic5, R.drawable.pic6, R.drawable.pic7, R.drawable.pic8,
            R.drawable.pic9, R.drawable.pic10, R.drawable.pic11, R.drawable.pic12};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        setListener();
        addCard(6);
    }

    private void initView() {
        titleView = (TitleView) findViewById(R.id.titleView);
        titleView.setAppTitle(UIUtils.getString(R.string.title));
        titleView.setLeftImageVisibility(View.GONE);
        mCardContainerView = (CardContainerView) findViewById(R.id.card_container);
        mCardContainerView.setLoadSize(3);
    }

    private void setListener() {
        mCardContainerView.setLoadMoreListener(new CardContainerView.LoadMore() {
            @Override
            public void load() {
                addCard(3);
            }
        });
        mCardContainerView.setLeftOrRightListener(new CardContainerView.LeftOrRight() {
            @Override
            public void leftOrRight(boolean left) {
                if (left) {
                    ToastUtil.showText(UIUtils.getString(R.string.like));
                } else {
                    ToastUtil.showText(UIUtils.getString(R.string.dislike));
                }
            }
        });
    }

    private void addCard(int size) {
        for (int i = 0; i < size; i++) {
            mCardContainerView.addView(getCard(getImageIcon(), getNickName(), getSignature()));
        }
    }

    private int getImageIcon() {
        int indexImageIcon = new Random().nextInt(imageIcon.length);
        return imageIcon[indexImageIcon];
    }

    private String getNickName() {
        String[] nickName = UIUtils.getResources().getStringArray(R.array.nickName);
        int indexNickName = new Random().nextInt(nickName.length);
        return nickName[indexNickName];
    }

    private String getSignature() {
        String[] signature = UIUtils.getResources().getStringArray(R.array.signature);
        int indexSignature = new Random().nextInt(signature.length);
        return signature[indexSignature];
    }

    private View getCard(int imageIcon, String nickName, String signature) {
        View card = LayoutInflater.from(this).inflate(R.layout.activity_card, null);
        RoundImageView mIvIcon = (RoundImageView) card.findViewById(R.id.iv_icon);
        TextView mNickName = (TextView) card.findViewById(R.id.tv_nickName);
        TextView mSignature = (TextView) card.findViewById(R.id.tv_signature);
        ImageView mIvLike = (ImageView) card.findViewById(R.id.iv_like);
        ImageView mIvDisLike = (ImageView) card.findViewById(R.id.iv_disLike);
        mIvIcon.setImageResource(imageIcon);
        mNickName.setText(nickName);
        mSignature.setText(signature);
        mIvLike.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCardContainerView.removeTopCard(true);
            }
        });
        mIvDisLike.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCardContainerView.removeTopCard(false);
            }
        });
        return card;
    }
}

项目地址 ☞ 传送门

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值