Android實(shí)現(xiàn)3D層疊式卡片圖片展示
本文實(shí)例為大家分享了Android實(shí)現(xiàn)3D層疊式卡片圖片展示的具體代碼,供大家參考,具體內(nèi)容如下
先看效果
好了效果看了,感興趣的往下看哦!
整體實(shí)現(xiàn)思路
1、重寫(xiě)RelativeLayout 實(shí)現(xiàn) 鎖定寬高比例的 RelativeLayout
2、自定義一個(gè)支持滑動(dòng)的面板 繼承 ViewGroup
3、卡片View繪制
4、頁(yè)面中使用布局
首先為了更好的展示圖片我們重寫(xiě)一下 RelativeLayout 編寫(xiě)一個(gè)鎖定寬高比例的 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, attrs, defStyleAttr);
  //通過(guò)布局獲取寬高比例
  TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.card, 0, 0);
  widthHeightRate = a.getFloat(R.styleable.card_widthHeightRate, widthHeightRate);
  a.recycle();
 }
 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  // 調(diào)整高度
  int width = getMeasuredWidth();
  int height = (int) (width * widthHeightRate);
  ViewGroup.LayoutParams lp = getLayoutParams();
  lp.height = height;
  setLayoutParams(lp);
 }
}
這樣我們就編寫(xiě)好了我們想要的父布局
使用方法
<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:就是設(shè)置寬高的百分比--> <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" /> <!-- 這個(gè)是為了讓我們圖片上有波紋--> </com.petterp.toos.ImageCard.AutoScaleRelativeLayout>
接下來(lái)就是主要布局,也就是展示圖片的布局了
為了實(shí)現(xiàn)滑動(dòng)我們編寫(xiě)一個(gè)支持滑動(dòng)的畫(huà)板
//事件處理
 @Override
 public boolean dispatchTouchEvent(MotionEvent ev) {
  int action = ev.getActionMasked();
  // 按下時(shí)保存坐標(biāo)信息
  if (action == MotionEvent.ACTION_DOWN) {
   this.downPoint.x = (int) ev.getX();
   this.downPoint.y = (int) ev.getY();
  }
  return super.dispatchTouchEvent(ev);
 }
 /* touch事件的攔截與處理都交給mDraghelper來(lái)處理 */
 @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的時(shí)候就對(duì)view重新排序
   if (mDragHelper.getViewDragState() == ViewDragHelper.STATE_SETTLING) {
    mDragHelper.abort();
   }
   orderViewStack();
   // 保存初次按下時(shí)arrowFlagView的Y坐標(biāo)
   // action_down時(shí)就讓mDragHelper開(kāi)始工作,否則有時(shí)候?qū)е庐惓?
   mDragHelper.processTouchEvent(ev);
  }
  return shouldIntercept && moveFlag;
 }
 @Override
 public boolean onTouchEvent(MotionEvent e) {
  try {
   // 統(tǒng)一交給mDragHelper處理,由DragHelperCallback實(shí)現(xiàn)拖動(dòng)效果
   // 該行代碼可能會(huì)拋異常,正式發(fā)布時(shí)請(qǐng)將這行代碼加上try catch
   mDragHelper.processTouchEvent(e);
  } catch (Exception ex) {
   ex.printStackTrace();
  }
  return true;
 }
 //計(jì)算
 @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());
  }
  // 初始化一些中間參數(shù)
  initCenterViewX = viewList.get(0).getLeft();
  initCenterViewY = viewList.get(0).getTop();
  childWith = viewList.get(0).getMeasuredWidth();
 }
  //onFinishInflate 當(dāng)View中所有的子控件均被映射成xml后觸發(fā)
 @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循環(huán)取view的時(shí)候,是從外層往里取
    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);
   }
  });
 }
 //裝載數(shù)據(jù)
 public void fillData(CardDataItem itemData) {
  Glide.with(getContext()).load(itemData.imagePath).into(imageView);
 }
 /**
  * 動(dòng)畫(huà)移動(dòng)到某個(gè)位置
  */
 public void animTo(int xPos, int yPos) {
  setCurrentSpringPos(getLeft(), getTop());
  springX.setEndValue(xPos);
  springY.setEndValue(yPos);
 }
 /**
  * 設(shè)置當(dāng)前spring位置
  */
 private void setCurrentSpringPos(int xPos, int yPos) {
  springX.setCurrentValue(xPos);
  springY.setCurrentValue(yPos);
 }
接下來(lái)我們需要使用它 編寫(xiě)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="右側(cè)移除"
    android:id="@+id/card_left_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
   />
   <Button
    android:background="#03A9F4"
    android:text="右側(cè)移除"
    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"+"卡片點(diǎn)擊=" + index, Toast.LENGTH_SHORT).show();
   }
  };
  slidePanel.setCardSwitchListener(cardSwitchListener);
  prepareDataList();
  slidePanel.fillData(dataList);
 }
 //封裝數(shù)據(jù)
 private void prepareDataList() {
  int num = imagePaths.length;
  //重復(fù)添加數(shù)據(jù)10次(測(cè)試數(shù)據(jù)太少)
  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);
   }
  }
 }
到此主要邏輯代碼就編寫(xiě)完了
詳細(xì)說(shuō)明代碼中已經(jīng)注釋 ,全部代碼請(qǐng)看源碼
源碼:github源碼
源碼中的TestCardFragment 為使用模板
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。
上一篇:關(guān)于ADB的Android Debug Bridge(安卓調(diào)試橋)那些事
欄 目:Android
下一篇:淺析Android高斯模糊實(shí)現(xiàn)方案
本文標(biāo)題:Android實(shí)現(xiàn)3D層疊式卡片圖片展示
本文地址:http://www.jygsgssxh.com/a1/Android/9124.html
您可能感興趣的文章
- 01-10Android自定義View之繪制圓形頭像功能
 - 01-10Android實(shí)現(xiàn)雙擊返回鍵退出應(yīng)用實(shí)現(xiàn)方法詳解
 - 01-10android實(shí)現(xiàn)記住用戶(hù)名和密碼以及自動(dòng)登錄
 - 01-10android實(shí)現(xiàn)簡(jiǎn)單計(jì)算器功能
 - 01-10Android 友盟第三方登錄與分享的實(shí)現(xiàn)代碼
 - 01-10C++自定義API函數(shù)實(shí)現(xiàn)大數(shù)相乘算法
 - 01-10如何給Flutter界面切換實(shí)現(xiàn)點(diǎn)特效
 - 01-10android實(shí)現(xiàn)指紋識(shí)別功能
 - 01-10Emoji表情在Android JNI中的兼容性問(wèn)題詳解
 - 01-10Android實(shí)現(xiàn)圓形漸變加載進(jìn)度條
 


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
 - 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
 - 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
 - 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
 - 5c語(yǔ)言計(jì)算三角形面積代碼
 - 6什么是 WSH(腳本宿主)的詳細(xì)解釋
 - 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
 - 8正則表達(dá)式匹配各種特殊字符
 - 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
 - 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
 
本欄相關(guān)
- 01-10Android自定義View之繪制圓形頭像功能
 - 01-10Android實(shí)現(xiàn)雙擊返回鍵退出應(yīng)用實(shí)現(xiàn)方
 - 01-10android實(shí)現(xiàn)簡(jiǎn)單計(jì)算器功能
 - 01-10android實(shí)現(xiàn)記住用戶(hù)名和密碼以及自動(dòng)
 - 01-10C++自定義API函數(shù)實(shí)現(xiàn)大數(shù)相乘算法
 - 01-10Android 友盟第三方登錄與分享的實(shí)現(xiàn)代
 - 01-10android實(shí)現(xiàn)指紋識(shí)別功能
 - 01-10如何給Flutter界面切換實(shí)現(xiàn)點(diǎn)特效
 - 01-10Android實(shí)現(xiàn)圓形漸變加載進(jìn)度條
 - 01-10Emoji表情在Android JNI中的兼容性問(wèn)題詳
 
隨機(jī)閱讀
- 01-10delphi制作wav文件的方法
 - 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
 - 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
 - 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文
 - 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
 - 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
 - 01-10C#中split用法實(shí)例總結(jié)
 - 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
 - 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
 - 04-02jquery與jsp,用jquery
 


