Android realizes 3D laminated card image display
This example shares the specific code for Android to realize 3D laminated card image display for your reference. The specific contents are as follows
Look at the effect first
Well, look at the effect. Look down if you are interested!
Overall realization idea
1. Override relativelayout to lock the relativelayout of width height ratio
2. Customize a panel that supports sliding and inherit ViewGroup
3. Card view drawing
4. Use layout in page
First, in order to better show the picture, we rewrite the relativelayout and write a relativelayout that locks the width height ratio
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); } }
In this way, we have written the parent layout we want
usage method
<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>
Next is the main layout, that is, the layout of the display pictures
In order to realize sliding, we write a drawing board that supports sliding
//事件处理 @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); }
Card view drawing
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); }
Next, we need to use it to write the fragment layout
<?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>
Use in code
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); } } }
At this point, the main logic code has been written
Source code: GitHub source code
The testcardfragment in the source code is a template
The above is the whole content of this article. I hope it will help you in your study, and I hope you will support us a lot.