本文实例为大家分享了Android实现3D层叠式卡片图片展示的具体代码,供大家参考,具体内容如下
先看效果
好了效果看了,感兴趣的往下看哦!
整体实现思路
1、重写RelativeLayout 实现 锁定宽高比例的 RelativeLayout
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 为使用模板