1. 程式人生 > 程式設計 >Android實現3D層疊式卡片圖片展示

Android實現3D層疊式卡片圖片展示

本文例項為大家分享了Android實現3D層疊式卡片圖片展示的具體程式碼,供大家參考,具體內容如下

先看效果

好了效果看了,感興趣的往下看哦!

整體實現思路

1、重寫RelativeLayout 實現 鎖定寬高比例的 RelativeLayout

2、自定義一個支援滑動的面板 繼承 ViewGroup

3、卡片View繪製

4、頁面中使用佈局

首先為了更好的展示圖片我們重寫一下 RelativeLayout 編寫一個鎖定寬高比例的 RelativeLayout

AutoScaleRelativeLayout

public class AutoScaleRelativeLayout extends RelativeLayout {
 //寬高比例
 private float widthHeightRate = 0.35f;

 public AutoScaleRelativeLayout(Context context) {
  this(context,null);
 }

 public AutoScaleRelativeLayout(Context context,AttributeSet attrs) {
  this(context,attrs,0);
 }

 public AutoScaleRelativeLayout(Context context,AttributeSet attrs,int defStyleAttr) {
  super(context,defStyleAttr);
  //通過佈局獲取寬高比例
  TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.card,0);
  widthHeightRate = a.getFloat(R.styleable.card_widthHeightRate,widthHeightRate);
  a.recycle();
 }

 @Override
 protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec,heightMeasureSpec);

  // 調整高度
  int width = getMeasuredWidth();
  int height = (int) (width * widthHeightRate);
  ViewGroup.LayoutParams lp = getLayoutParams();
  lp.height = height;
  setLayoutParams(lp);
 }
}

這樣我們就編寫好了我們想要的父佈局

使用方法

<com.petterp.toos.ImageCard.AutoScaleRelativeLayout
  android:id="@+id/card_top_layout"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  card:widthHeightRate="0.6588">
<!--  widthHeightRate:就是設定寬高的百分比-->
  <ImageView
   android:id="@+id/card_image_view"
   android:layout_width="fill_parent"
   android:layout_height="match_parent"
   android:scaleType="fitXY" />
<!--    這是我們展示的圖片-->
  <View
   android:id="@+id/maskView"
   android:layout_width="fill_parent"
   android:layout_height="match_parent"
   android:background="?android:attr/selectableItemBackground"
   android:clickable="true" />
<!--  這個是為了讓我們圖片上有波紋-->
</com.petterp.toos.ImageCard.AutoScaleRelativeLayout>

接下來就是主要佈局,也就是展示圖片的佈局了

為了實現滑動我們編寫一個支援滑動的畫板

//事件處理
 @Override
 public boolean dispatchTouchEvent(MotionEvent ev) {
  int action = ev.getActionMasked();
  // 按下時儲存座標資訊
  if (action == MotionEvent.ACTION_DOWN) {
   this.downPoint.x = (int) ev.getX();
   this.downPoint.y = (int) ev.getY();
  }
  return super.dispatchTouchEvent(ev);
 }

 /* touch事件的攔截與處理都交給mDraghelper來處理 */
 @Override
 public boolean onInterceptTouchEvent(MotionEvent ev) {
  boolean shouldIntercept = mDragHelper.shouldInterceptTouchEvent(ev);
  boolean moveFlag = moveDetector.onTouchEvent(ev);
  int action = ev.getActionMasked();
  if (action == MotionEvent.ACTION_DOWN) {
   // ACTION_DOWN的時候就對view重新排序
   if (mDragHelper.getViewDragState() == ViewDragHelper.STATE_SETTLING) {
    mDragHelper.abort();
   }
   orderViewStack();

   // 儲存初次按下時arrowFlagView的Y座標
   // action_down時就讓mDragHelper開始工作,否則有時候導致異常
   mDragHelper.processTouchEvent(ev);
  }

  return shouldIntercept && moveFlag;
 }

 @Override
 public boolean onTouchEvent(MotionEvent e) {
  try {
   // 統一交給mDragHelper處理,由DragHelperCallback實現拖動效果
   // 該行程式碼可能會拋異常,正式釋出時請將這行程式碼加上try catch
   mDragHelper.processTouchEvent(e);
  } catch (Exception ex) {
   ex.printStackTrace();
  }
  return true;
 }
 //計算
 @Override
 protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {
  measureChildren(widthMeasureSpec,heightMeasureSpec);
  int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
  int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
  setMeasuredDimension(
    resolveSizeAndState(maxWidth,widthMeasureSpec,0),resolveSizeAndState(maxHeight,heightMeasureSpec,0));

  allWidth = getMeasuredWidth();
  allHeight = getMeasuredHeight();
 }
 //定位
 @Override
 protected void onLayout(boolean changed,int left,int top,int right,int bottom) {
  // 佈局卡片view
  int size = viewList.size();
  for (int i = 0; i < size; i++) {
   View viewItem = viewList.get(i);
   int childHeight = viewItem.getMeasuredHeight();
   int viewLeft = (getWidth() - viewItem.getMeasuredWidth()) / 2;
   viewItem.layout(viewLeft,itemMarginTop,viewLeft + viewItem.getMeasuredWidth(),itemMarginTop + childHeight);
   int offset = yOffsetStep * i;
   float scale = 1 - SCALE_STEP * i;
   if (i > 2) {
    // 備用的view
    offset = yOffsetStep * 2;
    scale = 1 - SCALE_STEP * 2;
   }

   viewItem.offsetTopAndBottom(offset);
   viewItem.setScaleX(scale);
   viewItem.setScaleY(scale);
  }

  // 佈局底部按鈕的View
  if (null != bottomLayout) {
   int layoutTop = viewList.get(0).getBottom() + bottomMarginTop;
   bottomLayout.layout(left,layoutTop,right,layoutTop
     + bottomLayout.getMeasuredHeight());
  }

  // 初始化一些中間引數
  initCenterViewX = viewList.get(0).getLeft();
  initCenterViewY = viewList.get(0).getTop();
  childWith = viewList.get(0).getMeasuredWidth();
 }
  //onFinishInflate 當View中所有的子控制元件均被對映成xml後觸發
 @Override
 protected void onFinishInflate() {
  super.onFinishInflate();
  // 渲染完成,初始化卡片view列表
  viewList.clear();
  int num = getChildCount();
  for (int i = num - 1; i >= 0; i--) {
   View childView = getChildAt(i);
   if (childView.getId() == R.id.card_bottom_layout) {
    bottomLayout = childView;
    initBottomLayout();
   } else {
    // for迴圈取view的時候,是從外層往裡取
    CardItemView viewItem = (CardItemView) childView;
    viewItem.setParentView(this);
    viewItem.setTag(i + 1);
    viewItem.maskView.setOnClickListener(btnListener);
    viewList.add(viewItem);
   }
  }

  CardItemView bottomCardView = viewList.get(viewList.size() - 1);
  bottomCardView.setAlpha(0);
 }

卡片View繪製

private void initSpring() {
  SpringConfig springConfig = SpringConfig.fromBouncinessAndSpeed(15,20);
  SpringSystem mSpringSystem = SpringSystem.create();
  springX = mSpringSystem.createSpring().setSpringConfig(springConfig);
  springY = mSpringSystem.createSpring().setSpringConfig(springConfig);

  springX.addListener(new SimpleSpringListener() {
   @Override
   public void onSpringUpdate(Spring spring) {
    int xPos = (int) spring.getCurrentValue();
    setScreenX(xPos);
    parentView.onViewPosChanged(CardItemView.this);
   }
  });

  springY.addListener(new SimpleSpringListener() {
   @Override
   public void onSpringUpdate(Spring spring) {
    int yPos = (int) spring.getCurrentValue();
    setScreenY(yPos);
    parentView.onViewPosChanged(CardItemView.this);
   }
  });
 }
 //裝載資料
 public void fillData(CardDataItem itemData) {
  Glide.with(getContext()).load(itemData.imagePath).into(imageView);


 }
 /**
  * 動畫移動到某個位置
  */
 public void animTo(int xPos,int yPos) {
  setCurrentSpringPos(getLeft(),getTop());
  springX.setEndValue(xPos);
  springY.setEndValue(yPos);
 }

 /**
  * 設定當前spring位置
  */
 private void setCurrentSpringPos(int xPos,int yPos) {
  springX.setCurrentValue(xPos);
  springY.setCurrentValue(yPos);
 }

接下來我們需要使用它 編寫Fragment佈局

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



 <com.petterp.toos.ImageCard.CardSlidePanel
  android:id="@+id/image_slide_panel"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  card:bottomMarginTop="38dp"
  card:itemMarginTop="10dp"
  card:yOffsetStep="26dp">

  <LinearLayout
   android:id="@+id/card_bottom_layout"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:gravity="center"
   android:orientation="horizontal">

   <Button
    android:background="#03A9F4"
    android:text="右側移除"
    android:id="@+id/card_left_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
   />


   <Button
    android:background="#03A9F4"
    android:text="右側移除"
    android:id="@+id/card_right_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    />
  </LinearLayout>

  <com.petterp.toos.ImageCard.CardItemView
   android:layout_width="match_parent"
   android:layout_height="wrap_content" />

  <com.petterp.toos.ImageCard.CardItemView
   android:layout_width="match_parent"
   android:layout_height="wrap_content" />

  <com.petterp.toos.ImageCard.CardItemView
   android:layout_width="match_parent"
   android:layout_height="wrap_content" />

  <com.petterp.toos.ImageCard.CardItemView
   android:layout_width="match_parent"
   android:layout_height="wrap_content" />

 </com.petterp.toos.ImageCard.CardSlidePanel>

</LinearLayout>

程式碼中的使用

private void initView(View rootView) {
  CardSlidePanel slidePanel = (CardSlidePanel) rootView
    .findViewById(R.id.image_slide_panel);
  cardSwitchListener = new CardSlidePanel.CardSwitchListener() {

   @Override
   public void onShow(int index) {
    Toast.makeText(getContext(),"CardFragment"+"正在顯示=" +index,Toast.LENGTH_SHORT).show();

   }
   //type 0=右邊 ,-1=左邊
   @Override
   public void onCardVanish(int index,int type) {
    Toast.makeText(getContext(),"CardFragment"+ "正在消失=" + index + " 消失type=" + type,Toast.LENGTH_SHORT).show();
   }

   @Override
   public void onItemClick(View cardView,int index) {
    Toast.makeText(getContext(),"CardFragment"+"卡片點選=" + index,Toast.LENGTH_SHORT).show();
   }
  };
  slidePanel.setCardSwitchListener(cardSwitchListener);
  prepareDataList();
  slidePanel.fillData(dataList);
 }
 //封裝資料
 private void prepareDataList() {
  int num = imagePaths.length;
  //重複新增資料10次(測試資料太少)
  for (int j = 0; j < 10; j++) {
   for (int i = 0; i < num; i++) {
    CardDataItem dataItem = new CardDataItem();
    dataItem.imagePath = imagePaths[i];
    dataList.add(dataItem);
   }
  }
 }

到此主要邏輯程式碼就編寫完了

詳細說明程式碼中已經註釋 ,全部程式碼請看原始碼

原始碼:github原始碼

原始碼中的TestCardFragment 為使用模板

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。