Android中不同方向嵌套滑动的解决方式(ListView为样例)
前言:
就像手机QQ的聊天消息列表。一个纵向滑动的ListView列举全部消息,但每一条消息能够横向滑动。
而默认情况下,仅仅能有一个地方消化处理触摸事件,要么ListView吃掉这个事件。要么子Item中能滑动的部件吃掉。两者互相冲突。
是否认为非常分裂?实现起来事实上不复杂。
理解了以后,能够方便延伸到GridView,ViewPager,ScrollView等等滑动控件。
假设对Andoroid触摸事件传递过程不熟悉,请看这里:
为了最简单表达实现方法,我以一个LinearLayout为ListView的Item,里面放了消息的TextView。和一个删除button
基本的思路是:
重写ListView中的Item,也就是LinearLayout的onTouchEvent方法。以监听横向滑动和纵向滑动:
1)纵向滑动时。无论
2)横向滑动时。请求父容器,也即是ListView不要拦截触摸事件,自己在子View(也就是LinearLayout)里面处理就好了,当横向触摸时间结束(MotionEvent.Action_UP)或者划出边界(MotionEvent.Action_CANCEL)时,恢复同意父容器拦截触摸事件。
先上效果图
重写的LinearLayout例如以下:
package ex.oyyj.listviewfulldemo; import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.ViewParent;
import android.widget.LinearLayout; /**
* Created by oyyj on 2015/8/6.
*/
public class HorizontalSlideLayout extends LinearLayout {
private static String TAG = "VerticalSlideLayout";
private int DRAG_X_THROD = 0;
private int SCROLL_X = 0;
private final int ANIM_DURATION = 300; private static final int SLIDE_TO_LEFT = -1;
private static final int SLIDE_TO_RIGHT = 1;
private int mSlideDirection = 0; public HorizontalSlideLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
//推断横划的阈值,为了兼容不同尺寸的设备。以dp为单位
DRAG_X_THROD = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, context.getResources().getDisplayMetrics());
SCROLL_X = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, context.getResources().getDisplayMetrics());
} public HorizontalSlideLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public HorizontalSlideLayout(Context context) {
this(context, null, 0);
} @Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
} int downX, downY;
boolean isNeedToGoBack;
private ObjectAnimator mAnimator; @Override
public boolean onTouchEvent(MotionEvent ev) { boolean isInterceptHere = false;
try {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = (int) ev.getX();
downY = (int) ev.getY();
isInterceptHere = true;
if (mAnimator != null) {
mAnimator.cancel();
}
break; case MotionEvent.ACTION_MOVE: int dx = (int) Math.abs(ev.getX() - downX);
int dy = (int) Math.abs(ev.getY() - downY);
if (dx > dy && dx > DRAG_X_THROD) {
Log.i(TAG, "横划! 拦截它");
setParentInterceptTouchEvent(true);
isInterceptHere = true;
mSlideDirection = (ev.getX() - downX) > 0 ? SLIDE_TO_RIGHT : SLIDE_TO_LEFT;
if (mSlideDirection == SLIDE_TO_LEFT) {
isNeedToGoBack = true;
playAnimation(SCROLL_X, ANIM_DURATION);
} else if (mSlideDirection == SLIDE_TO_RIGHT && isNeedToGoBack) {
playAnimation(0, ANIM_DURATION);
}
} else if (dy > dx) {
Log.i(TAG, "竖划! 不拦截");
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
setParentInterceptTouchEvent(false);
isInterceptHere = false;
downX = 0;
downY = 0;
break;
}
} catch (Exception e) {
e.printStackTrace();
} return isInterceptHere;
} private void playAnimation(int translationX, int duration) {
if (mAnimator != null) {
mAnimator.cancel();
} mAnimator = ObjectAnimator.ofInt(this, "scrollX", translationX);
mAnimator.setDuration(duration);
mAnimator.start(); mAnimator.addListener(listener);
} /* 这个函数非常重要,请求禁止父容器拦截触摸事件 */
public void setParentInterceptTouchEvent(boolean disallow) {
ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(disallow);
}
} Animator.AnimatorListener listener = new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) { } @Override
public void onAnimationEnd(Animator animator) {
mAnimator = null;
} @Override
public void onAnimationCancel(Animator animator) {
mAnimator = null;
} @Override
public void onAnimationRepeat(Animator animator) { }
};
}
贴上具体调用代码:
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"
android:background="#ffffff"> <ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/listView"
android:divider="@drawable/divider"
android:listSelector="@drawable/selector_list_background"
android:layout_centerInParent="true" />
</RelativeLayout>
MainActivity.java
package ex.oyyj.listviewfulldemo; import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView; public class MainActivity extends Activity { private ListView mlistView;
String[] films = new String[]{
"煎饼侠",
"猎妖记",
"大圣归来",
"道士下山",
"王朝的女人·杨贵妃",
"栀子花开",
"太平轮(下) ",}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mlistView = (ListView) findViewById(R.id.listView);
final TitleAdapter titleAdapter = new TitleAdapter(LayoutInflater.from(this), films);
mlistView.setAdapter(titleAdapter);
} private class TitleAdapter extends BaseAdapter { String[] itemNames;
LayoutInflater inflater; public TitleAdapter(LayoutInflater _inflater, String[] names) {
inflater = _inflater;
itemNames = names.clone();
} @Override
public Object getItem(int i) {
return itemNames[i];
} @Override
public long getItemId(int i) {
return i;
} @Override
public int getCount() {
return itemNames.length;
} @Override
public View getView(int i, View view, ViewGroup viewGroup) {
try {
ViewHolder holder = new ViewHolder();
if (view == null ) {
view = inflater.inflate(R.layout.item_layout, viewGroup, false);
holder.title = (TextView) view.findViewById(R.id.item);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
} if (holder != null && holder.title != null) {
TextView tv = holder.title;
tv.setText(itemNames[i]);
}
} catch (Exception e) {
e.printStackTrace();
}
return view;
} private class ViewHolder {
TextView title;
}
}
}
转自:http://blog.csdn.net/oyyj42/article/details/47333443
Android中不同方向嵌套滑动的解决方式(ListView为样例)的更多相关文章
- Android中关于JNI 的学习(四)简单的样例,温故而知新
在第零篇文章简单地介绍了JNI编程的模式之后.后面两三篇文章,我们又针对JNI中的一些概念做了一些简单的介绍,也不知道我究竟说的清楚没有.但相信非常多童鞋跟我一样.在刚開始学习一个东西的时候,入门最好 ...
- Android中关于JNI 的学习(零)简单的样例,简单地入门
Android中JNI的作用,就是让Java可以去调用由C/C++实现的代码,为了实现这个功能.须要用到Anrdoid提供的NDK工具包,在这里不讲怎样配置了,好麻烦,配置了好久. . . 本质上,J ...
- Android开发:ScrollView嵌套GridView的解决办法
Android开发:ScrollView嵌套GridView的解决办法 前些日子在开发中用到了需要ScrollView嵌套GridView的情况,由于这两款控件都自带滚动条,当他们碰到一起的时候便 ...
- Android中三种超实用的滑屏方式汇总(转载)
Android中三种超实用的滑屏方式汇总 现如今主流的Android应用中,都少不了左右滑动滚屏这项功能,(貌似现在好多人使用智能机都习惯性的有事没事的左右滑屏,也不知道在干什么...嘿嘿),由于 ...
- android中实现view可以滑动的六种方法续篇(二)
承接上一篇,上一篇中讲解了实现滑动的第五种方法,如果你还没读过,可点击下面链接: http://www.cnblogs.com/fuly550871915/p/4985482.html 这篇文章现在来 ...
- Android: 利用SurfaceView绘制股票滑动直线解决延迟问题
1.背景介绍 最近项目要绘制股票走势图,并绘制能够跟随手指滑动的指示线(Indicator)来精确查看股票价格和日期.如下图所示: 上图中的那条白色直线就是股票的指示线,用来跟随手指精确确定股票的时间 ...
- android中常见的内存泄漏和解决的方法
android中的内存溢出预计大多数人在写代码的时候都出现过,事实上突然认为工作一年和工作三年的差别是什么呢.事实上干的工作或许都一样,产品汪看到的结果也都一样,那差别就是速度和质量了. 写在前面的一 ...
- C++中避免内存泄露常见的解决方式
常见内存泄露及解决方式-选自ood启发录 new/delete, array new/arrray delete匹配 case 1: 在类的构造函数与析构函数中没有匹配地调用 new/delete! ...
- Eclipse出现"Running Android Lint has encountered a problem"解决方式
近期打开Eclipse的时候,总是发生这种一个错误:"Running Android Lint has encountered a problem".截图例如以下: . 可是Ecl ...
随机推荐
- opencv函数之cv.InRange函数
2018-03-0421:22:46 (1)cv.InRange函数 void cvInRange(//提取图像中在阈值中间的部分 const CvArr* src,//目标图像const CvArr ...
- Pro ASP.Net Core MVC 6th 第四章
第四章 C# 关键特征 在本章中,我描述了Web应用程序开发中使用的C#特征,这些特征尚未被广泛理解或经常引起混淆. 这不是关于C#的书,但是,我仅为每个特征提供一个简单的例子,以便您可以按照本书其余 ...
- Microsoft SQL Server学习(七)--函数视图
系统函数 视图 索引 1.系统函数 (1) ()数学函数 Abs() 绝对值 Floor() 向下取整 Ceiling() 向上取整 Sin() 返回指定角度(以弧度为单位)的三角正弦值 Pi() 圆 ...
- Xilinx FPGA编程技巧之常用时序约束详解
1. 基本的约束方法 为了保证成功的设计,所有路径的时序要求必须能够让执行工具获取.最普遍的三种路径为: 输入路径(Input Path),使用输入约束 寄存器到寄存器路径(Register-to ...
- 【Linux】Tomcat安装及端口配置
安装环境 :Linux(CentOS 64位) 安装软件 : apache-tomcat-9.0.20.tar.gz(下载地址http://tomcat.apache.org/) 一:JDK安装配置 ...
- Java代码实现WORD转PDF
第一步: 安装OpenOffice 在此良心提供windows版本安装文件 链接:https://pan.baidu.com/s/17pPCkcS1C46VtLhevqSgPw 密码:vmlu ...
- 出现For input string: "" 错误
然后是因为后台生成的是一个数组,前台取的是一个对象,所以会产生这个错误 前后台交互时 mv.addObject("vo1",fhList.get(0));}将数组改成fhList. ...
- 这份Koa的简易Router手敲指南请收下
上一期链接--也就是本文的基础,参考KOA,5步手写一款粗糙的web框架 本文参考仓库:点我 Router其实就是路径匹配,通过匹配路径,返回给用户相应的网站内容. 以下方例子为例,主要通过提取req ...
- Python学习第二阶段,day1, 装饰器,生成器,迭代器
装饰器 不得不说,这是对初学者最难以理解的概念了,虽然我学过面向对象,但还是被搞懵逼了..前面还好理解,主要是后面“装饰器的装饰器”我理解不了.装饰器工厂,根据传入的参数不同去返回不同的装饰器,我不得 ...
- C语言二叉树的创建、(先中后序)遍历以及存在的问题
#include<stdlib.h> #include<stdio.h> #define True 1 #define False 0 typedef char TElemTy ...