Android 仿当乐游戏详情页面(三)


在上两篇文章中,我们已经实现了基本的界面的布局和移动效果,但是mImgShotViewmContentView却不能响应事件,而事件的响应就需要我们手动进行事件分发!</br>
android 仿当乐游戏详情页面(一)</br>
android 仿当乐游戏详情页面(二)

事件分发分析

在前面第二篇中,我们是通过手势来实现布局的移动,为了让系统能响应手势,在onTouchEvent(MotionEvent event)方法里面,调用了mDetector.onTouchEvent(event);将系统的焦点传递给了手势,因此,当滑动mImgShotView这个ViewPager时,会出现焦点丢失,截图不能进行切换的问题。</br>
因此,解决这个问题最好的方法就是重写dispatchTouchEvent(MotionEvent ev)方法,对事件分发进行处理。</br>
在上一篇文章中,已经介绍了,在仿当乐的游戏详情页面中,mContentView有三种不同的状态:

  1. 顶部状态时,ToolBarmContentView将获取到焦点。
  2. 中间状态时,ToolBarmImgShotView将获取到焦点。
  3. 底部状态时,上一篇文章中已经介绍了,这个状态,mImgShotView的参数将发生改变,因此,事件焦点的分发便需要进行改变。

事件分发的实现

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (mRawY <= mTopL) {
        mCurrentState = STATE_TOP;
    } else if (mTopL < mRawY && mRawY <= mCenterL + (mBarH >> 1)) {
        mCurrentState = STATE_CENTER;
    } else if (mCenterL + (mBarH >> 1) <= mRawY && mRawY < mBottomL + mBarH) {
        mCurrentState = STATE_BOTTOM;
    } else {
        mCurrentState = STATE_OTHER;
    }
    boolean isTop = mRawY == mTopL;
    // 处理横向滑动的事件
    if (Math.abs(ev.getX() - mOldX) >= 0 && Math.abs(ev.getY() - mOldY) < 300 && isTop) {
        mOldX = ev.getX();
        return super.dispatchTouchEvent(ev);
    }

    float t = Math.abs(ev.getY() - mOldY);
    //处于顶部时的事件过滤区域
    if (isTop && (ev.getY() < mTopL || t < 10)) {
        return super.dispatchTouchEvent(ev);
    }

    //处于中间时的事件过滤区域
   if (mCurrentState == STATE_CENTER && ev.getY() < (mCenterL + mBarH) && mRawY >= mCenterL) {
        return super.dispatchTouchEvent(ev);
    }

    //处于底部时的事件过滤区域
    if (mCurrentState == STATE_BOTTOM && ev.getY() < (mBottomL + mBarH) && mRawY >= mBottomL) {
        return super.dispatchTouchEvent(ev);
    }

    boolean isUp = ev.getY() - mOldY < 0;

    if (isTop && mCurrentState == STATE_TOP) {
        mOldY = (int) ev.getY();
        if (isScrollTop && !isUp) {
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                return super.dispatchTouchEvent(ev);
            }
            return onTouchEvent(ev);
        } else {
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                return super.dispatchTouchEvent(ev);
            }
            return super.dispatchTouchEvent(ev);
        }
    }

    return onTouchEvent(ev);
}

以上便是事件分发的全部代码</br>
我们从最简单的地方开始分析代码,在上一篇文章中,定义了几个变量mTopL、mCenterL、mBottomL用来确定布局移动的基准位置,mRawY用来确定布局的当前的Y坐标。</br>
因此,便可以使用这几个变量来确认当前布局所处的状态:

1. mRawY <= mTopL                                                 ==>   布局处于顶部状态
2. mTopL < mRawY && mRawY <= mCenterL + (mBarH >> 1)              ==>   布局处于中间状态。
3. mCenterL + (mBarH >> 1) <= mRawY && mRawY < mBottomL + mBarH   ==>   布局处于底部状态

在2、3中,mBarH表示的是ToolBar的高度常量,+ mBarH,表示往下移动的偏移常量。</br>

处理普通的事件过滤

继续看代码,

float t = Math.abs(ev.getY() - mOldY);
//处于顶部时的事件过滤区域
if (isTop && (ev.getY() < mTopL || t < 10)) {
    return super.dispatchTouchEvent(ev);
}

//处于中间时的事件过滤区域
if (mCurrentState == STATE_CENTER && ev.getY() < (mCenterL + mBarH) && mRawY >= mCenterL) {
    return super.dispatchTouchEvent(ev);
}

//处于底部时的事件过滤区域
if (mCurrentState == STATE_BOTTOM && ev.getY() < (mBottomL + mBarH) && mRawY >= mBottomL) {
    return super.dispatchTouchEvent(ev);
}
  1. 第一个if语句,是用来处理顶部状态的事件过滤的,但是由于布局处于顶部状态时,mContentView需要获取事件,而mContentView是一个ViewPager,如果ViewPager加载的Fragment有滑动控件,将是一个很复杂的分发过程,而Fragment滑动控件的处理我们是必须考虑的。</br>
    因此,第一个if语句里面,只处理mContentView的处于顶部状态时的点击事件,只要t小于10,就判断当前的事件为点击事件。而为了让焦点从手势交还给系统,只需要return super.dispatchTouchEvent(ev);便能将事件焦点拦截交还给系统。
  2. 第二个if语句就比较简单了,当处于中间状态时,只要事件的Y坐标小于mCenterL + mBarH坐标时,统统将事件焦点交还给系统, mRawY >= mCenterL,有这个判断时,系统才能确定是中间状态时的事件过滤,不会导致晚上移动的情况下,莫名奇妙事件就交给了系统。
  3. 第三个if语句和第二个if语句差不多,mRawY >= mBottomL这句代码同上所示,只有这个判断时,才能确认是底部事件的过滤,如果没有这句话,mContentView在中间状态时,就会处理,底部事件的过滤,将导致,mContentView处于中间状态时,将失去对手势的控制。

接下来便是处理mContentView的滑动控件的事件处理了!</br>
在当乐的游戏详情界面中,mContentView处于顶部时,里面的ListView或者ScrollView,只有滑动到最顶部时,再外下滑动,mContentView才能将焦点交还给系统。

处理Fragment 中滑动事件过滤

boolean isUp = ev.getY() - mOldY < 0;

if (isTop && mCurrentState == STATE_TOP) {
    mOldY = (int) ev.getY();
    if (isScrollTop && !isUp) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            return super.dispatchTouchEvent(ev);
        }
        return onTouchEvent(ev);
    } else {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            return super.dispatchTouchEvent(ev);
        }
        return super.dispatchTouchEvent(ev);
    }
}

如上的代码所示,如果,当mContentView中的Fragment的滑动控件滑动到顶部,并且mContentView处于顶部,并且手势向上则将事件焦点交给手势,否则,交还给系统。</br>
这里需要注意以下两行代码:

if (ev.getAction() == MotionEvent.ACTION_DOWN) {
    return super.dispatchTouchEvent(ev);
}

这两行代码是整个滑动事件分发的灵魂!!!,这两句代码是告诉系统,当焦点进行交换时,告诉当前掌控焦点的服务(系统、手势)交出焦点,重新进行分配!!如果没有这两句代码,焦点的切换将很有可能失败(就为了写出两行代码,工期延期了整整一个星期,说多了都是泪!!!!!!!)。

处理横向事件过滤

if (Math.abs(ev.getX() - mOldX) >= 0 && Math.abs(ev.getY() - mOldY) < 300 && isTop) {
     mOldX = ev.getX();
     return super.dispatchTouchEvent(ev);
 }

以上代码是实现mContentViewfragment的切换的,这代码很简单,只要是横向手势,并且X的偏差小于300就认为其是横向事件。

最终效果

最终效果

写在最后

来来回回一个多月,这个页面的blog算是写完了(整个功能实现也就7、8天,写着blog写了快两个月了,懒癌晚期伤不起)。现在的效果已经和当乐的差不多了,但是还是有点差别,比如,我这没有底部栏,比如,我这中间状态不能对mContentView进行切换,其实这些都很容易实现(其实我是懒癌晚期,不想写了..)</br>
最后还是说个思路吧:

  1. 底部导航栏那个,在布局里面写FrameLayout,然后编写自定义View,在主界面的代码里面,给mContentView设置addOnPageChangeListener事件监听,在滑动的过程中,FrameLayout动态添加你的自定义的导航栏View。(我在公司的APP里面采用的是这个思路)
  2. mContentView中间状态的切换,这个只需要在下面的语句中添加横向状态的添加横向手势移动的判断方法便可以了,需要注意下事件分发的范围
if (mCurrentState == STATE_CENTER && ev.getY() < (mCenterL + mBarH) && mRawY >= mCenterL) {
    return super.dispatchTouchEvent(ev);
}

源代码

其实上面说的全部都是废话,真正重要的还是源代码!!</br>
点击我获取源代码,最后跪求star和issues

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,756评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,059评论 4 62
  • 在上一篇文章里面,基本上算是实现了该效果的布局,有了布局,接下来就要对布局进行移动处理。 android 仿当乐游...
    dorn19978阅读 1,032评论 0 11
  • 微信每一次新功能的推出都令用户的神经为之振奋,红包照片这一功能更是让我对微信产品经理的膜拜之情上升到一个前所未...
    析思阅读 622评论 1 1
  • 21天践行打卡 Day13 【静心】每天有一段时间,关注自己。 【动身】去小学听课。 【成长】不断学习。 【助人】...
    石头缝里的小嫩芽变大树阅读 112评论 0 1