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

AndroidApp中实现向右滑动销毁功能的要点解析

这篇文章主要介绍了Android应用中实现向右滑动销毁条目功能的要点解析,有些类似于iOSApp中的滑动页面删除效果,需要的朋友可以参考下

今天给大家带来一个向右滑动销毁Activity的效果,Activtiy随着手指的移动而移动,该效果在Android应用中还是比较少见的,在IOS中就比较常见了,例如“网易新闻” ,"美食杰" , "淘宝"等应用采用此效果,而Android应用中“知乎”采用的也是这种滑动切换Activity的效果, 不过我发现“淘宝”并没有随着手势的移动而移动,只是捕捉到滑动手势,然后产生平滑切换界面的动画效果,这个在Android中还是很好实现的,  网上很多滑动切换Activity的Demo貌似都是这种效果的吧,如果要实现类似“网易新闻”的随手势的滑动而滑动,似乎就要复杂一些了,我之前在IOS中看到"网易新闻"的这种效果就很感兴趣,然后群里也有朋友问我怎么实现类似“知乎”这个应用的滑动切换的效果,我也特意去下了一个“知乎”,在之前的实现中我遇到了一些瓶颈,没有实现出来就搁置了在那里,今天无意中看到给Activity设置透明的背景,于是乎我恍然大悟,真是灵感来源于瞬间,不能强求啊,然后自己就将此效果实现了出来,给大家分享一下,希望给有此需求的你一点点帮助。
不知道大家对Scroller这个类以及View的scrollBy() 和scrollTo()的使用熟悉不?我之前介绍了Scroller类的滑动实现原理Android 带你从源码的角度解析Scroller的滚动实现原理,在那里面也介绍了scrollBy() 和scrollTo()方法,不明白的同学可以去看看,这对实现此效果有很大的帮助,了解scrollBy() 和scrollTo()的朋友应该知道,如果想对某个View(例如Button)就行滚动,我们直接调用该View(Button)的scrollBy()方法,并不是该View(Button)进行滚动,而是该View里面的内容(Button上面的文字)进行滚动,所以我们假如要让View整体滚动就需要对其View的父布局调用scrollBy()方法,回到这篇文章来,假如我们想要对一个Activity进行滚动,我们就需求对这个Activity布局文件的顶层布局的父布局进行滚动
例如下面的XML布局文件

 
 
 
 

如果我们对LinearLayout进行滚动,并不能实现我们想要的效果,而只能对LinearLayout里面的内容或者说是子View进行滚动,所以我们需要获取利用LinearLayout的getParent()方法获取父布局,其实Android系统会对我们的布局文件的最外层套一个FrameLayout,所以我们其实就是对FrameLayout进行滚动就行了
了解了实现的原理之后,我们就来编写代码吧,首先新建一个android工程,取名SildingFinish
由于我们的需求可能不是在一个界面提供这个滑动切换的效果,所以我们应该将这部分滑动的逻辑抽取出来,我这里就他写成了一个扩展RelativeLayout的自定义布局SildingFinishLayout,首先我们看其代码

package com.example.view; 
 
import android.content.Context; 
import android.util.AttributeSet; 
import android.view.MotionEvent; 
import android.view.View; 
import android.view.View.OnTouchListener; 
import android.view.ViewConfiguration; 
import android.view.ViewGroup; 
import android.widget.AbsListView; 
import android.widget.RelativeLayout; 
import android.widget.ScrollView; 
import android.widget.Scroller; 
 
/** 
 * 自定义可以滑动的RelativeLayout, 类似于IOS的滑动删除页面效果,当我们要使用 
 * 此功能的时候,需要将该Activity的顶层布局设置为SildingFinishLayout, 
 * 然后需要调用setTouchView()方法来设置需要滑动的View 
 * 
 * @author xiaanming 
 * 
 * @blog http://blog.csdn.net/xiaanming 
 * 
 */ 
public class SildingFinishLayout extends RelativeLayout implements 
    OnTouchListener { 
  /** 
   * SildingFinishLayout布局的父布局 
   */ 
  private ViewGroup mParentView; 
  /** 
   * 处理滑动逻辑的View 
   */ 
  private View touchView; 
  /** 
   * 滑动的最小距离 
   */ 
  private int mTouchSlop; 
  /** 
   * 按下点的X坐标 
   */ 
  private int downX; 
  /** 
   * 按下点的Y坐标 
   */ 
  private int downY; 
  /** 
   * 临时存储X坐标 
   */ 
  private int tempX; 
  /** 
   * 滑动类 
   */ 
  private Scroller mScroller; 
  /** 
   * SildingFinishLayout的宽度 
   */ 
  private int viewWidth; 
  /** 
   * 记录是否正在滑动 
   */ 
  private boolean isSilding; 
   
  private OnSildingFinishListener onSildingFinishListener; 
  private boolean isFinish; 
   
 
  public SildingFinishLayout(Context context, AttributeSet attrs) { 
    this(context, attrs, 0); 
  } 
 
  public SildingFinishLayout(Context context, AttributeSet attrs, int defStyle) { 
    super(context, attrs, defStyle); 
 
    mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 
    mScroller = new Scroller(context); 
  } 
 
  @Override 
  protected void onLayout(boolean changed, int l, int t, int r, int b) { 
    super.onLayout(changed, l, t, r, b); 
    if (changed) { 
      // 获取SildingFinishLayout所在布局的父布局 
      mParentView = (ViewGroup) this.getParent(); 
      viewWidth = this.getWidth(); 
    } 
  } 
 
  /** 
   * 设置OnSildingFinishListener, 在onSildingFinish()方法中finish Activity 
   * 
   * @param onSildingFinishListener 
   */ 
  public void setOnSildingFinishListener( 
      OnSildingFinishListener onSildingFinishListener) { 
    this.OnSildingFinishListener= onSildingFinishListener; 
  } 
 
  /** 
   * 设置Touch的View 
   * 
   * @param touchView 
   */ 
  public void setTouchView(View touchView) { 
    this.touchView = touchView; 
    touchView.setOnTouchListener(this); 
  } 
 
  public View getTouchView() { 
    return touchView; 
  } 
 
  /** 
   * 滚动出界面 
   */ 
  private void scrollRight() { 
    final int delta = (viewWidth + mParentView.getScrollX()); 
    // 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item 
    mScroller.startScroll(mParentView.getScrollX(), 0, -delta + 1, 0, 
        Math.abs(delta)); 
    postInvalidate(); 
  } 
 
  /** 
   * 滚动到起始位置 
   */ 
  private void scrollOrigin() { 
    int delta = mParentView.getScrollX(); 
    mScroller.startScroll(mParentView.getScrollX(), 0, -delta, 0, 
        Math.abs(delta)); 
    postInvalidate(); 
  } 
 
  /** 
   * touch的View是否是AbsListView, 例如ListView, GridView等其子类 
   * 
   * @return 
   */ 
  private boolean isTouchOnAbsListView() { 
    return touchView instanceof AbsListView ? true : false; 
  } 
 
  /** 
   * touch的view是否是ScrollView或者其子类 
   * 
   * @return 
   */ 
  private boolean isTouchOnScrollView() { 
    return touchView instanceof ScrollView ? true : false; 
  } 
 
  @Override 
  public boolean onTouch(View v, MotionEvent event) { 
    switch (event.getAction()) { 
    case MotionEvent.ACTION_DOWN: 
      downX = tempX = (int) event.getRawX(); 
      downY = (int) event.getRawY(); 
      break; 
    case MotionEvent.ACTION_MOVE: 
      int moveX = (int) event.getRawX(); 
      int deltaX = tempX - moveX; 
      tempX = moveX; 
      if (Math.abs(moveX - downX) > mTouchSlop 
          && Math.abs((int) event.getRawY() - downY) = 0 && isSilding) { 
        mParentView.scrollBy(deltaX, 0); 
 
        // 屏蔽在滑动过程中ListView ScrollView等自己的滑动事件 
        if (isTouchOnScrollView() || isTouchOnAbsListView()) { 
          return true; 
        } 
      } 
      break; 
    case MotionEvent.ACTION_UP: 
      isSilding = false; 
      if (mParentView.getScrollX() <= -viewWidth / 2) { 
        isFinish = true; 
        scrollRight(); 
      } else { 
        scrollOrigin(); 
        isFinish = false; 
      } 
      break; 
    } 
 
    // 假如touch的view是AbsListView或者ScrollView 我们处理完上面自己的逻辑之后 
    // 再交给AbsListView, ScrollView自己处理其自己的逻辑 
    if (isTouchOnScrollView() || isTouchOnAbsListView()) { 
      return v.onTouchEvent(event); 
    } 
 
    // 其他的情况直接返回true 
    return true; 
  } 
 
  @Override 
  public void computeScroll() { 
    // 调用startScroll的时候scroller.computeScrollOffset()返回true, 
    if (mScroller.computeScrollOffset()) { 
      mParentView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 
      postInvalidate(); 
 
      if (mScroller.isFinished()) { 
 
        if (onSildingFinishListener != null && isFinish) { 
          onSildingFinishListener.onSildingFinish(); 
        } 
      } 
    } 
  } 
   
 
  public interface OnSildingFinishListener { 
    public void onSildingFinish(); 
  } 
 
} 

我们在onLayout()方法中利用getParent()方法获取该布局的父布局和获取其控件的宽度,主要是为之后的实现做准备工作。
我们的滑动逻辑主要是利用View的scrollBy() 方法, scrollTo()方法和Scroller类来实现的,当手指拖动视图的时候,我们监听手指在屏幕上滑动的距离利用View的scrollBy() 方法使得View随着手指的滑动而滑动,而当手指离开屏幕,我们在根据逻辑使用Scroller类startScroll()方法设置滑动的参数,然后再根据View的scrollTo进行滚动。
对于View的滑动,存在一些Touch事件消费的处理等问题,因此我们需要对View的整个Touch事件很熟悉 ,最主要的就是Activity里面有一些ListView、 GridView、ScrollView等控件了, 假如我们Activity里面存在ListView、GridView等控件的话,我们对Activity的最外层布局进行滚动根本就无效果,因为Touch事件被ListView、GridView等控件消费了,所以Activity的最外层布局根本得不到Touch事件,也就实现不了Touch逻辑了,所以为了解决此Touch事件问题我提供了setTouchView(View touchView) 方法,这个方法是将Touch事件动态的设置到到View上面,所以针对上面的问题,我们将OnTouchListener直接设置到ListView、GridView上面,这样子就避免了Activity的最外层接受不到Touch事件的问题了

接下来看onTouch()方法
首先我们在ACTION_DOWN记录按下点的X,Y坐标
然后在ACTION_MOVE中判断,如果我们在水平方向滑动的距离大于mTouchSlop并且在竖直方向滑动的距离小于mTouchSlop,表示Activity处于滑动状态,我们判断如果touchView是ListView、GridView或者其子类的时候,因为我们手指在ListView、GridView上面,伴随着item的点击事件的发生,所以我们对touchView设置ACTION_CANCEL来取消item的点击事件,然后对该布局的父布局调用scrollBy()进行滚动,并且如果TouchView是AbsListView或者ScrollView直接返回true,来取消AbsListView或者ScrollView本身的ACTION_MOVE事件,最直观的感受就是我们在滑动Activity的时候,禁止AbsListView或者ScrollView的上下滑动
最后在ACTION_UP中判断如果手指滑动的距离大于控件长度的二分之一,表示将Activity滑出界面,否则滑动到起始位置,我们利用Scroller类的startScroll()方法设置好开始位置,滑动距离和时间,然后调用postInvalidate()刷新界面,之后就到computeScroll()方法中,我们利用scrollTo()方法对该布局的父布局进行滚动,滚动结束之后,我们判断界面是否滑出界面,如果是就调用OnSildingFinishListener接口的onSildingFinish()方法,所以只要在onSildingFinish()方法中finish界面就行了
整个滑动布局的代码就是这个样子,接下来我们就来使用了,主界面Activity只有三个按钮,分别跳转到普通布局的Activity,有ListView的Activity和有ScrollView的Activity中

 
 
  

 
然后就是MainActivity的代码,根据ID实例化Button,然后为Button设置OnClickListener事件,不同的按钮跳转到不同的Activity,然后设置从右向左滑动的动画,重写onBackPressed()方法,当我们按下手机物理键盘的返回键,添加从左向右滑出的动画

package com.example.slidingfinish; 
 
import android.app.Activity; 
import android.content.Intent; 
import android.os.Bundle; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.view.Window; 
import android.widget.Button; 
 
import com.example.slidingfinish.R; 
 
public class MainActivity extends Activity implements OnClickListener { 
 
  @Override 
  protected void onCreate(Bundle savedInstanceState) { 
    requestWindowFeature(Window.FEATURE_NO_TITLE); 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
 
    Button mButtOnNormal= (Button) findViewById(R.id.normal_activity); 
    mButtonNormal.setOnClickListener(this); 
 
    Button mButtOnAbs= (Button) findViewById(R.id.absListview_activity); 
    mButtonAbs.setOnClickListener(this); 
 
    Button mButtOnScroll= (Button) findViewById(R.id.scrollview_activity); 
    mButtonScroll.setOnClickListener(this); 
 
  } 
 
  @Override 
  public void onClick(View v) { 
    Intent mIntent = null; 
    switch (v.getId()) { 
    case R.id.normal_activity: 
      mIntent = new Intent(MainActivity.this, NormalActivity.class); 
      break; 
    case R.id.absListview_activity: 
      mIntent = new Intent(MainActivity.this, AbsActivity.class); 
      break; 
    case R.id.scrollview_activity: 
      mIntent = new Intent(MainActivity.this, ScrollActivity.class); 
      break; 
    } 
 
    startActivity(mIntent); 
    overridePendingTransition(R.anim.base_slide_right_in, R.anim.base_slide_remain); 
  } 
   
  //Press the back button in mobile phone 
  @Override 
  public void onBackPressed() { 
    super.onBackPressed(); 
    overridePendingTransition(0, R.anim.base_slide_right_out); 
  } 
 
} 

在这里我之贴出含有ListView的Activity的代码,先看布局,我们自定义滑动布局SildingFinishLayout应该放在XML的最顶层

<&#63;xml version="1.0" encoding="UTF-8"&#63;> 
 
 
   
   
   
   
 

package com.example.slidingfinish; 
 
import java.util.ArrayList; 
import java.util.List; 
 
import android.app.Activity; 
import android.content.Intent; 
import android.os.Bundle; 
import android.view.View; 
import android.view.Window; 
import android.widget.AdapterView; 
import android.widget.AdapterView.OnItemClickListener; 
import android.widget.ArrayAdapter; 
import android.widget.ListView; 
 
import com.example.slidingfinish.R; 
import com.example.view.SildingFinishLayout; 
import com.example.view.SildingFinishLayout.OnSildingFinishListener; 
 
public class AbsActivity extends Activity { 
  private List list = new ArrayList(); 
 
  @Override 
  protected void onCreate(Bundle savedInstanceState) { 
    requestWindowFeature(Window.FEATURE_NO_TITLE); 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_abslistview); 
 
    for (int i = 0; i <= 30; i++) { 
      list.add("测试数据" + i); 
    } 
 
    ListView mListView = (ListView) findViewById(R.id.listView); 
    ArrayAdapter adapter = new ArrayAdapter( 
        AbsActivity.this, android.R.layout.simple_list_item_1, list); 
    mListView.setAdapter(adapter); 
 
    SildingFinishLayout mSildingFinishLayout = (SildingFinishLayout) findViewById(R.id.sildingFinishLayout); 
    mSildingFinishLayout 
        .setOnSildingFinishListener(new OnSildingFinishListener() { 
 
          @Override 
          public void onSildingFinish() { 
            AbsActivity.this.finish(); 
          } 
        }); 
 
    // touchView要设置到ListView上面 
    mSildingFinishLayout.setTouchView(mListView); 
 
    mListView.setOnItemClickListener(new OnItemClickListener() { 
 
      @Override 
      public void onItemClick(AdapterView<&#63;> parent, View view, 
          int position, long id) { 
 
        startActivity(new Intent(AbsActivity.this, NormalActivity.class)); 
        overridePendingTransition(R.anim.base_slide_right_in, 
            R.anim.base_slide_remain); 
      } 
    }); 
  } 
 
  // Press the back button in mobile phone 
  @Override 
  public void onBackPressed() { 
    super.onBackPressed(); 
    overridePendingTransition(0, R.anim.base_slide_right_out); 
  } 
 
} 

利用ID找到SildingFinishLayout实例,利用setTouchView()方法设置touchView到ListView上面,然后调用setOnSildingFinishListener()设置OnSildingFinishListener,在onSildingFinish()中finish界面就可以啦。
在运行项目之前还有一个很重要的操作,也是之前我被卡到的问题,就是我们需要对Activity设置为透明,即设置主题android:theme="@android:style/Theme.Translucent"

 
     
     
     
     
     

 
好了,现在我们可以运行项目看看效果啦

正是我们想要的效果,如果想要加入滑动切换界面的效果只需要三步就行了,首先将Activity布局的最外层修改为SildingFinishLayout,然后在Activity里面调用setTouchView()方法设置touchView,设置OnSildingFinishListener监听在onSildingFinish()方法中finish界面,最后设置Activity的背景为透明(不是设置Activity布局文件的最顶层布局背景颜色透明,这点要区分一下)是不是很方便呢?好了,今天的讲解到这里就结束了~


推荐阅读
  • Flutter 2.* 路由管理详解
    本文详细介绍了 Flutter 2.* 中的路由管理机制,包括路由的基本概念、MaterialPageRoute 的使用、Navigator 的操作方法、路由传值、命名路由及其注册、路由钩子等。 ... [详细]
  • 自动验证时页面显示问题的解决方法
    在使用自动验证功能时,页面未能正确显示错误信息。通过使用 `dump($info->getError())` 可以帮助诊断和解决问题。 ... [详细]
  • 微信小程序详解:概念、功能与优势
    微信公众平台近期向200位开发者发送了小程序的内测邀请。许多人对微信小程序的概念还不是很清楚。本文将详细介绍微信小程序的定义、功能及其独特优势。 ... [详细]
  • 本视频教程将带你快速了解 Android 开发的基础知识,并详细讲解如何在 Android 应用中使用 SQLite 数据库进行数据存储和管理。 ... [详细]
  • Framework7:构建跨平台移动应用的高效框架
    Framework7 是一个开源免费的框架,适用于开发混合移动应用(原生与HTML混合)或iOS&Android风格的Web应用。此外,它还可以作为原型开发工具,帮助开发者快速创建应用原型。 ... [详细]
  • 本文详细介绍了如何在 Android 应用中获取系统的版本号,包括具体的应用场景和实现步骤。 ... [详细]
  • 本文介绍了如何使用 CMD 批处理脚本进行文件操作,包括将指定目录下的 PHP 文件重命名为 HTML 文件,并将这些文件复制到另一个目录。 ... [详细]
  • 本文详细介绍了DMA控制器如何通过映射表处理来自外设的请求,包括映射表的设计和实现方法。 ... [详细]
  • Spark中使用map或flatMap将DataSet[A]转换为DataSet[B]时Schema变为Binary的问题及解决方案
    本文探讨了在使用Spark的map或flatMap算子将一个数据集转换为另一个数据集时,遇到的Schema变为Binary的问题,并提供了详细的解决方案。 ... [详细]
  • 在使用Eclipse进行调试时,如果遇到未解析的断点(unresolved breakpoint)并显示“未加载符号表,请使用‘file’命令加载目标文件以进行调试”的错误提示,这通常是因为调试器未能正确加载符号表。解决此问题的方法是通过GDB的`file`命令手动加载目标文件,以便调试器能够识别和解析断点。具体操作为在GDB命令行中输入 `(gdb) file `。这一步骤确保了调试环境能够正确访问和解析程序中的符号信息,从而实现有效的调试。 ... [详细]
  • MySQL的查询执行流程涉及多个关键组件,包括连接器、查询缓存、分析器和优化器。在服务层,连接器负责建立与客户端的连接,查询缓存用于存储和检索常用查询结果,以提高性能。分析器则解析SQL语句,生成语法树,而优化器负责选择最优的查询执行计划。这一流程确保了MySQL能够高效地处理各种复杂的查询请求。 ... [详细]
  • 本项目通过Python编程实现了一个简单的汇率转换器v1.02。主要内容包括:1. Python的基本语法元素:(1)缩进:用于表示代码的层次结构,是Python中定义程序框架的唯一方式;(2)注释:提供开发者说明信息,不参与实际运行,通常每个代码块添加一个注释;(3)常量和变量:用于存储和操作数据,是程序执行过程中的重要组成部分。此外,项目还涉及了函数定义、用户输入处理和异常捕获等高级特性,以确保程序的健壮性和易用性。 ... [详细]
  • 本文介绍了一种自定义的Android圆形进度条视图,支持在进度条上显示数字,并在圆心位置展示文字内容。通过自定义绘图和组件组合的方式实现,详细展示了自定义View的开发流程和关键技术点。示例代码和效果展示将在文章末尾提供。 ... [详细]
  • 深入解析 SAP UI5 Page 控件的构造函数参数及其应用
    深入解析 SAP UI5 Page 控件的构造函数参数及其应用 ... [详细]
  • 使用 ListView 浏览安卓系统中的回收站文件 ... [详细]
author-avatar
yangxin
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有