Android仿淘宝、京东Banner滑动查看图文详情

文章目录

      • 写在前面
      • 效果图
      • 原理分析
      • 核心代码
      • 源码地址

写在前面

本文基于 ViewPager2 实现的 Banner 效果,进而实现了仿淘宝、京东Banner滑动至最后一页时继续滑动来查看图文详情的效果。关于 ViewPager2 的原理及其封装,可以参见之前的两篇文章:
1、Android 深入理解ViewPager2原理及其实践(上篇)
2、Android 深入理解ViewPager2原理及其实践(下篇)

效果图

Banner滑动查看图文详情

原理分析

滑动查看更多

  • Banner与右侧的查看更多View都是子View,被父View包裹,默认Banner的宽度是match_parent,而查看更多则是在屏幕的右侧,处于不可见状态;
  • Banner进行左右滑动时,当前的滑动事件是在Banner中消费的,即父View不会进行拦截。
  • Banner滑动到最右侧且要继续滑动时,此时父View会进行事件的拦截,从而事件由父View接管,并在父ViewonTouchEvent()中消费事件,此时就可以滑动父View中的内容了。怎么滑动呢?在MOVE事件时通过scrollTo()/scrollBy()滑动,而在UP/CANCEL事件时,需要通过ScrollerstartScroll()自动滑动到查看更多子View的左侧或右侧,从而完成一次事件的消费;
  • UP/CANCEL事件触发时,查看更多子View滑动的距离超过一半,认为需要触发查看更多操作了,当然这里的值都可以自行设置。

核心代码

  • TJBannerFragment.kt
/*** 仿淘宝京东宝贝详情Fragment*/
class TJBannerFragment : BaseFragment() {private val mModels: MutableList<Any> = mutableListOf()private val mContainer: VpLoadMoreView by id(R.id.vp2_load_more)override fun getLayoutId(): Int {return R.layout.fragment_tx_news_n}override fun onViewCreated(view: View, savedInstanceState: Bundle?) {initVerticalTxScroll()}private fun initVerticalTxScroll() {mModels.add(TxNewsModel(MConstant.IMG_4, "美轮美奂节目", "奥运五环缓缓升起"))mModels.add(TxNewsModel(MConstant.IMG_1, "精美商品", "9块9包邮"))mContainer.setData(mModels) {showToast("打开更多页面")}}
}
  • VpLoadMoreView.kt(父View)
class VpLoadMoreView @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyle: Int = 0,
) : LinearLayout(context, attrs, defStyle) {private val mMVPager2: MVPager2 by id(R.id.mvp_pager2)private var mNeedIntercept: Boolean = false //是否需要拦截VP2事件private val mLoadMoreContainer: LinearLayout by id(R.id.load_more_container)private val mIvArrow: ImageView by id(R.id.iv_pull)private val mTvTips: TextView by id(R.id.tv_tips)private var mCurPos: Int = 0 //Banner当前滑动的位置private var mLastX = 0fprivate var mLastDownX = 0f //用于判断滑动方向private var mMenuWidth = 0 //加载更多View的宽度private var mShowMoreMenuWidth = 0 //加载更多发生变化时的宽度private var mLastStatus = false // 默认箭头样式private var mAction: (() -> Unit)? = nullprivate var mScroller: OverScrollerprivate var isTouchLeft = false //是否是向左滑动private var animRightStart = RotateAnimation(0f, -180f,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f).apply {duration = 300fillAfter = true}private var animRightEnd = RotateAnimation(-180f, 0f,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f).apply {duration = 300fillAfter = true}init {orientation = HORIZONTALView.inflate(context, R.layout.fragment_tx_news, this)mScroller = OverScroller(context)}/*** @param mModels 要加载的数据* @param action 回调Action*/fun setData(mModels: MutableList<Any>, action: () -> Unit) {this.mAction = actionmMVPager2.setModels(mModels).setLoop(false) //非循环模式.setIndicatorShow(false).setLoader(TxNewsLoader(mModels)).setPageTransformer(CompositePageTransformer().apply {addTransformer(MarginPageTransformer(15))}).setOrientation(ViewPager2.ORIENTATION_HORIZONTAL).setAutoPlay(false).setOnBannerClickListener(object : OnBannerClickListener {override fun onItemClick(position: Int) {showToast(mModels[position].toString())}}).registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {override fun onPageScrollStateChanged(state: Int) {if (mCurPos == mModels.lastIndex && isTouchLeft && state == ViewPager2.SCROLL_STATE_DRAGGING) {//Banner在最后一页 & 手势往左滑动 & 当前是滑动状态mNeedIntercept = true //父View可以拦截mMVPager2.setUserInputEnabled(false) //VP2设置为不可滑动}}override fun onPageSelected(position: Int) {mCurPos = position}}).start()}override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {mMenuWidth = mLoadMoreContainer.measuredWidthmShowMoreMenuWidth = mMenuWidth / 3 * 2super.onLayout(changed, l, t, r, b)}override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {when (ev?.action) {MotionEvent.ACTION_DOWN -> {mLastX = ev.xmLastDownX = ev.x}MotionEvent.ACTION_MOVE -> {isTouchLeft = mLastDownX - ev.x > 0 //判断滑动方向}}return super.dispatchTouchEvent(ev)}override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {var isIntercept = falsewhen (ev?.action) {MotionEvent.ACTION_MOVE -> isIntercept = mNeedIntercept //是否拦截Move事件}//log("ev?.action: ${ev?.action},isIntercept: $isIntercept")return isIntercept}@SuppressLint("ClickableViewAccessibility")override fun onTouchEvent(ev: MotionEvent?): Boolean {when (ev?.action) {MotionEvent.ACTION_MOVE -> {val mDeltaX = mLastX - ev.xif (mDeltaX > 0) {//向左滑动if (mDeltaX >= mMenuWidth || scrollX + mDeltaX >= mMenuWidth) {//右边缘检测scrollTo(mMenuWidth, 0)return super.onTouchEvent(ev)}} else if (mDeltaX < 0) {//向右滑动if (scrollX + mDeltaX <= 0) {//左边缘检测scrollTo(0, 0)return super.onTouchEvent(ev)}}showLoadMoreAnim(scrollX + mDeltaX)scrollBy(mDeltaX.toInt(), 0)mLastX = ev.x}MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {smoothCloseMenu()mNeedIntercept = falsemMVPager2.setUserInputEnabled(true)//执行回调val mDeltaX = mLastX - ev.xif (scrollX + mDeltaX >= mShowMoreMenuWidth) {mAction?.invoke()}}}return super.onTouchEvent(ev)}private fun smoothCloseMenu() {mScroller.forceFinished(true)/*** 左上为正,右下为负* startX:X轴开始位置* startY: Y轴结束位置* dx:X轴滑动距离* dy:Y轴滑动距离* duration:滑动时间*/mScroller.startScroll(scrollX, 0, -scrollX, 0, 300)invalidate()}override fun computeScroll() {if (mScroller.computeScrollOffset()) {showLoadMoreAnim(0f) //动画还原scrollTo(mScroller.currX, mScroller.currY)invalidate()}}private fun showLoadMoreAnim(dx: Float) {val showLoadMore = dx >= mShowMoreMenuWidthif (mLastStatus == showLoadMore) returnif (showLoadMore) {mIvArrow.startAnimation(animRightStart)mTvTips.text = "释放查看图文详情"mLastStatus = true} else {mIvArrow.startAnimation(animRightEnd)mTvTips.text = "滑动查看图文详情"mLastStatus = false}}
}

父View的注释很清晰,不用过多解释了,这里需要注意一点,已知在Banner的最后一页滑动时需要判断滑动方向:继续向左滑动,需要父View拦截滑动事件并自己进行消费;向右滑动时,父View不需要处理滑动事件,仍由Banner进行事件消费。

滑动方向需要起始位置(DOWN事件)的X坐标 - 滑动时的X坐标(MOVE事件) 的差值进行判断,那问题在哪里取起始位置的X坐标呢?在父ViewonInterceptTouchEvent()->DOWN事件里吗?这里是不行的,因为滑动方向是在MOVE事件里判断的,在父ViewonInterceptTouchEvent()->DOWN事件里拦截的话,后续事件不会往Banner里传递了。这里可以选择在父ViewdispatchTouchEvent()->DOWN事件里即可解决。

VpLoadMoreView对应的XML布局

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/white"android:orientation="horizontal"tools:parentTag="android.widget.LinearLayout"><!--ViewPager2--><org.ninetripods.lib_viewpager2.MVPager2android:id="@+id/mvp_pager2"android:layout_width="match_parent"android:layout_height="200dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /><!--加载更多View--><LinearLayoutandroid:id="@+id/load_more_container"android:layout_width="100dp"android:layout_height="200dp"android:gravity="center_vertical"android:orientation="horizontal"><ImageViewandroid:id="@+id/iv_pull"android:layout_width="18dp"android:layout_height="18dp"android:layout_gravity="center_vertical"android:layout_marginStart="10dp"android:src="@drawable/icon_arrow_pull" /><TextViewandroid:id="@+id/tv_tips"android:layout_width="16dp"android:layout_height="match_parent"android:layout_marginStart="10dp"android:gravity="center_vertical"android:text="滑动查看图文详情"android:textColor="#333333"android:textSize="14sp"android:textStyle="bold" /></LinearLayout>
</merge>

这里的父View(VpLoadMoreView)LinearLayout,且必须是横向布局,XML的顶层布局使用的merge标签,这样既可以优化一层布局,又可以在父View中直接操作加载图文详情的子View

源码地址

完整代码地址参见:Android仿淘宝、京东Banner滑动至最后查看图文详情

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/66911.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

新锐潮流男装品牌CHINISM成功上线巨益全渠道电商中台

CHINISM是杭州麟术服饰有限公司旗下的潮流快时尚男装品牌&#xff0c;2012年创建于浙江杭州&#xff0c;经营产品包含服装、服饰、鞋帽、箱包等多项品类。致力于为青年一代提供全场景穿搭方案&#xff0c;通过简约舒适的服饰单品&#xff0c;构建“高级感衣橱”&#xff0c;将造…

跨境电商如何打造爆款主图

某种程度上来讲&#xff0c;一张好的主图决定了80%以上的点击率&#xff0c;如何设计出一张高点击率的主图是所有卖家最关心的问题。虽然你之前靠listing的优化让你进到了首页。然而你以为流量就会乖乖的来了吗&#xff1f;NO&#xff01;你只是带球到进区了&#xff0c;还差最…

电商女装评论数据集分析

探索女性服装电子商务数据集 背景描述 这是一个女性服装电子商务数据集&#xff0c;围绕客户的评论撰写。数据具有9个特征&#xff0c;可以从多个维度解析文本。 由于是真实的商业数据&#xff0c;所以做了匿名处理&#xff0c;评论文本和正文中对该公司的引用被替换为“零售商…

零门槛一键生成PPT,利用人工智能快速提高办公效率(无需第三方插件)

人工智能技术的发展正以惊人的速度改变着我们的世界&#xff0c;今天给大家介绍下利用ChatGPT快速生成PPT的方法&#xff0c;它能够帮助你一键生成PPT内容和漂亮的PPT文档&#xff0c;无需繁琐的设计和排版&#xff0c;只需要与ChatGPT交流&#xff0c;你就能轻松拥有一份令人赞…

面向开发者的 LLM 入门课程

文章目录 面向开发者的 LLM 入门课程项目简介项目意义项目受众项目亮点内容大纲一、面向开发者的 Prompt Engineering二、搭建基于 ChatGPT 的问答系统三、使用 LangChain 开发应用程序四、Prompt 高级技巧(暂未完成)配套视频致谢LICENSE面向开发者的 LLM 入门课程 项目简介…

安卓软件自动跳转和按钮跳转

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、创建项目与三个页面二、实现自动跳转三、实现点击跳转和传递内容与内容显示 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、创…

Android 从网页中跳转到APP

最近&#xff0c;在使用QQ和微信等SDK来实现分享网页的时候&#xff0c;发现&#xff0c;SDK已经为页面跳转回应用提供了基本的数据支持。我们只需在应用里和被分享的网页进行简单的设置&#xff0c;即可实现此功能。 那么我们先来看下网页跳转回应用的实现原理。 就Android平…

Android利用Intent实现Activity页面跳转闪退解决方案

当我们利用Intent跳转&#xff1a; btnLogin.setOnClickListener(new View.OnClickListener() {Overridepublic void onClick(View view) {//当点击了登入按钮&#xff0c;跳转到相应activity页面Intent intent new Intent(MainActivity.this, LoginActivity.class);startActi…

phonegap跳转Activity android插件调用原生

Phonegap的插件 调用本地的Activity 查看我上一篇插件开发的文章: http://blog.csdn.net/aaawqqq/article/details/20401111 本节主要记录调用Activity的方式; 并提供 插件Demo下载 插件开发4个步骤: 1 在assents 目录下的 cordova-plugins.js文件添加配置 2 在asse…

关于安卓手机的微信内置浏览器中页面跳转失效的问题

Write By Monkeyfly 以下内容均为原创&#xff0c;如需转载请注明出处。 前提 今天下午在JS前端群里有人问了一个问题&#xff1a; 在微信浏览器中&#xff0c;window.location.href这个跳转方法不能用吗&#xff1f;在安卓手机试过了不行&#xff0c;但是ios可以&#xff…

安卓页面的跳转(按钮点击跳转、自动跳转)

一.创建新页面 在eclipse中建立安卓工程后&#xff0c;默认会有一个页面MainActivity&#xff0c;要实现多个页面的跳转&#xff0c;首先需要建立新的页面 在eclipse建立新的页面过程如下&#xff1a; 要添加的新页面的工程下src 右键---->New—>Other—>选择Android…

安卓跳转按钮的闪退问题

不知道为什么 跳转按钮有时可以跳转有时会闪退 程序并没有报错&#xff0c;求大神指导&#xff01; 闪退的次数特别多&#xff01;

android app跳转到微信

今天写这片文章主要是记录下 app跳转到微信的实现方法&#xff0c;我的项目需求是跳转到微信公众号&#xff0c;由于微信官方关闭了这个直接可以跳到公众号的接口&#xff0c;只能 从app打开微信&#xff0c;让用户自己去搜索。 我的项目需求&#xff1a; 点击跳转微信的时候&…

Android页面跳转(Intent)

Android 意图的使用&#xff08;Intent&#xff09; 显式四种跳转方式一二三四布局代码效果 隐式意图和隐式意图的跳转Intent概述Action属性Data属性Category属性按home键时启动自己做的应用 Component属性Extra属性&#xff08;重点&#xff09;Bundle传递序列化对象 Type属性…

安卓——Intent(实现页面跳转的两种方法)

下图中两个不同的方法就是两种页面之间跳转的情况 1>跳转不返回数据 2>跳转返回数据 实例&#xff1a; 第一种启动方式&#xff08;跳转不返回数据&#xff09; 第二种启动方式&#xff08;跳转返回数据&#xff09; 先看第一种&#xff1a; 点击第一种启动方式按钮…

【安卓开发】安卓页面跳转

目录 一、如何添加一个页面&#xff08;activity&#xff09;二、如何实现页面的跳转二、页面跳转传参1. 方式一2. 方式二安卓线程实现页面跳转安卓创建一个线程 三、Activity&#xff08;页面)的生命周期 一、如何添加一个页面&#xff08;activity&#xff09; 1. 右键 src …

安卓app之页面跳转

手动跳转 1.打开eclipse&#xff0c;New一个安卓工程 后面一直next&#xff0c;知道出现以下界面说明项目创建成功 2.添加第二个页面 一直next&#xff0c;直到出现以下界面 修改 Activity Name &#xff0c;点击Finish&#xff0c;页面添加成功 点击 AndroidManifest.xml 可…

大咖说·对话开源|与Tapdata论道数据技术开放生态

一个良好的开源生态具有哪些特征&#xff1f;开源与商业一定是对立的吗&#xff1f;开源产品应该如何做好商业化&#xff1f; 一个良好的开源生态具有哪些特征&#xff1f;开源与商业一定是对立的吗&#xff1f;开源产品应该如何做好商业化&#xff1f;本期大咖说&#xff0c;阿…

中奖名单公布

阅读本文大概需要 1.8 分钟。 头条今天的推送是恰饭文章&#xff0c;但确实觉得很不错&#xff0c;觉得靠谱才会推荐&#xff0c;感兴趣的可以参与下&#xff0c;反正免费的&#xff0c;不感兴趣忽略就好。 那个&#xff0c;先说声不好意思&#xff0c;五月份的收租抽奖我一直以…

纪录篇 之 我收集整理的一些网址(不断更新 ing)

LZ-Says&#xff1a;路漫漫其修远兮&#xff0c;吾将上下而求索。 前言 起初&#xff0c;不知道写博文是为了什么。 现在&#xff0c;点滴记录&#xff0c;为了更好的明天。 未来&#xff0c;只是为了证明这个世界我来过。 写这篇文章的前提&#xff0c;或者说诱发因素如下…