热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android仿微信列表滑动删除之可滑动控件(一)

这篇文章主要为大家详细介绍了Android仿微信列表滑动删除之可滑动控件,具有一定的实用性和参考价值,感兴趣的小伙伴们可以参考一下

这次是列表滑动删除的第三波,仿微信的列表滑动删除。先上个效果图: 

 前面的文章里面说过开源框架SwipeListView的实现原理是每个列表item中包含上下两层view,普通状态下上层的view覆盖着下层的view,当用户滑开上层的view,下层的view就显示出来了。但是仔细观察微信列表的item,很明显并非这个实现方案,微信的item应该一个单层view,只不过这个item超出了所在的ListView的宽度,在用户滑动item的时候,item超出屏幕的view则会显示在屏幕之上,这种滑动实现也很不错。

既然推测出微信的实现原理,现在就要寻找具体的实现方案了,我最开始想的比较简单,以为写一个横向线性布局LinearLayout,让其包含两个子布局,左边的子布局宽度设置为填充父布局的宽度,以为另一个子布局就自然而然的会超出父布局的显示范围,但是具体测试的时候,发现就算左边的子布局设置为填充父布局的宽度,但实际显示的时候还是两个子布局被包含在父布局的显示范围内,右边的子布局无法做到超出父布局的显示范围。

纠结了一段时间,偶然情况下想起了ScrollView还有一种类型是HorizontalScrollView,,这个android提供的HorizontalScrollView可以做到其子view超出它的显示范围,那我可以直接使用HorizontalScrollView来实现这个item,我决定自定义一个控件,继承自HorizontalScrollView,在代码里面直接添加两个子布局,并让左边的布局宽度填充这个控件的自身宽度,以便让右边的布局超出控件的显示范围,不过很不辛,HorizontalScrollView比较傲娇,很难驾驭,奇葩bug层出不穷,譬如左边的子布局渲染出来了,但是右边的子布局愣是没初始化,更别说滑动了,弄的焦头烂额,最后实在没办法,只得暂时放下。

最后则是想到了使用一个自定义的ViewGroup来实现这个item。现在很多app在第一次启动的时候,会出现一个介绍性的导航界面,用户一页一页的滑动,看完导航的介绍之后再正式进入app,现在这种导航介绍应该大多数是用ViewPager实现的,ViewPager可以做到两个滑动的子页同时显示在屏幕范围内,具有很好的体验效果。但是ViewPager是在之后的support.v4里面引入的,最初并没有,那一开始这种导航介绍使用什么方案实现的呢?当然就是自定义的ViewGroup了,其实supprot.v4.ViewPager本身就是一个自定义的ViewGroup。关于使用自定义的ViewGroup实现导航介绍,csdn上有个大牛有专门写过一个文章介绍了,这里就不详细说了。

本篇的要讲的是如何使用自定义的ViewGroup实现item的子view可以超出父布局的显示范围这样的效果。

写一个SwipeItemView,继承自ViewGroup,构造方法里面,传入左边布局的引用id和右边布局的引用id,初始化左子布局和右子布局,并将它们添加到SwipeItemView中作为子view,代码如下:

private void init(Context context, AttributeSet attrs) {
    mScroller = new Scroller(context);

    ......

    if(mPrimaryViewID == -1)
      throw new RuntimeException(
          "Illegal attribute 'primaryView', make sure you have set it");

    mPrimaryView = LayoutInflater.from(getContext()).inflate(
        mPrimaryViewID, null);
    mPrimaryView.setClickable(false);
    addView(mPrimaryView, 0);
    if(mSlidingViewID != -1) {
      mSlidingView = LayoutInflater.from(getContext()).inflate(
          mSlidingViewID, null);
      mSlidingView.setClickable(false);
      addView(mSlidingView, 1);
    }
  }  

接下来需要重写VIewGroup的onMeasure()方法,用来测量这个SwipeItemView及其子view的宽高,其中先获取转入的heightMeasureSpec中包含的heightSize,当heightSize的值和heightMeasureSpec相同的时候,是测量整个SwipeItemView的宽高,这是我们不做额外的处理,当heightSize不等于的传入的heightMeasureSpec的时候,是用于测量SwipeItemView它包含的子view的宽高,这里我们做一下额外的处理,主要是针对超出SwipeItemView显示范围的右边的mSlidingView,我想要它的宽只是包裹其内容就行,不想它的宽和屏幕范围等宽,所以构造一个这样的参数MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),具体代码如下:

@Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);

    if(heightSize != heightMeasureSpec) {
      mPrimaryView.measure(MeasureSpec.makeMeasureSpec(widthSize, widthMode),
          MeasureSpec.makeMeasureSpec(heightSize, heightMode));

      if(mSlidingView != null) {
        mSlidingView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
            MeasureSpec.makeMeasureSpec(heightSize, heightMode));
      }

    } else {
      mPrimaryView.measure(widthMeasureSpec, heightMeasureSpec);
      if(mSlidingView != null)
        mSlidingView.measure(widthMeasureSpec, heightMeasureSpec);
    }
  } 
     

然后是重写ViewGroup的onLayout()方法,用来放置子view在SwipeItemView中的具体位置,主要就是让右边的mSlidingView排列在左边的mPrimaryView的右边,具体代码如下:

@Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    mPrimaryView.layout(l, t, r, b);
    if(mSlidingView != null)
      mSlidingView.layout(r, t, r + mSlidingView.getMeasuredWidth(), b);
  }  

 好了,具体的SwipeItemView的初始化完成了,接下来需要做什么工作呢,看一下上面的构造方法,有没有看到mScroller = new Scroller(context)这行代码,我们需要使用这个mScroller来做这个SwipeItemView滑动的动画效果。我们知道ViewGroup中提供了scrollBy()和scrollTo()两个方法用来移动这个ViewGroup视图内容的位置,其中scrollBy()移动参数指定的距离,scrollTo()方法移动到参数指定的位置。但是scrollBy()还好,如果每次都是移动一小段距离的话,给用户的感觉就是一段连续的动画效果了,但是scrollTo()则是瞬间移动,中间没有任何动画效果,会让人感觉到非常突兀,这样我们就需要用到mScroller这个对象了。

Scroller是android提供的用来实现我们需要的移动动画效果的。Scroller本身并非去执行移动的动画的,感觉上Scroller更多是作为一个指挥者,当我们调用它的Scroller.startScroll()方法的时候,这个方法我们需要传入移动初始的位置、移动的距离以及花费的时间,简单理解的话,我们假设指定的时间是10s,那第3s的时候,ViewGroup视图内容应该移动到什么位置,第7s,应该移动到哪个位置,而且这个时刻Scroller计算出来的位置可以通过Scroller.getCurrX()和Scroller.getCurrY()获取到,这个过程,Scroller会回调ViewGroup中的computeScroll()方法,在这个回调方法中,我们调用scrollTo()执行具体的移动操作。而我们实现的这个scrollToWithAnimation()方法就是提供给后面的SwipeListView用来移动某个item并且使其移动过程带有动画效果的方法。代码如下:

 @Override
  public void computeScroll() {
    if(mScroller.computeScrollOffset()) {
      scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
    }
  }

  /**
   * just like scrollTo(), but with animation :D
   * */
  public void scrollToWithAnimation(int scrollX, int scrollY) {
    mScroller.abortAnimation();
    mScroller.startScroll(getScrollX(), getScrollY(),
        scrollX - getScrollX(), getScrollY() - scrollY, 300);
  }

 接下来需要实现一个自定义的ListView,暂且命名为SwipeListView。下一篇继续。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 国内BI工具迎战国际巨头Tableau,稳步崛起
    尽管商业智能(BI)工具在中国的普及程度尚不及国际市场,但近年来,随着本土企业的持续创新和市场推广,国内主流BI工具正逐渐崭露头角。面对国际品牌如Tableau的强大竞争,国内BI工具通过不断优化产品和技术,赢得了越来越多用户的认可。 ... [详细]
  • 深入理解OAuth认证机制
    本文介绍了OAuth认证协议的核心概念及其工作原理。OAuth是一种开放标准,旨在为第三方应用提供安全的用户资源访问授权,同时确保用户的账户信息(如用户名和密码)不会暴露给第三方。 ... [详细]
  • Android 九宫格布局详解及实现:人人网应用示例
    本文深入探讨了人人网Android应用中独特的九宫格布局设计,解析其背后的GridView实现原理,并提供详细的代码示例。这种布局方式不仅美观大方,而且在现代Android应用中较为少见,值得开发者借鉴。 ... [详细]
  • 深入解析Android自定义View面试题
    本文探讨了Android Launcher开发中自定义View的重要性,并通过一道经典的面试题,帮助开发者更好地理解自定义View的实现细节。文章不仅涵盖了基础知识,还提供了实际操作建议。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文详细介绍如何使用arm-eabi-gdb调试Android平台上的C/C++程序。通过具体步骤和实用技巧,帮助开发者更高效地进行调试工作。 ... [详细]
  • 深入理解 Oracle 存储函数:计算员工年收入
    本文介绍如何使用 Oracle 存储函数查询特定员工的年收入。我们将详细解释存储函数的创建过程,并提供完整的代码示例。 ... [详细]
  • 本文总结了2018年的关键成就,包括职业变动、购车、考取驾照等重要事件,并分享了读书、工作、家庭和朋友方面的感悟。同时,展望2019年,制定了健康、软实力提升和技术学习的具体目标。 ... [详细]
  • 在计算机技术的学习道路上,51CTO学院以其专业性和专注度给我留下了深刻印象。从2012年接触计算机到2014年开始系统学习网络技术和安全领域,51CTO学院始终是我信赖的学习平台。 ... [详细]
  • CSS 布局:液态三栏混合宽度布局
    本文介绍了如何使用 CSS 实现液态的三栏布局,其中各栏具有不同的宽度设置。通过调整容器和内容区域的属性,可以实现灵活且响应式的网页设计。 ... [详细]
  • Linux 系统启动故障排除指南:MBR 和 GRUB 问题
    本文详细介绍了 Linux 系统启动过程中常见的 MBR 扇区和 GRUB 引导程序故障及其解决方案,涵盖从备份、模拟故障到恢复的具体步骤。 ... [详细]
  • 本文介绍了如何使用jQuery根据元素的类型(如复选框)和标签名(如段落)来获取DOM对象。这有助于更高效地操作网页中的特定元素。 ... [详细]
  • 本文将详细介绍如何使用剪映应用中的镜像功能,帮助用户轻松实现视频的镜像效果。通过简单的步骤,您可以快速掌握这一实用技巧。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文介绍如何在 Xcode 中使用快捷键和菜单命令对多行代码进行缩进,包括右缩进和左缩进的具体操作方法。 ... [详细]
author-avatar
乖乖紫仪面_614
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有