在开发中,会发现很多列表希望条目能够侧滑,侧滑出来一两个按钮什么的,例如QQ就可以侧滑出删除按钮。这边文章就是教大家写一个可以侧滑的自定义控件。另外,本文的内容不是属于Android中比较高深的内容,高手可以略过。通过阅读本文,你可能学习到的知识有:

  1. 自定义侧滑控件的实现
  2. Android事件传递简要内容
  3. 属性动画ValueAnimator的使用

先来看一下要实现的侧滑是什么样的效果:


因为这个例子很简单所以代码就不单独拿出来了,想看代码的可以去这个仓库:https://github.com/Lee-swifter/UToolBox 。 这个一个工具箱的APP,里面包含有一些查询的工具,选择“电视节目表”,然后随便进入一个频道,就可以看到这个例子了。代码的话请搜索类 TvChannelItem

这个例子其实就是类似于QQ消息界面的侧滑,只是QQ是滑出来两个按钮,而我的只是一个按钮,另外在滑动出来后再进行上下滑动时我没有做处理。下面看一下这个控件是怎么做出来的吧。


功能分析

首先可以看到,这个侧滑是列表中的条目的功能,那么需不需要对ListViewRecyclerView做一做手脚呢?这个是不需要的,因为这个侧滑只是属于条目的功能,虽然是放在列表里面的,但是并非需要列表特别支持。另外如果要修改列表控件的话,势必会降低这个功能的可移植性。所以我们仅仅是针对每一个条目写一个可侧滑的自定义控件。虽然很多人会说上下滑动的东西里面再加上左右滑动,肯定会出现事件冲突的情况。没错,这个问题是有的,但是也很好解决。

说句题外话,ListView已经有些过时,现在应该转到RecyclerView上了,这个例子中的代码使用的都是RecyclerView

创建自定义控件

既然已经确定只需要创建自定义控件,那么就开始考虑这个自定义控件要怎么去设计。

控件的设计

  1. 首先,能看到这个控件是由一些其他基本控件组成,因此我们需要写的只是一个组合控件,而非继承自View类;
  2. 再次,控件中所有的基本控件整体是横向排列的,使用LinearLayout可以方便的做出这种布局。因此我们继承LinearLayout
  3. 关于滑动:因为控件的内容是要大于控件的宽度的,因此再滑动的时候应该移动的是控件的内容,而不是控件的位置。这个说是好说,但是这里有一些函数和变量如果弄混了,就很容易卡在这里;
  4. 上下滑动与左右滑动的冲突:首先,我们必须要判断用户当前是要进行上下滑动还是左右滑动,如果是上下滑动,可以交由RecyclerView来处理;如果是左右滑动,那么我们必须屏蔽列表的事件拦截,至于怎么滑动,就是我们自己说的算了。

控件的实现

整体就是这些问题了,下面就开始编码,如果在写代码过程中出现了什么问题,那就再去解决什么问题。

  1. 自定义控件的布局:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><!-- 注意下面这个布局的宽度是match_parent的,也就是占用整个控件宽度--><LinearLayout
        android:orientation="vertical"android:padding="5dip"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center_vertical"><TextView
            android:id="@+id/widget_channel_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:lines="1"android:textSize="16sp"/><TextView
            android:id="@+id/widget_channel_rel"android:layout_marginTop="5dip"android:layout_width="wrap_content"android:lines="1"android:layout_height="wrap_content"/></LinearLayout><!-- 这个Button是放在上面的布局的右边,也就是超出了控件显示部分--><Button
        android:id="@+id/widget_channel_live_button"android:layout_width="100dip"android:layout_height="match_parent"android:text="@string/live"android:textSize="16sp"android:background="@android:color/holo_green_light"/></merge>

这里需要注意的第一个LinearLayout的宽度和下面的Button的宽度,这两个宽度是这个布局的重点。

  1. 创建自定义控件:
public class TvChannelItem extends LinearLayout {private int touchSlop;public TvChannelItem(Context context) {super(context, null);}public TvChannelItem(Context context, AttributeSet attrs) {this(context, attrs, 0);}public TvChannelItem(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);setOrientation(HORIZONTAL);LayoutInflater.from(context).inflate(R.layout.widget_tv_channel, this);name = ButterKnife.findById(this, R.id.widget_channel_name);url = ButterKnife.findById(this, R.id.widget_channel_rel);button = ButterKnife.findById(this, R.id.widget_channel_live_button);touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();}
}

在创建自定义控件的时候,不需要什么特别的操作,只是将布局通过LayoutInflater引入进来,并找到响应的控件。注意在构造时初始化了一个变量touchSlop,这个变量指的是可以考虑为用户进行滑动操作的最小像素距离。也就是说如果滑动超过了这个值,那么认为是滑动操作;如果是小于这个值,则认为是点击操作。

  1. 滑动处理

滑动的处理是这个控件的关键部分,内容移动、冲突处理都在这里做。下面贴出代码,并在代码中给出注释:

@Override
public boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN://记录按下的位置downX = event.getRawX();downY = event.getRawY();break;case MotionEvent.ACTION_MOVE:float nowX = event.getRawX();float nowY = event.getRawY();//判断用户是上下滑动还是左右滑动if (!touchMode && (Math.abs(nowX - downX) > touchSlop || Math.abs(nowY - downY) > touchSlop)) {touchMode = true;   //一旦该变量被置为true,则滑动方向确定if (Math.abs(nowX - downX) > touchSlop && Math.abs(nowY - downY) <= touchSlop) {slide = true;   //此时认为是左右滑动getParent().requestDisallowInterceptTouchEvent(true);   //请求父控件不要拦截触摸事件//以下代码避免出发点击事件MotionEvent cancelEvent = MotionEvent.obtain(event);cancelEvent.setAction(MotionEvent.ACTION_CANCEL | (event.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));onTouchEvent(cancelEvent);}}if (slide) {float diffX = downX - nowX + lastScrollX;if (diffX < 0)  //设置阻尼diffX /= 3;else if (diffX > button.getWidth())diffX = (diffX - button.getWidth()) / 3 + button.getWidth();scrollTo((int) diffX, 0);   //滑动到手指位置}break;case MotionEvent.ACTION_UP:if (slide) {    //如果是左右滑动,那么松手时需要自动滑到指定位置ValueAnimator animator;     //使用的是ValueAnimator,而非Scrollerif (getScrollX() > button.getWidth() / 2) {animator = ValueAnimator.ofInt(getScrollX(), button.getWidth());} else {animator = ValueAnimator.ofInt(getScrollX(), 0);}animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {scrollTo((Integer) animation.getAnimatedValue(), 0);}});animator.start();slide = false;}touchMode = false;  //重置变量break;}return super.onTouchEvent(event);
}

以上就是本控件的关键代码。由几个地方需要讲解一下:

  1. getParent().requestDisallowInterceptTouchEvent(true); 此代码用于避免父控件拦截事件。因为在Android的事件传递过程中,如果一个控件的onTouchEvent函数返回true,那么后续的事件都会传递到这个控件中处理,但是此时父控件仍然可以拦截事件,而子控件会接收到一个ACTION_CANCEL的事件。在本例中,此行代码可以避免在横向滑动时触发上下滑动。
  2. 此处移动是移动的控件中的内容,而控件本身没有移动,scrollX就是只内容距控件左边的相对距离。如果你使用了translationX,那么你会发现控件位置在移动,而右边的按钮并没有被移动出来。如果对这个问题有疑问,可以看看这篇文章http://blog.csdn.net/whsdu929/article/details/52152520。
  3. 在手指抬起来的时候,滑出来的控件将滑回指定位置,此时可以用Scroller来实现,但本例中使用的是ValueAnimator,也仅仅是这个要比用Scroller方便一些。至于ValueAnimator的用法,本例子只是最简单的,其详细用法可以自行搜索。

使用

控件已经写好了,那么就看看怎么使用。因为这个控件仅仅是一个LinearLayout,因此其使用也没有需要额外注意的地方,只要会使用RecyclerView,就会使用这个。只不过把布局换成了单个自定义控件而已。

下面是布局代码:

<?xml version="1.0" encoding="utf-8"?>
<lic.swifter.box.widget.TvChannelItem xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/item_tv_channel"android:layout_width="match_parent"android:layout_height="70dip" />

Adapter代码:

public class TvChannelAdapter extends RecyclerView.Adapter<TvChannelHolder> {public class TvChannelHolder extends RecyclerView.ViewHolder {private TvChannelItem channelItem;public TvChannelHolder(View itemView) {super(itemView);channelItem = ButterKnife.findById(itemView, R.id.item_tv_channel);}public void setChannel(TvChannel channel) {channelItem.setChannel(channel);}}private List<TvChannel> list;public TvChannelAdapter(List<TvChannel> list) {this.list = list;}@Overridepublic TvChannelHolder onCreateViewHolder(ViewGroup parent, int viewType) {View rootView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_tv_channel, parent, false);return new TvChannelHolder(rootView);}@Overridepublic void onBindViewHolder(TvChannelHolder holder, int position) {holder.setChannel(list.get(position));}@Overridepublic int getItemCount() {return list.size();}
}

RecyclerView布局代码:

recycler.setLayoutManager(new LinearLayoutManager(this));
recycler.setAdapter(new TvChannelAdapter(response.result));

以上代码中包含了我项目中的一些内容,但是那些只是数据的封装。总体使用方式就是这样,就是RecyclerView正常使用而已。

代码

本文章中的代码都可以在https://github.com/Lee-swifter/UToolBox 中找到,搜索TvChannelItem就可以找到本文中描述的自定义控件。
也可以从这里直接下载应用http://fir.im/tobox ,在应用中查看效果(选择“电视节目表”,然后随便进入一个频道,就可以看到本例子)。

Android 写一个可以横向滑动条目的列表相关推荐

  1. 用Android写一个记事本软件

    利用安卓知识写一个简单的记事本 实现添加文本,修改文本,显示文本添加的时间,以及长按文本时能够删除文本.我使用用的是安卓自带的数据库SQLite. 首先创建一个工具类 public class DBU ...

  2. Android写一个简易计算器(可以实现连续计算)

    发一个库存程序,好像是几个礼拜之前写的吧,是一个用安卓实现的简易的计算器,写这个小程序之前,看了很多人写的计算器,觉得使用一个 EditText,并将它设置为不可编写,是比较好的解决方案. 设计思路主 ...

  3. 印象笔记android 闪退,Android 写一个属于自己的印象笔记

    之前在看大部分的Android 富文本编辑几乎都是利用webview实现,所以,便有了做一个Android原生的富文本编辑器的主意. 样例 照惯例先秀一下图: 列表 预览模式 编辑模式 该富文本编辑器 ...

  4. mint-ui 写一个下拉滑动选择,mt-popup和mt-picker结合使用

    <template><div id="feedback"><div @click="getpopupVisible">产品选 ...

  5. 【Android】SerialPortFinder学习笔记,显示串口列表

    显示串口列表这个操作还不涉及底层的东西,因为Android与Linux相似,有串口设备就会在/dev目录下生成一个文件,比如/dev/ttyS0之类的,在谷歌的ndroid-serialport-ap ...

  6. android 层叠轮播,vue手写一个卡片化层叠轮播(支持滑动,移动端连续滚动,点击)...

    项目需求,需要写一个卡片化层叠的轮播,找了下插件都没有合适的,于是写了一个展示5个卡片的轮播 先看效果图: 卡片化层叠轮播 5个卡片要计算各自的高度,宽度,利用相对定位计算出各自的位置 然后trans ...

  7. android media player实现一个可手势滑动控制 + 可以调节分辨率|字幕|倍速的视频播放器(MediaPlayer + ExoPlayer实现)

    文章来自:http://blog.csdn.net/intbird 转载请说明出处 五一第一天在家休息(后续休息有空会进行升级) 看了一下视频播放的相关东西 写了一个简单的触摸视频播放器 (使用 Me ...

  8. Android横向滑动加载更多的控件的实现---HorizontalScrollSlideView

    Android横向滑动加载更多的控件的实现-HorizontalScrollSlideView 需求 之前公司业务要求做一个横向滑动的,可以加载更多的控件,第一时间想到的就是 RecyclerView ...

  9. Android学QQ空间相册浏览类型横向滑动效果显示多图片MyHorizontalScrollView

    Android学QQ空间相册浏览类型横向滑动效果显示多图片MyHorizontalScrollView 我们来定制一下吧 布局文件:activity_main.xml <LinearLayout ...

最新文章

  1. linux 卸载aria2,Linux Mint 19下安装aria2的过程完整总结
  2. 【Hibernate】Hibernate实体关系映射实例解析
  3. 按照时间,每天分区;按照数字,200000一个分区
  4. 交叉编译iperf源代码
  5. 中国新时代贡献人物_关于如何鼓励新贡献者的8个新博客文章
  6. 颜值实力派—打造MySQL运行监控环境
  7. php a文件怎么继承b文件的类,php 如何将存在a文件中图片移到b文件中
  8. 高效开发Android App的10个建议
  9. 63. windows php 加载不了 curl
  10. 中文信息杂志中文信息杂志社中文信息编辑部2022年第6期目录
  11. 平安保险IT员工收入揭秘
  12. win10系统电池图标不见了怎么恢复
  13. android最新版本下载vivo,vivo应用商店下载安卓版
  14. java面向对象编程(六)--四大特征之继承
  15. 帮助你构建自适应布局的30款优秀 jQuery 插件(上篇)
  16. 这些都是我自学时私藏的学习网站/实用工具网站/技术网站,非常适合自学
  17. Zigbee使用MT层实现串口写和读操作,简要了解osal_msg_send消息机制
  18. 新年特供【供应链作战指北】
  19. 回到1989年,用java成为网络皇帝
  20. 研读Rust圣经解析——Rust learn-15(unsafe Rust )

热门文章

  1. python-excel 之表格数据截图保存到本地
  2. 利用永中office解决ubuntu下office文件无法显示音标的问题。
  3. ESP32 DEVKIT V1 引脚图
  4. 计算机二级WPS 选择题(模拟和解析十一)
  5. [附源码]计算机毕业设计JAVA政府公用车辆管理系统
  6. linux mint 17 输入法,linux mint17.2 安装fcitx输入法
  7. AI人脸检测智能分析网关新增车辆检测/车牌识别,支持车辆违停告警
  8. 怎样翻译word文档中的英文,仅需三分钟即可搞定
  9. 软件测试之路已不再是坦途
  10. 2022年高处安装、维护、拆除考试题模拟考试平台操作