recyclerView實現左右滑動的效果
阿新 • • 發佈:2019-02-14
這裡是一個通過自定義view和自定義RecyclerView的:layoutManager,再結合ItemTouchHelper實現的一個仿探探的卡片滑動的效果:
對應的介面卡裡面 的 class viewhodler 前面 換成 public 要不調不到
第一步:就是子佈局的對應的圖片和文字的佈局
第二部 對應的主佈局的recycleview<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" android:layout_height="wrap_content" xmlns:fresco="http://schemas.android.com/tools" android:background="@mipmap/ic_launcher" android:gravity="center" android:orientation="vertical"> <com.facebook.drawee.view.SimpleDraweeView android:id="@+id/img" android:layout_width="50dp" android:layout_height="50dp" fresco:placeholderImage="@mipmap/ic_launcher" fresco:placeholderImageScaleType="focusCrop" fresco:roundAsCircle="true" /> <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycleview"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
//建立一個CardConfig類
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 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; }
接下來建立一個CardItemTouchHelperCallback類
在建立一個CardLayoutMannager類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 = checkIsNull(adapter); this.dataList = checkIsNull(dataList); } public CardItemTouchHelperCallback(@NonNull RecyclerView.Adapter adapter, @NonNull List<T> dataList, OnSwipeListener<T> listener) { this.adapter = checkIsNull(adapter); this.dataList = checkIsNull(dataList); this.mListener = listener; } private <T> T checkIsNull(T t) { if (t == null) { throw new NullPointerException(); } return t; } public void setOnSwipedListener(OnSwipeListener<T> mListener) { this.mListener = mListener; } /** * 設定滑動型別標記 * * @param recyclerView * @param viewHolder * @return * 返回一個整數型別的標識,用於判斷Item那種移動行為是允許的 */ @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); } /** * 拖拽切換Item的回撥 * * @param recyclerView * @param viewHolder * @param target * @return * 如果Item切換了位置,返回true;反之,返回false */ @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { return false; } /** * * 劃出時會執行 * @param viewHolder * @param direction:左側劃出為:4,右側劃出為:8 */ @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { Log.d("mylog", "onSwiped: " + direction); // 移除 onTouchListener,否則觸控滑動會亂了 viewHolder.itemView.setOnTouchListener(null); int layoutPosition = viewHolder.getLayoutPosition(); T remove = dataList.remove(layoutPosition); adapter.notifyDataSetChanged(); if (mListener != null ) { mListener.onSwiped(viewHolder, remove, direction == ItemTouchHelper.LEFT ? CardConfig.SWIPED_LEFT : CardConfig.SWIPED_RIGHT); } // 當沒有資料時回撥 mListener if (adapter.getItemCount() == 0) { if (mListener != null) { mListener.onSwipedClear(); } } } /** * Item是否支援滑動 * * @return * true 支援滑動操作 * false 不支援滑動操作 */ @Override public boolean isItemViewSwipeEnabled() { return false; } /** * 拖動時會執行的方法 * @param c * @param recyclerView * @param viewHolder * @param dX * @param dY * @param actionState * @param isCurrentlyActive */ @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); // Log.d("mylog", "onChildDraw: 拖動"); 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; } Log.d("mylog", "onChildDraw: " + ratio); 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); 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.setScaleX(1 - index * CardConfig.DEFAULT_SCALE ); view.setScaleY(1 - index * 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) { // Log.d("mylog", "onChildDraw: 不為零"); mListener.onSwiping(viewHolder, ratio, ratio < 0 ? CardConfig.SWIPING_LEFT : CardConfig.SWIPING_RIGHT); } else { // Log.d("mylog", "onChildDraw: 為零"); mListener.onSwiping(viewHolder, ratio, CardConfig.SWIPING_NONE); } } } } @Override public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); viewHolder.itemView.setRotation(0f); } private float getThreshold(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { return recyclerView.getWidth() * getSwipeThreshold(viewHolder); } }
public class CardLayoutManager extends RecyclerView.LayoutManager {
private RecyclerView mRecyclerView;
private ItemTouchHelper mItemTouchHelper;
public CardLayoutManager(@NonNull RecyclerView recyclerView, @NonNull ItemTouchHelper itemTouchHelper) {
this.mRecyclerView = checkIsNull(recyclerView);
this.mItemTouchHelper = checkIsNull(itemTouchHelper);
}
private <T> T checkIsNull(T t) {
if (t ==null ) {
throw new NullPointerException();
}
return t;
}
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
@Override
public void onLayoutChildren(final RecyclerView.Recycler recycler, RecyclerView.State state) {
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() - getDecoratedMeasuredHeight(view);
// recyclerview 佈局
layoutDecoratedWithMargins(view, widthSpace / 2, heightSpace / 2,
widthSpace / 2 + getDecoratedMeasuredWidth(view),
heightSpace / 2 + getDecoratedMeasuredHeight(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;
}
};
}
在定義一個介面
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();
}
在建立
public class RoundImageView extends ImageView {
private Path mPath;
private RectF mRectF;
/*圓角的半徑,依次為左上角xy半徑,右上角,右下角,左下角*/
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) {
// Log.d("mylog", "onDraw: ");
//重置path
mPath.reset();
//p1:大小,p2:圓角,p3:CW:順時針繪製path,CCW:逆時針
mPath.addRoundRect(mRectF, rids, Path.Direction.CW);
//新增抗鋸齒
canvas.setDrawFilter(paintFlagsDrawFilter);
canvas.save();
//該方法不支援硬體加速,如果開啟會導致效果出不來,所以之前設定關閉硬體加速
//Clip(剪下)的時機:通常理解的clip(剪下),是對已經存在的圖形進行clip的。
// 但是,在android上是對canvas(畫布)上進行clip的,要在畫圖之前對canvas進行clip,
// 如果畫圖之後再對canvas進行clip不會影響到已經畫好的圖形。一定要記住clip是針對canvas而非圖形
//開始根據path裁剪
canvas.clipPath(mPath);
super.onDraw(canvas);
canvas.restore();
}
int a,b;
//執行在onDraw()之前
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// Log.d("mylog", "onSizeChanged: ");
a = w;
b = h;
mRectF = new RectF(0, 0, w, h);
Log.d("mylog", "onSizeChanged: "+w+"-----"+h);
}
}
對應的介面卡
public class HomeAdaper extends RecyclerView.Adapter {
Context context;
List<News.DataBean> list;
public HomeAdaper(Context context, List<News.DataBean> list) {
this.context = context;
this.list = list;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder holder = new MyViewHolder(LayoutInflater.from(
context).inflate(R.layout.listview, parent,
false));
return holder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
MyViewHolder holder1 = (MyViewHolder) holder;
holder1.tv.setText(list.get(position).getNews_title());
holder1.draweeView1.setImageURI(list.get(position).getPic_url());
//建立DraweeController
DraweeController controller = Fresco.newDraweeControllerBuilder()
//重試之後要載入的圖片URI地址
.setUri(list.get(position).getPic_url())
//設定點選重試是否開啟
.setTapToRetryEnabled(true)
//動畫播放
.setAutoPlayAnimations(true)
//設定舊的Controller
.setOldController(holder1.draweeView1.getController())
//構建
.build();
//設定DraweeController
holder1.draweeView1.setController(controller);
}
@Override
public int getItemCount() {
return list.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
SimpleDraweeView draweeView1;
TextView tv;
public MyViewHolder(View view) {
super(view);
tv = (TextView) view.findViewById(R.id.tv);
draweeView1 = (SimpleDraweeView) view.findViewById(R.id.img);
}
}
}
下面就是主頁面 的 程式碼 Activity 根據自己的MVP實現 view層
注意事項 裡面對應的UserPersenter 是對應自己的MVp 的 p層的東西 根據自己的MVP寫
Activity 裡面 的 public void getNews(Arrlist<News.Bean> list){}
activity實現 view層的方法
public class MainActivity extends AppCompatActivity implements IView{
RecyclerView recycler;
HomeAdaper homeadper;
UserPresenter userpresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
recycler = (RecyclerView)findViewById(R.id.recycleview);
recycler.setLayoutManager(new LinearLayoutManager(this));
userpresenter = new UserPresenter(this);
userpresenter.getUser(Api.HOME_URL);
recycler.setItemAnimator(new DefaultItemAnimator());
recycler.setAdapter(homeadper);
}
@Override
public void getNews(ArrayList<News.DataBean> list) {
recycler.setAdapter(homeadper =new HomeAdaper(this,list));
CardItemTouchHelperCallback cardCallback = new CardItemTouchHelperCallback(recycler.getAdapter(), list);
cardCallback.setOnSwipedListener(new OnSwipeListener<News.DataBean>() {
@Override
public void onSwiping(RecyclerView.ViewHolder viewHolder, float ratio, int direction) {
HomeAdaper.MyViewHolder myHolder = ( HomeAdaper.MyViewHolder) viewHolder;
viewHolder.itemView.setAlpha(1 - Math.abs(ratio) * 0.2f);
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, News.DataBean dataBean, int direction) {
HomeAdaper.MyViewHolder myHolder = (HomeAdaper.MyViewHolder) viewHolder;
viewHolder.itemView.setAlpha(1f);
}
@Override
public void onSwipedClear() {
Toast.makeText(MainActivity.this, "data clear", Toast.LENGTH_SHORT).show();
recycler.postDelayed(new Runnable() {
@Override
public void run() {
/* initData();*/
recycler.getAdapter().notifyDataSetChanged();
}
}, 3000L);
}
});
final ItemTouchHelper touchHelper = new ItemTouchHelper(cardCallback);
final CardLayoutManager cardLayoutManager = new CardLayoutManager(recycler, touchHelper);
recycler.setLayoutManager(cardLayoutManager);
touchHelper.attachToRecyclerView(recycler);
}
}
對應的Value下面建立 attrs
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RoundImageView">
<attr name="radius" format="reference|dimension" />
</declare-styleable>
</resources>
下面就是對應的依賴和許可權
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
compile 'io.reactivex:rxandroid:1.2.1'
//recyclreview依賴
compile 'com.android.support:recyclerview-v7:26.1.0'
implementation files('libs/Msc.jar')
compile 'com.facebook.fresco:fresco:0.12.0'
// 支援 GIF 動圖,需要新增
compile 'com.facebook.fresco:animated-gif:0.12.0'