本篇文章主要記錄一下開發過程通過網上搜索和本項目需求結合最終實現效果做個記錄,
本人比較賴,就不抽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();
}
二、仿探探滑動卡片
可自選變量(已上傳)
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>
三、飄贊動畫
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;
}
}
* 注: 自定義部分代碼來源於網絡*