本篇文章主要记录一下开发过程通过网上搜索和本项目需求结合最终实现效果做个记录,
本人比较赖,就不抽demo了,关键代码已贴,仿探探卡片相关文件附git 下载链接,避免以后再各种搜索
附上最终效果图
//水波纹
implementation 'com.github.onlynight:WaveView:1.0.0'
<com.github.onlynight.waveview.WaveView
android:id="@+id/waveView"
android:layout_width="match_parent"
android:layout_height="@dimen/base440dp"
app:isCircle="false"
app:layout_constraintBottom_toBottomOf="parent"
app:period="2"
app:wave1Color="#80C3EEE1"
app:wave2Color="#80C3EEE1"
app:waveHeightPercent="0.5"
app:waveRange="10dp"
app:waveSpeed="7"
app:waveStrokeWidth="3dp" />
//初始化开启
waveView.start();//开启水波纹
@Override
public void onDestroy() {
super.onDestroy();
waveView.stop();
}
点击跳转相关自定义类(swip_card)
可自选变量(已上传)
public final 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 int DEFAULT_TRANSLATE_Y = -10; //重叠方向 正数向下
/**
* 卡片滑动时默认倾斜的角度
*/
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;
}
<android.support.v7.widget.RecyclerView
android:id="@+id/recy_sound_card"
android:layout_width="match_parent"
android:layout_height="@dimen/base300dp"
app:layout_constraintTop_toBottomOf="@+id/tv_sound_card" />
//滑动卡片初始化
private void initCardView() {
soundPublishCardAdapter = new SoundPublishCardAdapter(mActivity);
recySoundCard.setItemAnimator(new DefaultItemAnimator());
recySoundCard.setAdapter(soundPublishCardAdapter);
soundPublishCardAdapter.setNewData(cardList);
CardItemTouchHelperCallback cardCallback = new CardItemTouchHelperCallback(recySoundCard.getAdapter(), cardList);
final ItemTouchHelper touchHelper = new ItemTouchHelper(cardCallback);
final CardLayoutManager cardLayoutManager = new CardLayoutManager(recySoundCard, touchHelper);
recySoundCard.setLayoutManager(cardLayoutManager);
touchHelper.attachToRecyclerView(recySoundCard);
cardCallback.setOnSwipedListener(new OnSwipeListener() {
@Override
public void onSwiping(RecyclerView.ViewHolder viewHolder, float ratio, int direction) {
BaseViewHolder myHolder = (BaseViewHolder) viewHolder;
myHolder.itemView.setAlpha(1 - Math.abs(ratio) * 0.2f);
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, Object o, int direction) {
viewHolder.itemView.setAlpha(1f);
int position = viewHolder.getAdapterPosition()+1;
if (soundPublishCardAdapter.getData().size()>0){
currentCardBean = soundPublishCardAdapter.getData().get(position);
tvCardContent.setText(currentCardBean.getContent());
tvCardBelong.setText(StringUtil.getHTMLStr(currentCardBean.getWriter(),"\u2014\u2014"));
view_card_belong.setVisibility(TextUtils.isEmpty(currentCardBean.getWriter()) ? View.GONE : View.VISIBLE);
}
}
@Override
public void onSwipedClear() {
LogUtils.e("debug+onSwipedClear");
recySoundCard.postDelayed(new Runnable() {
@Override
public void run() {
presenter.getSoundCards(page,type_id);//循环
recySoundCard.getAdapter().notifyDataSetChanged();
}
}, 1000L);
}
});
}
//卡片Adapter展示
public class SoundPublishCardAdapter extends BaseQuickAdapter<SoundCardListBean, BaseViewHolder> {
private BaseActivity context;
public SoundPublishCardAdapter(BaseActivity activity) {
super(R.layout.item_sound_publish_card, new ArrayList<>());
this.context = activity;
}
@Override
protected void convert(final BaseViewHolder holder, final SoundCardListBean data) {
holder.setText(R.id.tv_card_content, data.getContent());
//getHTMLStr 遇到安卓手机修改系统字体后————显示断— — ,所以过滤本地处理替换View
holder.setText(R.id.tv_card_belong, StringUtil.getHTMLStr(data.getWriter(),"\u2014\u2014"));
View view_card_belong = holder.getView(R.id.view_card_belong);
view_card_belong.setVisibility(TextUtils.isEmpty(data.getWriter()) ? View.GONE : View.VISIBLE);
ConstraintLayout cl_layout = holder.getView(R.id.cl_layout);
if (holder.getAdapterPosition() == 0) {
cl_layout.setBackgroundResource(R.mipmap.icon_card_bg);
} else if (holder.getAdapterPosition() == 1) {
cl_layout.setBackgroundResource(R.drawable.shape_gradient_sound_card2);
} else {
//有透明度,所以二三层得文字不显示,
holder.setGone(R.id.tv_card_content,false);
holder.setGone(R.id.tv_card_belong,false);
holder.setGone(R.id.view_card_belong,false);
holder.setGone(R.id.tv_card_swip,false);
cl_layout.setBackgroundResource(R.drawable.shape_gradient_sound_card3);
}
}
}
/**
* 过滤html标签(——)
* @param htmlStr
* @return
*/
public static String getHTMLStr(String htmlStr,String regix){
Pattern p_html = Pattern.compile(regix, Pattern.CASE_INSENSITIVE);
Matcher m_html = p_html.matcher(htmlStr);
return m_html.replaceAll("");
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tool="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="@dimen/base250dp"
android:padding="@dimen/base15dp">
<android.support.constraint.ConstraintLayout
android:id="@+id/cl_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/icon_card_bg"
android:padding="@dimen/base15dp">
<TextView
android:id="@+id/tv_card_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingMultiplier="1.5"
android:padding="@dimen/base5dp"
android:textColor="@color/color_33615D"
android:textSize="@dimen/text16sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tool:text="别怪我心狠,为了圣教主的垂怜,我什么都可为了圣教主的垂怜" />
<TextView
android:id="@+id/tv_card_belong"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/base45dp"
tool:text="剑网3·牡丹"
android:textStyle="bold"
android:layout_marginEnd="@dimen/base8dp"
android:textColor="@color/color_33615D"
android:textSize="@dimen/text16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<View
android:id="@+id/view_card_belong"
android:layout_width="@dimen/base25dp"
android:layout_height="1dp"
app:layout_constraintEnd_toStartOf="@+id/tv_card_belong"
app:layout_constraintTop_toTopOf="@+id/tv_card_belong"
app:layout_constraintBottom_toBottomOf="@+id/tv_card_belong"
android:background="@color/color_33615D"/>
<TextView
android:id="@+id/tv_card_swip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="左右滑动可以更换卡片哦~"
android:textColor="@color/color_33615D"
android:textSize="@dimen/text12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
点击跳转相关图片(photo)
package com.huanqiu.miudeal.mvp.ui.view.card;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import com.huanqiu.miudeal.R;
import java.util.Random;
/**
* 定义我们自己的布局
* */
public class LoveLayout extends RelativeLayout {
private Context context;
private LayoutParams params;
private Drawable[] icons = new Drawable[6]; //图片数量对应
private Interpolator[] interpolators = new Interpolator[4];
private int mWidth;
private int mHeight;
public LoveLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
initView();
}
private void initView() {
// 图片资源
icons[0] = getResources().getDrawable(R.mipmap.icon_sound_anim1);
icons[1] = getResources().getDrawable(R.mipmap.icon_sound_anim2);
icons[2] = getResources().getDrawable(R.mipmap.icon_sound_anim3);
icons[3] = getResources().getDrawable(R.mipmap.icon_sound_anim4);
icons[4] = getResources().getDrawable(R.mipmap.icon_sound_anim5);
// 插值器
interpolators[0] = new AccelerateDecelerateInterpolator(); // 在动画开始与结束的地方速率改变比较慢,在中间的时候加速
interpolators[1] = new AccelerateInterpolator(); // 在动画开始的地方速率改变比较慢,然后开始加速
interpolators[2] = new DecelerateInterpolator(); // 在动画开始的地方快然后慢
interpolators[3] = new LinearInterpolator(); // 以常量速率改变
int width = icons[0].getIntrinsicWidth();
int height = icons[0].getIntrinsicWidth();
params = new LayoutParams(width, height);
params.addRule(CENTER_HORIZONTAL, TRUE);
params.addRule(ALIGN_PARENT_BOTTOM, TRUE);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
}
public void addLoveView() {
// TODO Auto-generated method stub
final ImageView iv = new ImageView(context);
iv.setLayoutParams(params);
iv.setImageDrawable(icons[new Random().nextInt(4)]);
addView(iv);
// 开启动画,并且用完销毁
AnimatorSet set = getAnimatorSet(iv);
set.start();
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// TODO Auto-generated method stub
super.onAnimationEnd(animation);
removeView(iv);
}
});
}
/**
* 获取动画集合
* @param iv
* */
private AnimatorSet getAnimatorSet(ImageView iv) {
// 1.alpha动画
ObjectAnimator alpha = ObjectAnimator.ofFloat(iv, "alpha", 0.3f, 1f);
// 2.缩放动画
ObjectAnimator scaleX = ObjectAnimator.ofFloat(iv, "scaleX", 0.2f, 1f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(iv, "scaleY", 0.2f, 1f);
// 动画集合
AnimatorSet set = new AnimatorSet();
set.playTogether(alpha, scaleX, scaleY);
set.setDuration(500);
// 贝塞尔曲线动画
ValueAnimator bzier = getBzierAnimator(iv);
AnimatorSet set2 = new AnimatorSet();
set2.playSequentially(set, bzier);
set2.setTarget(iv);
return set2;
}
/**
* 贝塞尔动画
* */
private ValueAnimator getBzierAnimator(final ImageView iv) {
// TODO Auto-generated method stub
PointF[] PointFs = getPointFs(iv); // 4个点的坐标
BasEvaluator evaluator = new BasEvaluator(PointFs[1], PointFs[2]);
ValueAnimator valueAnim = ValueAnimator.ofObject(evaluator, PointFs[0], PointFs[3]);
valueAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// TODO Auto-generated method stub
PointF p = (PointF) animation.getAnimatedValue();
iv.setX(p.x);
iv.setY(p.y);
iv.setAlpha(1- animation.getAnimatedFraction()); // 透明度
}
});
valueAnim.setTarget(iv);
valueAnim.setDuration(3000);
valueAnim.setInterpolator(interpolators[new Random().nextInt(4)]);
return valueAnim;
}
private PointF[] getPointFs(ImageView iv) {
// TODO Auto-generated method stub
PointF[] PointFs = new PointF[4];
PointFs[0] = new PointF(); // p0
PointFs[0].x = (mWidth- params.width)/ 2;
PointFs[0].y = mHeight - params.height;
PointFs[1] = new PointF(); // p1
PointFs[1].x = new Random().nextInt(mWidth);
PointFs[1].y = new Random().nextInt(mHeight /2) + mHeight / 2 + params.height;
PointFs[2] = new PointF(); // p2
PointFs[2].x = new Random().nextInt(mWidth);
PointFs[2].y = new Random().nextInt(mHeight /2);
PointFs[3] = new PointF(); // p3
PointFs[3].x = new Random().nextInt(mWidth);
PointFs[3].y = 0;
return PointFs;
}
}
* 注: 自定义部分代码来源于网络*