我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android 自定义ViewPager

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android 自定义ViewPager

项目地址

概述 处理滑动到左边界和右边界时,不允许滑动。 页面滑动一半回弹,滑动一半以上自动切换下一界面。 当页面内存在ScrollView这类子控件,事件要正常分发,不允许自定义ViewPager拦截事件。 回弹与切换动画处理。 源码分析 初始化
public ViewPagerY(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    mContext = context;
    // Scroller设置的是一个匀速插值器
    myScroll = new Scroller(context, new LinearInterpolator());
    // 初始化ImageLoader
    ImageLoader.getInstance().init(ImageLoaderConfiguration.createDefault(mContext));
}
设置资源,如图片id,布局,图片链接等

public void setRes(ArrayList res) {
    for (ResType mResType : res) {
        if (mResType.getmType() == ResType.Type.IAMG) {
            // 如果是资源图片id,创建ImageView对象
            ImageView imageView = new ImageView(mContext);
            imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
            imageView.setImageResource((Integer) mResType.getRes());
            // 将设置好的ImageView添加进ViewPagerY控件中
            this.addView(imageView);
        }
        if (mResType.getmType() == ResType.Type.URL) {
            // 如果资源是图片URL,创建ImageView对象.
            ImageView imageView = new ImageView(mContext);
            imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
            // 使用ImageLoader将图片从网络获取设置到ImageView中.
            ImageLoader.getInstance().displayImage((String) mResType.getRes(), imageView);
            // 将设置好的ImageView添加进ViewPagerY控件中
            this.addView(imageView);
        }
        if (mResType.getmType() == ResType.Type.LAYOUT) {
            // 如果资源是自己写的布局文件,就获取该布局文件对应的View
            View view = LayoutInflater.from(mContext).inflate((Integer) mResType.getRes(), null);
            // 将获取到的View添加进ViewPagerY控件中
            this.addView(view);
        }
    }
}
// 资源类型类
public class ResType {
    public enum Type {
        IAMG,
        LAYOUT,
        URL
    }
    private T mRes;
    private Type mType;
    public ResType(T res, Type mType) {
        this.mRes = res;
        this.mType = mType;
    }
    public T getRes() {
        return mRes;
    }
    public Type getmType() {
        return mType;
    }
}
ViewPagerY对子View的测量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // 设置ViewPagerY尺寸
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    // 获取ViewPagerY宽高
    height = getMeasuredHeight();
    widht = getMeasuredWidth();
    // 创建子View的MeasureSpec, 创建MeasureSpec是有规律的,可以看这个笔记: https://blog.csdn.net/MoLiao2046/article/details/105708819
    int wMeasureSpec = MeasureSpec.makeMeasureSpec(widht, MeasureSpec.EXACTLY);
    int hMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
    for (int i = 0; i < getChildCount(); i++) {
        // 遍历子View开始测量子View.
        getChildAt(i).measure(wMeasureSpec, hMeasureSpec);
    }
}
ViewPagerY对子View进行布局
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    for (int i = 0; i < getChildCount(); i++) {
        // 资源集合中每一个资源对应一个页面.
        // 分别设置这些View的左上角与右下角坐标.
        this.getChildAt(i).layout(i * widht, 0, i * widht + widht, height);
    }
}
判断手势,如果手势为水平滑动就拦截, 否则就正常将事件分发给子View处理.
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    // 默认交给ViewGroup拦截事件,ViewGroup一般是不会拦截事件.
    boolean interceptChildeEvent = super.onInterceptTouchEvent(event);
    switch (event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN:
            // mLastX与mDownX是手指按下时候的坐标,这两个值会在ViewPagerY中onTouchEvent中用到,处理页面滑动用的.
            // onTouchEvent()中也能获取ACTION_DOWN事件,但是在onTouchEvent中获取手指按下坐标再进行相应移动处理会出现页面跳动的问题.
            mLastX = mDownX = interceptLastX = event.getX();
            interceptLastY = event.getY();
            break;
        case MotionEvent.ACTION_MOVE:
            // 获取当前手指坐标
            float moveX = event.getX();
            float moveY = event.getY();
            // 移动后,计算出滑动后与上一个坐标点之间的距离.
            float slopX = moveX - interceptLastX;
            float slopY = moveY - interceptLastY;
            // 得到手指滑动距离绝对值
            float slopAbsX = Math.abs(slopX);
            float slopAbsY = Math.abs(slopY);
            if ((slopAbsX > 0 || slopAbsY > 0) && (slopAbsX - slopAbsY) >= 6) {
                // 如果手指移动距离大于0,且横向移动距离减去纵向移动距离大于6像素
                // 那么ViewPagerY就将该事件拦截, 不分发给它的子View使用,留给自己使用了.
                // 这样会导致mFirstTouchTarget=null,之后子View就再也接收不到事件组的其他事件了.
                interceptChildeEvent = true;
            }
            interceptLastX = moveX;
            interceptLastY = moveY;
            break;
    }
    return interceptChildeEvent;
}
处理页面滑动回弹的逻辑
@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getActionMasked()) {
        case MotionEvent.ACTION_MOVE:
            float mMoveX = event.getX();
            // mLastX:是在onInterceptTouchEvent()中得到的值
            float mDiffX = mMoveX - mLastX;
            mLastX = mMoveX;
            if (event.getPointerId(event.getActionIndex()) == 0 && event.getPointerCount() == 1) {// 这个条件可以控制只追踪屏幕中的一个手指的滑动.
                int scrollX = getScrollX();
                if (currentIndex == 0) {
                    // 第一页
                    if (mDiffX = 0) {
                            // 内容左边距离控件左边的距离减去向右滑动距离,如果大于0,说明内容左边距离控件左边还有间隔距离,滑动距离取手指移动距离.
                            ViewPagerY.this.scrollBy((int) -mDiffX, 0);
                        } else {
                            // 内容左边距离控件左边的距离减去向右滑动距离,如果小于0,说明内容左边与控件左边需要重合,滑动距离取getScrollX().
                            ViewPagerY.this.scrollBy(-scrollX, 0);
                        }
                    }
                }
                if (currentIndex == getChildCount() - 1) {
                    // 最后一页
                    if (mDiffX > 0) {
                        // 如果向右滑动
                        ViewPagerY.this.scrollBy((int) -mDiffX, 0);
                    } else {
                        // 处理先向右滑动,然后又向左滑动.
                        // (((getChildCount() - 1) * widht) - scrollX): 表示内容右边距离控件右边的距离
                        float mDiffMargin = (((getChildCount() - 1) * widht) - scrollX) + mDiffX;
                        if (mDiffMargin >= 0) {
                            // 说明内容右边与控件右边还有距离,滑动距离取手指移动距离.
                            ViewPagerY.this.scrollBy((int) -mDiffX, 0);
                        } else {
                            // 说明内容右边与控件右边需要重合,滑动距离取内容右边与控件右边的距离.
                            ViewPagerY.this.scrollBy(-(((getChildCount() - 1) * widht) - scrollX), 0);
                        }
                    }
                }
                if (currentIndex != 0 && currentIndex != getChildCount() - 1) {
                    // scrollBy总是和移动的相反
                    ViewPagerY.this.scrollBy((int) -mDiffX, 0);
                }
            }
            break;
        case MotionEvent.ACTION_UP:
            float mUpX = event.getX();
            // mDownX:是在onInterceptTouchEvent()中得到的值
            if (mUpX - mDownX > getWidth() / 2) {
                // 移动到上一个
                moveTo(currentIndex - 1);
            } else if (mUpX - mDownX < -getWidth() / 2) {
                // 移动到下一个
                moveTo(currentIndex + 1);
            } else {
                // 移动到当前页面
                moveTo(currentIndex);
            }
            break;
    }
    return true;
}
页面切换逻辑

public void moveTo(int index) {
    int duration = 0;
    if (index  getChildCount() - 1) {
        index = getChildCount() - 1;
    }
    // 中间经历几个界面, 每个页面切换时长是固定的.
    int count = currentIndex - index;
    if (count != 0) {
        duration = mDuration * count;
    } else {
        duration = mDuration;
    }
    currentIndex = index;
    if (onPageChangeListener != null) {
        // 页面切换的监听.
        onPageChangeListener.onPageSelect(currentIndex);
    }
    // getScrollX(): 内容左边与ViewPager控件左边距离
    // currentIndex * getWidth(): 切换到currentIndex界面时的getScrollX()值.
    // 得到需要移动的距离.
    int distanceX = currentIndex * getWidth() - getScrollX();
    //给MyScroll 计算的类赋初始值
    myScroll.startScroll(getScrollX(), 0, distanceX, 0, Math.abs(duration));
    invalidate();
}
// 想要缓慢滑动这个也很重要.
@Override
public void computeScroll() {
    //如果为true说明移动还没结束
    if (myScroll.computeScrollOffset()) {
        //得到计算的位置,然后移动
        float currX = myScroll.getCurrX();
        scrollTo((int) currX, 0);
        invalidate();
    }
}
效果图

效果图

碧云天丶 原创文章 65获赞 14访问量 3万+ 关注 私信 展开阅读全文
作者:碧云天丶


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android 自定义ViewPager

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android 自定义ViewPager

项目地址 概述 处理滑动到左边界和右边界时,不允许滑动。 页面滑动一半回弹,滑动一半以上自动切换下一界面。 当页面内存在ScrollView这类子控件,事件要正常分发,不允许自定义ViewPager拦截事件。 回弹与切换动画处理。 源码分析
2022-06-06

Android自定义ViewPager实例

本文实例讲述了Android自定义ViewPager的方法。分享给大家供大家参考,具体如下:package com.rong.activity; import android.content.Context; import android.
2022-06-06

Android自定义ViewPager指示器

本文实例为大家分享了Android ViewPager指示器的制作方法,供大家参考,具体内容如下 1.概述ViewPageIndicator这个开源框架大家都接触过,个人感觉还不错就是用起来比较麻烦,需要这里配置那里配置效果定制起来也不方便
2022-06-06

Android自定义View Flyme6的Viewpager指示器

最新更新的Flyme6整体效果不错,动画效果增加了很多了,看了看flyme6的Viewpager指示器,觉得有点意思,就模仿写了一下,整体效果如下: Gradle JitPack v1.0.2allprojects {repositorie
2022-06-06

Android 自定义布局竖向的ViewPager的实现

Android 自定义布局竖向的ViewPager的实现效果图:这个自定义控件涉及到的知识点:自定义ViewGroup中onMeasure和onLayout的写法 弹性滚动Scroller的用法 速度轨迹追踪器VelocityTracker
2023-05-31

Android自定义控件案例汇总1(菜单、popupwindow、viewpager)

自定义控件是根据自己的需要自己来编写控件。安卓自带的控件有时候无法满足你的需求,这种时候,我们只能去自己去实现适合项目的控件。同时,安卓也允许你去继承已经存在的控件或者实现你自己的控件以便优化界面和创造更加丰富的用户体验。在平常的项目中,我
2022-06-06

Android自定义ViewPager实现个性化的图片切换效果

第一次见到ViewPager这个控件,瞬间爱不释手,做东西的主界面通通ViewPager,以及图片切换也抛弃了ImageSwitch之类的,开始让ViewPager来做。时间长了,ViewPager的切换效果觉得枯燥,形成了审美疲劳~~我们
2022-06-06

Android自定义Toast

Java代码: public void showCustomToast(View v){ // Toast.makeText(this, "吐司", 0).show();Toast result = new Toast(this);Lay
2022-06-06

Android自定义ViewPagerIndicator实现炫酷导航栏指示器(ViewPager+Fragment)

ViewPagerIndicator导航栏指示器运行效果:实现这个效果,我是看了很多大神写的博客和视频后自己敲的,欢迎指正 github地址:https://github.com/dl10210950/TabViewPagerIndicat
2022-06-06

详解Android自定义View--自定义柱状图

绪论 转眼间,2016伴随着互联网寒冬和帝都的雾霾马上就过去了,不知道大家今年一整年过得怎么样?最近票圈被各个城市的雾霾刷屏,内心难免会动荡,庆幸自己早出来一年,也担忧着自己的未来的职业规划。无所谓了,既然选择了这个行业,我觉得大家就应该坚
2022-06-06

Android自定义控件之自定义属性(二)

前言: 上篇介绍了自定义控件的基本要求以及绘制的基本原理,本篇文章主要介绍如何给自定义控件自定义一些属性。本篇文章将继续以上篇文章自定义圆形百分比为例进行讲解。有关原理知识请参考Android自定义控件基本原理详解(一)这篇文章。 需求产生
2022-06-06

Android自定义组件:1、什么是自定义组件、自定义组件的方式、定义自定义属性

声明:本教程不收取任何费用,欢迎转载,尊重作者劳动成果,不得用于商业用途,侵权必究!!! 目录 一、前言 二、什么是自定义组件 三、自定义组件的方式 1、组合现有组件 2、在某一个组件上进行扩展 3、完全自定义组件 四、定义自定义属性 1、
2022-06-06

Android 自定义SurfaceView详解

本文简单讨论以后Android游戏引擎模板的架构问题。在Android游戏开发教程之二:View类与SurfaceView类中我们已经谈到,SurfaceView类是有很多优势的,所以在Android游戏开发中还是选择Surf
2022-06-06

Android 自定义Dialog 实例

开发中经常需要请求网络获取数据,我们在请求网络到得到数据时当中需要等待一些时间,为了增加用户体验,我们一般会用一个Dialog来提示用户我们在加载网络数据。 今天我们来实现如下效果的加载中Dialog。 从图中我们可以看到要这个Dialo
2022-06-06

Android自定义TabLayout效果

周末就要到了,今天项目中遇到这样一个Tab,选中tab的背景是个圆角矩形,方向指向器没有了,这样普通的TabLayout不能满足我的要求,可能会想到动态的去设置选中Tab的背景不就可以了,但是那样的话太生硬了,没有动画效果,其实想想也还比较
2022-06-06

android怎么自定义viewgroup

要自定义一个ViewGroup,你需要创建一个继承自ViewGroup的子类,并重写一些关键的方法来定义你的布局和子视图的排列方式。以下是一个简单的例子来帮助你开始自定义一个ViewGroup:1. 创建一个新的Java类并命名为Custo
2023-10-11

Android自定义圆角ImageView

废话不多说了,直接给大家贴代码了。 java类如下: import android.content.Context; import android.content.res.TypedArray; import android.graph
2022-06-06

Android怎么自定义View

这篇文章主要介绍“Android怎么自定义View”,在日常操作中,相信很多人在Android怎么自定义View问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Android怎么自定义View”的疑惑有所帮助!
2023-06-30

android 自定义控件 自定义属性详细介绍

自定义控件在android中无处不见,自定义控件给了我们很大的方便。比如说,一个视图为imageview ,imagebutton ,textview 等诸多控件的组合,用的地方有很多,我们不可能每次都来写3个的组合,既浪费时间,效率又低。
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录