Android如何实现3D层叠式卡片图片展示
创始人
2024-12-20 04:20:24
0次

整体实现思路

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, 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); }}

这样我们就编写好了我们想要的父布局

使用方法

    

接下来就是主要布局,也就是展示图片的布局了

为了实现滑动我们编写一个支持滑动的画板

//事件处理 @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布局

                    

代码中的使用

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);   }  } }

关于“Android如何实现3D层叠式卡片图片展示”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

相关内容

热门资讯

空调连续工作多久需要停机休息 空调连续工作的时间取决于多个因素,包括空调的型号、功率、使用环境和维护情况等。一般来说,空调可以连续...
空调哪个符号是除湿的 空调除湿功能通常用一个水滴形状的符号表示。不同品牌和型号的空调遥控器可能会有所差异,但一般来说,除湿...
格力空调多长时间加一次氟利昂 1、一般情况下,空调对氟利昂的使用量是非常小的,所以在空调使用了四年左右的时候再考虑加也没问题。2、...
空调用多长时间需要加氟一次 一般情况下是五年。当出现以下这几种情况时,空调才需要加氟。1、空调的室内机和室外机各有两个接口,当接...
格调生活·C标思享荟:卡萨帝在... 9月12日的郑州龙湖金融岛,夜风轻拂,灯光璀璨。“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑...
品牌价值破千亿、高端份额持续领... 9月12日,“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑州站”在郑州龙湖金融岛举办。2026...
空调主板坏了修800还是换新的 空调主板坏了是修还是换新,需要考虑多个因素,以下是一些建议:1. 维修成本:如果维修费用较低,比如 ...
空调开多久需要休息多长时间 问题:空调开多久需要休息多长时间回答:1、在使用空调的过程中,一般空调开7~8个小时左右需要休息2~...
中央空调多长时间清洗一次 1、一般空调开机前清洗一次即可,空调开机中间时段再清洗一次,空调关机时清洗一次,这样比较合理。2、空...
中央空调氟利昂多久加一次 看空调制热和制冷程度,很多人的观点认为氟利昂必须一年放一次,其实这个观点是错误的,理解上面有点偏差,...