0

0

Android中自定义view实现侧滑效果

高洛峰

高洛峰

发布时间:2017-01-13 10:34:55

|

1308人浏览过

|

来源于php中文网

原创

效果图:

Android中自定义view实现侧滑效果

看网上的都是两个view拼接,默认右侧的不显示,水平移动的时候把右侧的view显示出来。但是看最新版QQ上的效果不是这样的,但给人的感觉却很好,所以献丑来一发比较高仿的。

知识点:

1、ViewDragHelper 的用法; 
2、滑动冲突的解决; 
3、自定义viewgroup。

ViewDragHelper 出来已经比较久了 相信大家都比较熟悉,不熟悉的话google一大把这里主要简单用一下它的几个方法 

1、tryCaptureView(View child, int pointerId) :确定那个子view可以滑动

2、 getViewHorizontalDragRange(View child):用我蹩脚的英语翻译一下是‘返回的是子view在水平方向上可移动的大小,以像素为单位,返回0的时候表示水平方向上不能拖动'

3、clampViewPositionHorizontal(View child, int left, int dx):在这里可以对边界进行检查,left和dx分别代表即将移动到的位置

4、onViewPositionChanged(View changedView, int left, int top, 
int dx, int dy):当要捕获view,由于拖曳或者设定而发生位置变更时回调

它的基本用法是:

public SwipeLayout(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init();
  }
  private void init() {
    viewDragHelper = ViewDragHelper.create(this, callback);
  }
   public boolean onInterceptTouchEvent(MotionEvent ev) {
    boolean result = viewDragHelper.shouldInterceptTouchEvent(ev);
   }
   public boolean onTouchEvent(MotionEvent event) {
    viewDragHelper.processTouchEvent(event);
    return true;
  }

1)、在构造方法中创建

2)、在onInterceptTouchEvent 中判断是否拦截

3 )、 在 onTouchEvent出来事件

好了 最不好理解的已经搞定了。接下来看看具体实现:

首先看布局:

文希AI写作
文希AI写作

AI论文写作平台

下载

  
    
    
    
    
  

这个没什么好说的,一个自定义viewgroup包含两个子控件。

接着看看SwipeLayout是怎么实现的:

@Override
  protected void onFinishInflate() {
    super.onFinishInflate();
    deleteView = getChildAt(0);
    contentView = getChildAt(1);
  }
  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    deleteHeight = deleteView.getMeasuredHeight();
    deleteWidth = deleteView.getMeasuredWidth();
    contentWidth = contentView.getMeasuredWidth();
    screenWidth = getWidth();
  }
  @Override
  protected void onLayout(boolean changed, int left, int top, int right,
              int bottom) {
    // super.onLayout(changed, left, top, right, bottom);
    deleteView.layout(screenWidth - deleteWidth, 0, (screenWidth - deleteWidth)
        + deleteWidth, deleteHeight);
    contentView.layout(0, 0, contentWidth, deleteHeight);
  }

上面代码进行了一些初始化的操作,重点看看onlayout里面的,我们继承的是framelayout 这里先画出来 deleteView并让他在右边,然后在上面改了一层contentView,这样显示的时候只会显示contentView。

接下来看ontouch方法

public boolean onTouchEvent(MotionEvent event) {
    //如果当前有打开的,则下面的逻辑不能执行
    if(!SwipeLayoutManager.getInstance().isShouldSwipe(this)){
      requestDisallowInterceptTouchEvent(true);
      return true;
    }
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        downX = event.getX();
        downY = event.getY();
        break;
      case MotionEvent.ACTION_MOVE:
        //1.获取x和y方向移动的距离
        float moveX = event.getX();
        float moveY = event.getY();
        float delatX = moveX - downX;//x方向移动的距离
        float delatY = moveY - downY;//y方向移动的距离
        if(Math.abs(delatX)>Math.abs(delatY)){
          //表示移动是偏向于水平方向,那么应该SwipeLayout应该处理,请求父view不要拦截
          requestDisallowInterceptTouchEvent(true);
        }
        //更新downX,downY
        downX = moveX;
        downY = moveY;
        break;
      case MotionEvent.ACTION_UP:
        break;
    }
    viewDragHelper.processTouchEvent(event);
    return true;
  }

上面主要就是对事件冲突的处理,当是水平移动的时候就请求父视图不要拦截。

接下来来重点就来了

private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
    @Override
    public boolean tryCaptureView(View child, int pointerId) {
      return child==contentView;
    }
    @Override
    public int getViewHorizontalDragRange(View child) {
      return deleteWidth;
    }
    @Override
    public int clampViewPositionHorizontal(View child, int left, int dx) {
      if(child==contentView){
        if(left>0)left = 0;
        if(left<-deleteWidth)left = -deleteWidth;
      }
      return left;
    }
    @Override
    public void onViewPositionChanged(View changedView, int left, int top,
                     int dx, int dy) {
      super.onViewPositionChanged(changedView, left, top, dx, dy);
      //判断开和关闭的逻辑
      if(contentView.getLeft()==0 && currentState!=SwipeState.Close){
        //说明应该将state更改为关闭
        currentState = SwipeState.Close;
        //回调接口关闭的方法
        if(listener!=null){
          listener.onClose(getTag());
        }
        //说明当前的SwipeLayout已经关闭,需要让Manager清空一下
        SwipeLayoutManager.getInstance().clearCurrentLayout();
      }else if (contentView.getLeft()==-deleteWidth && currentState!=SwipeState.Open) {
        //说明应该将state更改为开
        currentState = SwipeState.Open;
        //回调接口打开的方法
        if(listener!=null){
          listener.onOpen(getTag());
        }
        //当前的Swipelayout已经打开,需要让Manager记录一下下
        SwipeLayoutManager.getInstance().setSwipeLayout(SwipeLayout.this);
      }
    }
    @Override
    public void onViewReleased(View releasedChild, float xvel, float yvel) {
      super.onViewReleased(releasedChild, xvel, yvel);
      if(contentView.getLeft()<-deleteWidth/2){
        //应该打开
        open();
      }else {
        //应该关闭
        close();
      }
    }
  };

上面这段代码里面的方法一开始我们都说过了,在来看下在tryCaptureView中我们让 contentView可以滑动,在getViewHorizontalDragRange中却东滑动范围是deleteWidth,在clampViewPositionHorizontal中对边界进行了下限制,在onViewPositionChanged中进行状态的更新, 最后在手指抬起的时候让view自动回滚,

/**
  * 打开的方法
  */
 public void open() {
   viewDragHelper.smoothSlideViewTo(contentView,-deleteWidth,contentView.getTop());
   ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
 }
 /**
  * 关闭的方法
  */
 public void close() {
   viewDragHelper.smoothSlideViewTo(contentView,0,contentView.getTop());
   ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
 };
 public void computeScroll() {
   if(viewDragHelper.continueSettling(true)){
     ViewCompat.postInvalidateOnAnimation(this);
   }
 }

这里注意一定要重写computeScroll方法,不然滑动效果动一下就不动了。

至此这个自定义framelayout就完成了

但是发现一个问题,我们在已经滑动出来的view中上下滑动时,这个view的deleteView还是显示状态,所以还要在activity中处理一下:

recyView.setOnScrollListener(new RecyclerView.OnScrollListener() {
     @Override
     public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
       super.onScrollStateChanged(recyclerView, newState);
     }
     @Override
     public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
       super.onScrolled(recyclerView, dx, dy);
       if(dy>0 || dy<0){
         SwipeLayoutManager.getInstance().closeCurrentLayout();
       }
     }
   });

当这个RecyclerView是上下滑动时,让子view复位。 
收工。

ps:本来是在eclipse中listview中实现的,但是想想google都已经不支持eclipse了,而listview也快被RecyclerView代替了,所以最后还是切换到Android studio,用RecyclerView实现了一套。

以上所述是小编给大家介绍的Android中自定义view实现侧滑效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对PHP中文网的支持!

更多Android中自定义view实现侧滑效果相关文章请关注PHP中文网!

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Python 序列化
Python 序列化

本专题整合了python序列化、反序列化相关内容,阅读专题下面的文章了解更多详细内容。

0

2026.02.02

AO3官网入口与中文阅读设置 AO3网页版使用与访问
AO3官网入口与中文阅读设置 AO3网页版使用与访问

本专题围绕 Archive of Our Own(AO3)官网入口展开,系统整理 AO3 最新可用官网地址、网页版访问方式、正确打开链接的方法,并详细讲解 AO3 中文界面设置、阅读语言切换及基础使用流程,帮助用户稳定访问 AO3 官网,高效完成中文阅读与作品浏览。

91

2026.02.02

主流快递单号查询入口 实时物流进度一站式追踪专题
主流快递单号查询入口 实时物流进度一站式追踪专题

本专题聚合极兔快递、京东快递、中通快递、圆通快递、韵达快递等主流物流平台的单号查询与运单追踪内容,重点解决单号查询、手机号查物流、官网入口直达、包裹进度实时追踪等高频问题,帮助用户快速获取最新物流状态,提升查件效率与使用体验。

27

2026.02.02

Golang WebAssembly(WASM)开发入门
Golang WebAssembly(WASM)开发入门

本专题系统讲解 Golang 在 WebAssembly(WASM)开发中的实践方法,涵盖 WASM 基础原理、Go 编译到 WASM 的流程、与 JavaScript 的交互方式、性能与体积优化,以及典型应用场景(如前端计算、跨平台模块)。帮助开发者掌握 Go 在新一代 Web 技术栈中的应用能力。

11

2026.02.02

PHP Swoole 高性能服务开发
PHP Swoole 高性能服务开发

本专题聚焦 PHP Swoole 扩展在高性能服务端开发中的应用,系统讲解协程模型、异步IO、TCP/HTTP/WebSocket服务器、进程与任务管理、常驻内存架构设计。通过实战案例,帮助开发者掌握 使用 PHP 构建高并发、低延迟服务端应用的工程化能力。

5

2026.02.02

Java JNI 与本地代码交互实战
Java JNI 与本地代码交互实战

本专题系统讲解 Java 通过 JNI 调用 C/C++ 本地代码的核心机制,涵盖 JNI 基本原理、数据类型映射、内存管理、异常处理、性能优化策略以及典型应用场景(如高性能计算、底层库封装)。通过实战示例,帮助开发者掌握 Java 与本地代码混合开发的完整流程。

5

2026.02.02

go语言 注释编码
go语言 注释编码

本专题整合了go语言注释、注释规范等等内容,阅读专题下面的文章了解更多详细内容。

62

2026.01.31

go语言 math包
go语言 math包

本专题整合了go语言math包相关内容,阅读专题下面的文章了解更多详细内容。

55

2026.01.31

go语言输入函数
go语言输入函数

本专题整合了go语言输入相关教程内容,阅读专题下面的文章了解更多详细内容。

27

2026.01.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号