本文实例为大家分享了Android实现3D层叠式卡片图片展示的具体代码,供大家参考,具体内容如下
先看效果
好了效果看了,感兴趣的往下看哦!
整体实现思路
1、重写RelativeLayout 实现 锁定宽高比例的 RelativeLayout
2、自定义一个支持滑动的面板 继承 ViewGroup
3、卡片View绘制
4、页面中使用布局
首先为了更好的展示图片我们重写一下 RelativeLayout 编写一个锁定宽高比例的 RelativeLayout
AutoScaleRelativeLayout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
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); //通过布局获取宽高比例 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); // 调整高度 int width = getMeasuredWidth(); int height = ( int ) (width * widthHeightRate); ViewGroup.LayoutParams lp = getLayoutParams(); lp.height = height; setLayoutParams(lp); } } |
这样我们就编写好了我们想要的父布局
使用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
< 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 > |
接下来就是主要布局,也就是展示图片的布局了
为了实现滑动我们编写一个支持滑动的画板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
//事件处理 @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绘制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
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布局
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
<? 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 > |
代码中的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
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 为使用模板
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/duihuapixiu/article/details/102795767