• Android UI效果实现——Activity滑动退出效果


    更新说明:

    1、在QQ网友北京-旭的提醒下,在SlideFrame的initilize方法中添加了focusable、focusableInTouch、clickable的状态设置,否则会导致部分情况下无法滑动,感谢!

    一、效果动图

    二、使用说明

    使用方法很简单,只有一个类HorizontalActivity,继承自FragmentActivity类,实现了contentView的滑动事件触发和动画效果,要在自己的代码里实现,方法两种:

    1、如果对Activity没特殊要求,直接继承HorizontalActivity即可

    2、如果Activity的父类必须是某一特定类型的Activity子类,则可以仿照我的写法对该类进行继承

    三、HorizontalActivity类的代码

      1 package com.beifeng.widget;
      2 
      3 import android.content.Context;
      4 import android.support.v4.app.FragmentActivity;
      5 import android.util.AttributeSet;
      6 import android.view.LayoutInflater;
      7 import android.view.MotionEvent;
      8 import android.view.View;
      9 import android.view.ViewGroup.LayoutParams;
     10 import android.view.animation.Animation;
     11 import android.view.animation.Animation.AnimationListener;
     12 import android.view.animation.DecelerateInterpolator;
     13 import android.view.animation.Transformation;
     14 import android.widget.FrameLayout;
     15 
     16 /**
     17  * HorizontalActivity:可滑动Activity
     18  * 
     19  * 注意事项: 本Activity中与滑动方向相同的滑动操作会被拦截
     20  * 
     21  * @author HalfmanG2
     22  * @version 1.0.0
     23  * @since JDK7 SDK19
     24  */
     25 public class HorizontalActivity extends FragmentActivity {
     26 
     27     /** 框架视图 */
     28     protected SlideFrame frameView;
     29     /** 内容视图 */
     30     protected View contentView;
     31 
     32     @Override
     33     public void setContentView(int layoutResID) {
     34         // 初始化frame
     35         if (frameView == null) {
     36             // 未初始化则初始化
     37             frameView = new SlideFrame(this);
     38         } else {
     39             // 已经初始化则清空
     40             frameView.removeAllViews();
     41         }
     42         // 创造framelayout的填充参数
     43         FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(-1, -1);
     44         // 获取layoutResId对应的contentView视图并插入frameView
     45         LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
     46         contentView = inflater.inflate(layoutResID, null);
     47         frameView.addView(contentView, params);
     48         // 设置frameview为根视图
     49         super.setContentView(frameView);
     50     }
     51 
     52     @Override
     53     public void setContentView(View view) {
     54         // 初始化frame
     55         if (frameView == null) {
     56             // 未初始化则初始化
     57             frameView = new SlideFrame(this);
     58         } else {
     59             // 已经初始化则清空
     60             frameView.removeAllViews();
     61         }
     62         // 创造framelayout的填充参数
     63         FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(-1, -1);
     64         // 获取view为contentView视图并插入frameView
     65         contentView = view;
     66         frameView.addView(contentView, params);
     67         // 设置frameview为根视图
     68         super.setContentView(frameView);
     69     }
     70 
     71     @Override
     72     public void setContentView(View view, LayoutParams params) {
     73         // 初始化frame
     74         if (frameView == null) {
     75             // 未初始化则初始化
     76             frameView = new SlideFrame(this);
     77         } else {
     78             // 已经初始化则清空
     79             frameView.removeAllViews();
     80         }
     81         // 创造framelayout的填充参数
     82         FrameLayout.LayoutParams fp = new FrameLayout.LayoutParams(-1, -1);
     83         // 获取view为contentView视图并插入frameView
     84         contentView = view;
     85         frameView.addView(contentView, fp);
     86         // 设置frameview为根视图
     87         super.setContentView(frameView, params);
     88     }
     89 
     90     /**
     91      * 推出页面
     92      */
     93     protected void onSlideFinish() {
     94         finish();
     95     }
     96 
     97     /**
     98      * 位移内容视图到
     99      * 
    100      * @param position
    101      *            目标位置
    102      */
    103     public void slideTo(int position) {
    104         if (android.os.Build.VERSION.SDK_INT > 10) {
    105             contentView.setX(position);
    106         } else {
    107             android.widget.FrameLayout.LayoutParams params = (android.widget.FrameLayout.LayoutParams) contentView
    108                     .getLayoutParams();
    109             params.setMargins(position, 0, -position, 0);
    110             contentView.setLayoutParams(params);
    111         }
    112     }
    113 
    114     /**
    115      * 获得当前容器位移
    116      * 
    117      * @return 当前容器位移
    118      */
    119     public int getSlide() {
    120         if (android.os.Build.VERSION.SDK_INT > 10) {
    121             return (int) contentView.getX();
    122         } else {
    123             return ((android.widget.FrameLayout.LayoutParams) contentView
    124                     .getLayoutParams()).leftMargin;
    125         }
    126     }
    127 
    128     /**
    129      * 滑动框架
    130      * 
    131      * @author HalfmanG2
    132      * @version 1.0.0
    133      * @since JDK7 SDK19
    134      */
    135     public class SlideFrame extends FrameLayout {
    136         /** 默认滑动阀值 */
    137         private final static int DEFAULT_SLIDE_DUMPING = 8;
    138         /** 默认状态改变阀值 */
    139         private final static int DEFAULT_DO_DUMPING = 100;
    140         /** 滑动起始位置与当前位置 */
    141         private int startX, currentX, startY, currentY;
    142         /** 是否拦截事件,是否已经完成滑动检查 */
    143         private boolean doNotIntercept, hasChecked;
    144         /** 滑动阀值 */
    145         private int slideDumping;
    146         /** 操作阀值 */
    147         private int doDumping;
    148         /** 滑屏动画 */
    149         protected SlideAnimation slideAnimation;
    150 
    151         @Override
    152         public boolean onInterceptTouchEvent(MotionEvent ev) {
    153             super.onInterceptTouchEvent(ev);
    154             // 若当前处在侧滑状态中,则拦截信号
    155             if ((!doNotIntercept) && hasChecked) {
    156                 return true;
    157             }
    158             // 否则使用默认
    159             return false;
    160         }
    161 
    162         @Override
    163         public boolean dispatchTouchEvent(MotionEvent ev) {
    164             if (ev.getAction() == MotionEvent.ACTION_DOWN) {
    165                 // 获得起始滑动坐标
    166                 startX = (int) ev.getX();
    167                 startY = (int) ev.getY();
    168                 // 初始化状态
    169                 doNotIntercept = false;
    170                 hasChecked = false;
    171             } else if (!doNotIntercept) {
    172                 // 获得当前滑动坐标
    173                 currentX = (int) ev.getX();
    174                 currentY = (int) ev.getY();
    175                 // 根据滑动类型区分
    176                 switch (ev.getAction()) {
    177                 case MotionEvent.ACTION_MOVE: // 移动状态
    178                     if (hasChecked) {
    179                         doSlide();
    180                     } else {
    181                         doCheck();
    182                     }
    183                     break;
    184                 case MotionEvent.ACTION_CANCEL: // 取消状态
    185                 case MotionEvent.ACTION_UP: // 抬起状态
    186                     // 初始化状态
    187                     doNotIntercept = false;
    188                     hasChecked = false;
    189                     if (Math.abs(currentX - startX) > doDumping) {
    190                         if (currentX > startX) {
    191                             // 右滑
    192                             slideAnimation = new SlideAnimation(getSlide(),
    193                                     contentView.getWidth(), 0);
    194                             slideAnimation
    195                                     .setAnimationListener(new AnimationListener() {
    196                                         @Override
    197                                         public void onAnimationStart(
    198                                                 Animation animation) {
    199                                         }
    200 
    201                                         @Override
    202                                         public void onAnimationRepeat(
    203                                                 Animation animation) {
    204                                         }
    205 
    206                                         @Override
    207                                         public void onAnimationEnd(
    208                                                 Animation animation) {
    209                                             onSlideFinish();
    210                                         }
    211                                     });
    212                             startAnimation(slideAnimation);
    213                         }
    214                     } else {
    215                         // 返回0位置
    216                         slideAnimation = new SlideAnimation(getSlide(), 0, 0);
    217                         startAnimation(slideAnimation);
    218                     }
    219                     break;
    220                 default:
    221                     break;
    222                 }
    223             }
    224             return super.dispatchTouchEvent(ev);
    225         }
    226 
    227         /**
    228          * 检查是否超过滑动阀值开启滑动状态
    229          */
    230         private void doCheck() {
    231             if (Math.abs(startY - currentY) > slideDumping) {
    232                 hasChecked = true;
    233                 doNotIntercept = true;
    234                 slideTo(0);
    235             } else if (currentX - startX > slideDumping) {
    236                 hasChecked = true;
    237                 doNotIntercept = false;
    238             }
    239         }
    240 
    241         /**
    242          * 进行滑动
    243          */
    244         private void doSlide() {
    245             if (currentX > startX) {
    246                 slideTo(currentX - startX);
    247             } else {
    248                 slideTo(0);
    249             }
    250         }
    251 
    252         /**
    253          * 设置滑动阀值
    254          * 
    255          * @param dpValue
    256          */
    257         public void setSlideDumping(int dpValue) {
    258             slideDumping = dip2px(dpValue);
    259         }
    260 
    261         /**
    262          * 设置状态改变阀值
    263          * 
    264          * @param dpValue
    265          */
    266         public void setDoDumping(int dpValue) {
    267             doDumping = dip2px(dpValue);
    268         }
    269 
    270         /**
    271          * 二级构造方法
    272          */
    273         private void initilize() {
    274             setSlideDumping(DEFAULT_SLIDE_DUMPING);
    275             setDoDumping(DEFAULT_DO_DUMPING);
    276             doNotIntercept = false;
    277             hasChecked = false;
    278             setClickable(true);
    279             setFocusable(true);
    280             setFocusableInTouchMode(true);
    281         }
    282 
    283         /**
    284          * 构造方法
    285          * 
    286          * @param context
    287          * @param attrs
    288          * @param defStyle
    289          */
    290         public SlideFrame(Context context, AttributeSet attrs, int defStyle) {
    291             super(context, attrs, defStyle);
    292             initilize();
    293         }
    294 
    295         /**
    296          * 构造方法
    297          * 
    298          * @param context
    299          * @param attrs
    300          */
    301         public SlideFrame(Context context, AttributeSet attrs) {
    302             super(context, attrs);
    303             initilize();
    304         }
    305 
    306         /**
    307          * 构造方法
    308          * 
    309          * @param context
    310          */
    311         public SlideFrame(Context context) {
    312             super(context);
    313             initilize();
    314         }
    315 
    316         /**
    317          * 讲dip值转换为px值,像素密度距离转像素距离
    318          * 
    319          * @param dipValue dp值
    320          * @return px值
    321          */
    322         private int dip2px(float dipValue) {
    323             // 获得像素密度
    324             final float scale = getContext().getResources().getDisplayMetrics().density;
    325             // 四舍五入dp值乘像素密度
    326             return (int) (dipValue * scale + 0.5f);
    327         }
    328     }
    329 
    330     /**
    331      * 滑动动画类
    332      * 
    333      * @author HalfmanG2
    334      */
    335     public class SlideAnimation extends Animation {
    336         /** 起始位置,目标位置 */
    337         private float from, to;
    338         /**
    339          * 构造方法
    340          * @param from 起始位置
    341          * @param to 目标位置
    342          * @param startOffset 起始延迟
    343          */
    344         public SlideAnimation(int from, int to, int startOffset) {
    345             this.from = from;
    346             this.to = to;
    347             setFillEnabled(false);
    348             setDuration(200);
    349             setRepeatCount(0);
    350             setStartOffset(startOffset);
    351             setInterpolator(new DecelerateInterpolator());
    352         }
    353         @Override
    354         protected void applyTransformation(float interpolatedTime,
    355                 Transformation t) {
    356             float current = from + (to - from) * interpolatedTime;
    357             slideTo((int) current);
    358             super.applyTransformation(interpolatedTime, t);
    359         }
    360     }
    361 }

     

    四、使用详细步骤

    1、建立一个Android工程,项目最小api level必须在api level 11及以上

    2、把本类考入任意代码包下

    3、项目中想要实现Activity滑动退出效果的Activity继承本类

    4、如果要在滑动过程中显示上一个Activity的话,将Activity的背景设置为透明,方法建议通过设置Activity的Style或者说theme来实现

    5、如果滑动过程中需要加入一些特殊效果,可以复写slideTo(int)方法,记得别把super.slideTo(int)给漏了,否则滑动失效

    6、如果滑动结束后不希望立即返回上一页,可以复写onSlideFinish()方法

    PS、

    很简单的实现方法,但离我觉得完美还太远,所以如果有更好的实现方法希望可以一起交流下:

    联系方式QQ:811868948,备注加上Android交流,有好的想法一定要联系我,谢谢!

    联系方式E-Mail:halfmanhuang@gmail.com

  • 相关阅读:
    text/css什麼意思
    w3cschool的在線編輯器的特點
    html
    notepad编写html
    html学习心得
    html 链接
    資料鏈接
    微信小程序的界面下拉刷新
    C# PictureBox控件畫圖
    CPK公式
  • 原文地址:https://www.cnblogs.com/halfmanhuang/p/3834952.html
Copyright © 2020-2023  润新知