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.CardViewPageandroid: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" /><LinearLayoutandroid: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);}@Overrideprotected 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 oneint 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 yetif (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 cardCardView currentCard = mAdapter.getCardViewAt(mViewPager.getCurrentItem());if (currentCard != null) {currentCard.animate().scaleY(1);currentCard.animate().scaleX(1);}}else if(!mScalingEnabled && enable){// grow main cardCardView currentCard = mAdapter.getCardViewAt(mViewPager.getCurrentItem());if (currentCard != null) {currentCard.animate().scaleY(1.1f);currentCard.animate().scaleX(1.1f);}}mScalingEnabled = enable;}@Overridepublic void transformPage(View page, float position) {}@Overridepublic 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 oneif (goingLeft) {realCurrentPosition = position + 1;nextPosition = position;realOffset = 1 - positionOffset;} else {nextPosition = position + 1;realCurrentPosition = position;realOffset = positionOffset;}// Avoid crash on overscrollif (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 yetif (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 yetif (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;}@Overridepublic void onPageSelected(int position) {}@Overridepublic 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;}@Overridepublic CardView getCardViewAt(int position) {return mViews.get(position);}@Overridepublic int getCount() {return mData.size();}@Overridepublic boolean isViewFromObject(View view, Object object) {return view == object;}@Overridepublic 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;}@Overridepublic 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() {@Overridepublic 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"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><RelativeLayoutandroid:layout_width="270dp"android:layout_height="498dp"android:layout_centerHorizontal="true"><ImageViewandroid:id="@+id/imag_back"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="fitXY"android:src="@mipmap/cardy" /><ImageViewandroid: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"/><TextViewandroid: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:ImageViewprivate lateinit var text_title:TextViewprivate lateinit var viewPager: CardViewPageprivate lateinit var mCardAdapter: CardPagerAdapterprivate lateinit var mCardShadowTransformer: ShadowTransformerprivate lateinit var linear: LinearLayoutprivate lateinit var list: MutableList<String>private lateinit var view: Viewprivate var mNum = 22.代碼部分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}//添加到LinearLayoutlinear.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>

總結

以上就是整個卡片滑動效果的實現步驟和代碼,是不是很簡單,歡迎各位點評指正。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/73589.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/73589.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/73589.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

(UI自動化測試web端)第二篇:元素定位的方法_css定位之css選擇器

看代碼里的【find_element_by_css_selector( )】( )里的表達式怎么寫&#xff1f; 文章介紹了第三種寫法css選擇器&#xff0c;你要根據網頁中的實際情況來判斷自己到底要用哪一種方法來進行元素定位。每種方法都要多練習&#xff0c;全都熟了之后你在工作當中使用起來元素定位…

使用vscode搭建pywebview集成vue項目示例

文章目錄 前言環境準備項目源碼下載一、項目說明1 目錄結構2 前端項目3 后端項目獲取python安裝包(選擇對應版本及系統) 三、調試與生成可執行文件1 本地調試2 打包應用 四、核心代碼說明1、package.json2、vite.config.ts設置3、main.py后端入口文件說明 參考文檔 前言 本節我…

C stm32f10x LED亮

#include<stm32f10x.h>int main(){#if 0 //APIOA 時鐘初始化unsigned int * p(unsigned int*)0x40021018;*p | 0x1<<2;//A0 推挽輸出p(unsigned int*)0x40010800;*p *p & ~0xf | 0x1;//A0低電平p(unsigned int*)0x4001080c;*p & ~0x1;#endifRCC->APB2E…

redux ,react-redux,redux-toolkit 簡單總結

Redux、React-Redux 和 Redux Toolkit 是協同工作的三個庫&#xff0c;各自承擔不同角色&#xff0c;相互協同。 Redux&#xff1a;基礎底座 底層狀態管理庫&#xff0c;負責狀態存儲、Action 派發和 Reducer 執行 ?React-Redux&#xff1a;連接 React 組件與 Redux Store 通…

智能制造:物聯網和自動化之間的關系

工業自動化 工業自動化是機器設備或生產過程在不需要人工直接干預的情況下按預期的目標實現測量、操縱等信息處理和過程控制的統稱。 在傳統的工業生產過程中&#xff0c;很多環節需要人工操作&#xff0c;比如設備調試、生產監控、質量檢測等。然而&#xff0c;隨著工業自動化…

“自動駕駛背后的數學” 專欄導讀

專欄鏈接&#xff1a; 自動駕駛背后的數學 專欄以“自動駕駛背后的數學”為主題&#xff0c;從基礎到深入&#xff0c;再到實際應用和未來展望&#xff0c;全面解析自動駕駛技術中的數學原理。開篇用基礎數學工具搭建自動駕駛的整體框架&#xff0c;吸引兒童培養興趣&#xff0…

集成學習(下):Stacking集成方法

一、Stacking的元學習革命 1.1 概念 Stacking&#xff08;堆疊法&#xff09; 是一種集成學習技術&#xff0c;通過組合多個基學習器&#xff08;base learner&#xff09;的預測結果&#xff0c;并利用一個元模型&#xff08;meta-model&#xff09;進行二次訓練&#xff0c…

Dubbo 全面解析:從 RPC 核心到服務治理實踐

一、分布式系統與 RPC 框架概述 在當今互聯網時代&#xff0c;隨著業務規模的不斷擴大&#xff0c;單體架構已經無法滿足高并發、高可用的需求&#xff0c;分布式系統架構成為主流選擇。而在分布式系統中&#xff0c;遠程服務調用&#xff08;Remote Procedure Call&#xff0…

vmware虛擬機突然連不上網

1.一般是自己的主機把服務給關掉了&#xff0c;右擊我的電腦&#xff0c;然后找到管理->服務&#xff0c;確保下面虛擬機的網絡服務是否打開 Vmware虛擬機突然連接不上網絡【方案集合】_vmware虛擬機連不上網-CSDN博客 2.識別到無效網絡 控制面板->網絡和共享中心&…

Selenium之簡介

Selenium簡介 首先&#xff0c;讓我們看看官網是怎么定義的 Selenium是一個支持web瀏覽器自動化的一系列工具和庫的綜合項目&#xff0c;提供了擴展來模擬用戶和瀏覽器的交互&#xff0c;用于擴展瀏覽器分配的分發服務器&#xff1b;用于W3C WebDriver規范的基礎架構 其實&a…

SpringBoot 開發入門—Springboot基礎框架匯總

一、環境準備 Java&#xff1a;Spring Boot 3.0.2 需要 Java 17&#xff0c;并且與 Java 19 兼容 Maven&#xff1a;Apache Maven 3.5 或更高版本兼容 二、啟動器 以下應用程序啟動器由 Spring Boot 在該組下提供&#xff1a;org.springframework.boot 表 1.Spring 引導應…

前端批量導入方式

webpack批量導入 webpack中使用 require.context 實現自動導入 const files require.context(./modules, false, /\.ts$/); const modules {}; files.keys().forEach((key) > {if (key ./index.ts) { return; }modules[key.replace(/(\.\/|\.ts)/g, )] files(key).def…

阿里巴巴1688類網站高保真原型設計

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>1688類B2B平臺原型</title><script src…

C++設計模式-裝飾模式:從基本介紹,內部原理、應用場景、使用方法,常見問題和解決方案進行深度解析

一、裝飾模式基本介紹 裝飾模式&#xff08;Decorator Pattern&#xff09;是一種結構型設計模式&#xff0c;允許你在不改變對象自身的基礎上&#xff0c;動態地給一個對象添加額外的職責。這種模式創建了一個裝飾類&#xff0c;用來包裝原有的類&#xff0c;并在保持類方法簽…

2、學習Docker前置操作

docker三件套&#xff1a;鏡像、容器、倉庫 Docker hubhub.docker.com ubuntu安裝【待更新】 CentOS安裝 CentOS 僅發行版本中的內核支持 Docker。Docker 運行在 CentOS 7 (64-bit)上&#xff0c;要求系統為 64 位、Linux 系統內核版本為 3.8 以上&#xff0c;這里選用 Cen…

70. Linux驅動開發與裸機開發區別,字符設備驅動開發

一、裸機驅動開發回顧 1、底層&#xff0c;跟寄存器打交道&#xff0c;有些MCU提供了庫。 二、Linux驅動開發思維 1、Linux下驅動開發直接操作寄存器不現實。 2、根據Linux下的各種驅動框架進行開發。一定要滿足框架&#xff0c;也就是Linux下各種驅動框架的掌握。 3、驅動最…

【JavaScript 簡明入門教程】為了Screeps服務的純JS入門教程

0 前言 0-1 Screeps: World 眾所不周知&#xff0c;?Screeps: World是一款面向編程愛好者的開源大型多人在線即時戰略&#xff08;MMORTS&#xff09;沙盒游戲&#xff0c;其核心機制是通過編寫JavaScript代碼來控制游戲中的單位&#xff08;稱為“Creep”&#xff09;&#…

第12章:優化并發_《C++性能優化指南》notes

優化并發 一、并發基礎與優化核心知識點二、關鍵代碼示例與測試三、關鍵優化策略總結四、性能測試方法論多選題設計題答案與詳解多選題答案&#xff1a; 設計題答案示例 一、并發基礎與優化核心知識點 線程 vs 異步任務 核心區別&#xff1a;std::thread直接管理線程&#xf…

[C++面試] RAII資源獲取即初始化(重點)

一、入門 1、什么是 RAII&#xff1f; RAII&#xff08;Resource Acquisition Is Initialization&#xff0c;資源獲取即初始化&#xff09;是 C 的核心編程范式&#xff0c;核心思想是 ?將資源的生命周期與對象的生命周期綁定&#xff1a; ?資源獲取&#xff1a;在對象構造…

Unity粒子系統

目錄 一、界面參數介紹1.主模塊2.Emission 模塊3.Shape 模塊4.Velocity over Lifetime 模塊5.Noise 模塊6.Limit Velocity Over Lifetime 模塊7.Inherit Velocity 模塊8.Force Over Lifetime 模塊9.Color Over Lifetime 模塊10.Color By Speed 模塊11.Size over Lifetime 模塊1…