当前位置: 首页 > news >正文

Android之卡片式滑动

文章目录

  • 前言
  • 一、效果图
  • 二、实现步骤
    • 1.主界面xml
    • 2.自定义的viewpage
    • 3.卡片接口类
    • 4.阴影和缩放变化类
    • 5.卡片adapter
    • 6.卡片adapter的xml
    • 7.style
    • 8.CardItem
    • 9.activity实现
    • 10.指示器drawable
  • 总结


前言

对于这个需求,之前的项目也有做过,但是过于赶项目就没有放博客上,刚好这次又遇到这个需求,所以就记录一下,也希望能帮到正在实现此功能的朋友们少走一些弯路。


一、效果图

在这里插入图片描述

二、实现步骤

1.主界面xml

自定义的ViewPage和一个LinearLayout

		<com.hzwl.aidigital.utils.CardViewPage
            android:id="@+id/viewPager"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/backhsColor"
            android:clipToPadding="false"
            android:elevation="0dp"
            android:overScrollMode="never"
            android:paddingLeft="40dp"
            android:paddingTop="50dp"
            android:paddingRight="40dp"
            android:paddingBottom="50dp" />
            
          <LinearLayout
            android:id="@+id/linear"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:orientation="horizontal"></LinearLayout>

2.自定义的viewpage

代码如下(示例):

/**
 * @Author : CaoLiulang
 * @Time : 2025/3/22 15:21
 * @Description :自定义ViewPager
 */
public class CardViewPage  extends ViewPager {
    private   float  mLastOffset;
    private CardAdapter cardAdapter;

    public CardViewPage(Context context) {
        super(context);
    }

    public CardViewPage(Context context, AttributeSet attrs) {
        super(context, attrs);
    }


    @Override
    protected void onPageScrolled(int position, float positionOffset, int offsetPixels) {
        super.onPageScrolled(position, positionOffset, offsetPixels);
        cardAdapter = (CardAdapter) getAdapter();
        if (cardAdapter ==null)
        {
            return;
        }

        // If we're going backwards, onPageScrolled receives the last position
        // instead of the current one
        int realCurrentPosition;
        int nextPosition;
        float realOffset;
        //positionOffset  如果往左边滑动就是逐渐变大  0->1 ,然后归0,如果往右滑动  1-》0  ,最后归0。
        //下面这个判断区分左右,
        boolean goingLeft = mLastOffset > positionOffset;
        if (goingLeft) {
            realCurrentPosition = position + 1;
            nextPosition = position;
            realOffset = 1 - positionOffset;
        } else {
            nextPosition = position + 1;
            realCurrentPosition = position;
            realOffset = positionOffset;
        }

        if (nextPosition > getAdapter().getCount() - 1
                || realCurrentPosition > cardAdapter.getCount() - 1) {
            return;
        }
        CardView currentCard = cardAdapter.getCardViewAt(realCurrentPosition);
        if (currentCard!=null)
        {
            float  scclex=(float) (1 + 0.1 * (1 - realOffset));
            float  sccley=(float)(1 + 0.1 * (1 - realOffset));
            currentCard.setScaleX(scclex);
            currentCard.setScaleY(sccley);
            currentCard.setCardElevation((cardAdapter.getBaseElevation() + cardAdapter.getBaseElevation()
                    * (CardAdapter.MAX_ELEVATION_FACTOR - 1) * (1 - realOffset)));

        }


        CardView nextCard = cardAdapter.getCardViewAt(nextPosition);

        // We might be scrolling fast enough so that the next (or previous) card
        // was already destroyed or a fragment might not have been created yet
        if (nextCard != null) {
            nextCard.setScaleX((float) (1 + 0.1 * (realOffset)));
            nextCard.setScaleY((float) (1 + 0.1 * (realOffset)));
            nextCard.setCardElevation((cardAdapter.getBaseElevation() + cardAdapter.getBaseElevation()
                    * (CardAdapter.MAX_ELEVATION_FACTOR - 1) * (realOffset)));
        }

        mLastOffset = positionOffset;


    }




}

3.卡片接口类

/**
 * @Author : CaoLiulang
 * @Time : 2025/3/22 14:19
 * @Description :卡片接口类
 */

public interface CardAdapter {

    int MAX_ELEVATION_FACTOR = 5;

    float getBaseElevation();

    CardView getCardViewAt(int position);

    int getCount();
}

4.阴影和缩放变化类

/**
 * @Author : CaoLiulang
 * @Time : 2025/3/22 14:18
 * @Description :阴影和缩放变化类
 */

public class ShadowTransformer implements ViewPager.OnPageChangeListener, ViewPager.PageTransformer {

    private ViewPager mViewPager;
    private CardAdapter mAdapter;
    private float mLastOffset;
    private boolean mScalingEnabled;

    public ShadowTransformer(ViewPager viewPager, CardAdapter adapter) {
        mViewPager = viewPager;
        viewPager.addOnPageChangeListener(this);
        mAdapter = adapter;
    }

    public void enableScaling(boolean enable) {
        if (mScalingEnabled && !enable) {
            // shrink main card
            CardView currentCard = mAdapter.getCardViewAt(mViewPager.getCurrentItem());
            if (currentCard != null) {
                currentCard.animate().scaleY(1);
                currentCard.animate().scaleX(1);
            }
        }else if(!mScalingEnabled && enable){
            // grow main card
            CardView currentCard = mAdapter.getCardViewAt(mViewPager.getCurrentItem());
            if (currentCard != null) {
                currentCard.animate().scaleY(1.1f);
                currentCard.animate().scaleX(1.1f);
            }
        }

        mScalingEnabled = enable;
    }

    @Override
    public void transformPage(View page, float position) {

    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        int realCurrentPosition;
        int nextPosition;
        float baseElevation = mAdapter.getBaseElevation();
        float realOffset;
        boolean goingLeft = mLastOffset > positionOffset;

        // If we're going backwards, onPageScrolled receives the last position
        // instead of the current one
        if (goingLeft) {
            realCurrentPosition = position + 1;
            nextPosition = position;
            realOffset = 1 - positionOffset;
        } else {
            nextPosition = position + 1;
            realCurrentPosition = position;
            realOffset = positionOffset;
        }

        // Avoid crash on overscroll
        if (nextPosition > mAdapter.getCount() - 1
                || realCurrentPosition > mAdapter.getCount() - 1) {
            return;
        }

        CardView currentCard = mAdapter.getCardViewAt(realCurrentPosition);

        // This might be null if a fragment is being used
        // and the views weren't created yet
        if (currentCard != null) {
            if (mScalingEnabled) {
                currentCard.setScaleX((float) (1 + 0.1 * (1 - realOffset)));
                currentCard.setScaleY((float) (1 + 0.1 * (1 - realOffset)));
            }
            currentCard.setCardElevation((baseElevation + baseElevation
                    * (CardAdapter.MAX_ELEVATION_FACTOR - 1) * (1 - realOffset)));
        }

        CardView nextCard = mAdapter.getCardViewAt(nextPosition);

        // We might be scrolling fast enough so that the next (or previous) card
        // was already destroyed or a fragment might not have been created yet
        if (nextCard != null) {
            if (mScalingEnabled) {
                nextCard.setScaleX((float) (1 + 0.1 * (realOffset)));
                nextCard.setScaleY((float) (1 + 0.1 * (realOffset)));
            }
            nextCard.setCardElevation((baseElevation + baseElevation
                    * (CardAdapter.MAX_ELEVATION_FACTOR - 1) * (realOffset)));
        }

        mLastOffset = positionOffset;
    }

    @Override
    public void onPageSelected(int position) {

    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
}

5.卡片adapter

/**
 * @Author : CaoLiulang
 * @Time : 2025/3/22 14:18
 * @Description :卡片adapter
 */
public class CardPagerAdapter extends PagerAdapter implements CardAdapter {

    private List<CardView> mViews;
    private List<CardItem> mData;
    private float mBaseElevation;

    public CardPagerAdapter() {
        mData = new ArrayList<>();
        mViews = new ArrayList<>();
    }

    public void addCardItem(CardItem item) {
        mViews.add(null);
        mData.add(item);
    }

    public float getBaseElevation() {
        return mBaseElevation;
    }

    @Override
    public CardView getCardViewAt(int position) {
        return mViews.get(position);
    }

    @Override
    public int getCount() {
        return mData.size();
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        View view = LayoutInflater.from(container.getContext())
                .inflate(R.layout.adapter, container, false);
        container.addView(view);
        bind(mData.get(position), view);
        CardView cardView = view.findViewById(R.id.cardView);

        if (mBaseElevation == 0) {
            mBaseElevation = cardView.getCardElevation();
        }

        cardView.setMaxCardElevation(mBaseElevation * MAX_ELEVATION_FACTOR);
        mViews.set(position, cardView);
        return view;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View) object);
        mViews.set(position, null);
    }

    private void bind(CardItem item, View view) {
        ImageView imag_back = view.findViewById(R.id.imag_back);
        ImageView imag_logo = view.findViewById(R.id.imag_logo);
        TextView text_ok = view.findViewById(R.id.text_ok);
        if (item.getmTitleResource().equals("抖音")) {
            imag_back.setImageResource(R.mipmap.cardy);
            imag_logo.setImageResource(R.mipmap.ico_dy);
        } else if (item.getmTitleResource().equals("快手")) {
            imag_back.setImageResource(R.mipmap.carks);
            imag_logo.setImageResource(R.mipmap.ico_ks);
        } else if (item.getmTitleResource().equals("视频号")) {
            imag_back.setImageResource(R.mipmap.carsph);
            imag_logo.setImageResource(R.mipmap.ico_sph);
        } else if (item.getmTitleResource().equals("小红书")) {
            imag_back.setImageResource(R.mipmap.carxhs);
            imag_logo.setImageResource(R.mipmap.ico_xhs);
        } else if (item.getmTitleResource().equals("美团")) {
            imag_back.setImageResource(R.mipmap.carmt);
            imag_logo.setImageResource(R.mipmap.ico_mt);
        } else if (item.getmTitleResource().equals("拼多多")) {
            imag_back.setImageResource(R.mipmap.carpdd);
            imag_logo.setImageResource(R.mipmap.ico_pdd);
        } else if (item.getmTitleResource().equals("京东")) {
            imag_back.setImageResource(R.mipmap.carjd);
            imag_logo.setImageResource(R.mipmap.ico_jd);
        } else if (item.getmTitleResource().equals("淘宝")) {
            imag_back.setImageResource(R.mipmap.cartb);
            imag_logo.setImageResource(R.mipmap.ico_tb);
        } else if (item.getmTitleResource().equals("支付宝")) {
            imag_back.setImageResource(R.mipmap.carzfb);
            imag_logo.setImageResource(R.mipmap.ico_zbzfb);
        } else if (item.getmTitleResource().equals("百度")) {
            imag_back.setImageResource(R.mipmap.carbd);
            imag_logo.setImageResource(R.mipmap.ico_bd);
        }
        text_ok.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ToastUtils.showToast("点击了" + item.getmTitleResource());
            }
        });
    }

}

6.卡片adapter的xml

这里一定要引用样式,不然会有阴影,然后有边框线,试过xml各种设置都不行,必须要样式

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cardView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center"
    style="@style/CustomCardView"
    app:cardElevation="0dp"
    app:cardUseCompatPadding="false">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <RelativeLayout
            android:layout_width="270dp"
            android:layout_height="498dp"
            android:layout_centerHorizontal="true">

            <ImageView
                android:id="@+id/imag_back"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="fitXY"
                android:src="@mipmap/cardy" />
            <ImageView
                android:id="@+id/imag_logo"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="28dp"
                android:src="@mipmap/ico_dy"/>

            <TextView
                android:id="@+id/text_ok"
                android:layout_width="200dp"
                android:layout_height="54dp"
                android:layout_below="@+id/imag_back"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="-70dp"
                android:gravity="center"
                android:textColor="#FF4400"
                android:textSize="14dp"
                android:textStyle="bold" />
        </RelativeLayout>

    </RelativeLayout>

</androidx.cardview.widget.CardView>

7.style

 <style name="CustomCardView" parent="CardView">
        <item name="cardBackgroundColor">#020D1B</item>
        <item name="cardElevation">0dp</item>
        <item name="cardCornerRadius">8dp</item>
    </style>

8.CardItem

/**
 * @Author : CaoLiulang
 * @Time : 2025/3/22 14:19
 * @Description :卡片Javabean
 */
public class CardItem {

    private String mTitleResource;

    public CardItem(String title) {
        mTitleResource = title;
    }

    public String getmTitleResource() {
        return mTitleResource;
    }

    public void setmTitleResource(String mTitleResource) {
        this.mTitleResource = mTitleResource;
    }
}

9.activity实现

1.自定义变量
	private lateinit var imag_fh:ImageView
    private lateinit var text_title:TextView
    private lateinit var viewPager: CardViewPage
    private lateinit var mCardAdapter: CardPagerAdapter
    private lateinit var mCardShadowTransformer: ShadowTransformer
    private lateinit var linear: LinearLayout
    private lateinit var list: MutableList<String>
    private lateinit var view: View
    private var mNum = 2

2.代码部分
   list = mutableListOf()
        list.add("抖音")
        list.add("快手")
        list.add("视频号")
        list.add("拼多多")
        list.add("京东")
        list.add("淘宝")
        list.add("支付宝")
        list.add("百度")
        list.add("美团")
        list.add("小红书")
        imag_fh=findViewById(R.id.imag_fh)
        text_title=findViewById(R.id.text_title)
        text_title.text="选择直播平台"
        imag_fh.setOnClickListener(this)
        linear = findViewById(R.id.linear)
        viewPager = findViewById(R.id.viewPager)
        mCardAdapter = CardPagerAdapter()
        for (i in list.indices) {
            mCardAdapter.addCardItem(CardItem(list[i]))
            //创建底部指示器(小圆点)
            view = View(this)
            view.setBackgroundResource(R.drawable.background)
            view.isEnabled = false
            //设置宽高
            val layoutParams = LinearLayout.LayoutParams(30, 30)
            //设置间隔
            if (i != 0) {
                layoutParams.leftMargin = 10
            }
            //添加到LinearLayout
            linear.addView(view, layoutParams)
        }
        mCardShadowTransformer = ShadowTransformer(viewPager, mCardAdapter)
        mCardShadowTransformer.enableScaling(true)
        viewPager.setAdapter(mCardAdapter)
        viewPager.setPageTransformer(false, mCardShadowTransformer)
        //预加载几个界面
        viewPager.setOffscreenPageLimit(5)
        //默认显示第三个界面
        viewPager.setCurrentItem(2)
        //第一次显示小白点
        linear.getChildAt(2).setEnabled(true)
        //注册
        viewPager.addOnPageChangeListener(object : OnPageChangeListener {
            override fun onPageScrolled(
                position: Int,
                positionOffset: Float,
                positionOffsetPixels: Int
            ) {
            }

            override fun onPageSelected(position: Int) {
                linear.getChildAt(mNum).setEnabled(false)
                linear.getChildAt(position).setEnabled(true)
                mNum = position
            }

            override fun onPageScrollStateChanged(state: Int) {
            }
        })

10.指示器drawable

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/enable" android:state_enabled="true" />
    <item android:drawable="@drawable/disable" android:state_enabled="false" />
</selector>

1.enable

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <!--白色-->
    <solid android:color="#ffffff" />
    <!--半径-->
    <corners android:radius="10dp" />
</shape>

2.disable

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <!--灰色-->
    <solid android:color="#807E7E" />
    <!--半径-->
    <corners android:radius="10dp" />
</shape>

总结

以上就是整个卡片滑动效果的实现步骤和代码,是不是很简单,欢迎各位点评指正。

相关文章:

  • 零基础上手Python数据分析 (9):DataFrame 数据读取与写入 - 让数据自由穿梭
  • 基于Java的班级事务管理系统(源码+lw+部署文档+讲解),源码可白嫖!
  • HarmonyOS-ArkUI Grip组件
  • Charles汉化步骤 charles中文版怎么用
  • 凝视型高光谱相机:钻石光谱分析研究与应用
  • PoE交换机如何助力智慧城市基础设施建设?
  • C# 如何检查给定的四个点是否形成一个正方形(How to check if given four points form a square)
  • docker ssh远程连接
  • uni app跨端开发遇到的问题
  • Linux搭建本地时间服务器及时间同步
  • mysql中show命令的使用
  • react-activation 实现页面保活记录
  • 前端模拟 websocket 请求小工具
  • mac vim命令快捷键
  • LeetCode热题100精讲——Top7:接雨水【双指针】
  • 树莓派5-GPIO和40针引脚
  • redis使用
  • 手动创建kkFileView4.4.0镜像
  • C#基础学习(二)C#数组生存手册:从入门到“血压拉满“的奇妙旅程
  • Socket如何实现客户端和服务器间的通信
  • 衡天主机怎么做网站/seo关键词挖掘
  • 舟山网站制作/推广普通话手抄报简单
  • 网站建设 业务走下坡/百度视频推广怎么收费
  • 做百科需要用什么网站做参考/佛山网站建设正规公司
  • 北京哪里可以做网站/企业培训心得
  • 诸暨哪些公司可以制作网站/优化设计