WorkSpace

明天将Workspace和DragLayer联合起来进行阐述。

废话就不多说,Workspace的大体功能前面已有简介,现在具体说其中的技术点。

技术点 如何通过xml文件来构造自己定义的View组件?

在通过xml文件构造view组件的时候,往往都要使用到AttributeSet和defStyle这个两个参数。defStyleAttr是一个reference, 它指向当前Theme中的一个style, style其实就是各种属性的集合。

代码
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Workspace, defStyle, 0);
// style就是各种属性的数组集合
mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 2);
// 通过styleable name + _attr name得到属性值,如果没有定义则默认为2
a.recycle();

技术点 如何保证自定义的ViewGroup如Workspace按照我们想的那样横向显示3个(5个)屏幕?

要知道这一点我们必须先知道一个View是如何显示出来的,在显示View之前,我们应该要为其指定尺寸和分布,这些是在onMeasure和onLayout中调用完成,废话不多说,上代码:

代码
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
       
        final int width = MeasureSpec.getSize(widthMeasureSpec);
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        // 仅当ViewGroup为fill_parent才处于EXACTLY模式
        if (widthMode != MeasureSpec.EXACTLY) {
            throw new IllegalStateException("Workspace can only be EXACTLY mode.");        }
       
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (heightMode != MeasureSpec.EXACTLY) {
            throw new IllegalStateException("Workspace can only be EXACTLY mode.");        }
            final int count = getChildCount();
             for (int i = 0; i < count; i++) {
                  getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);       // 为所有子View设定标准尺寸
              }
       
        if (mFirstLayout) {
            scrollTo(mCurrentScreen * width, 0);      
           updateWallpaperOffset(width * (getChildCount() - 1));
            mFirstLayout = false;
        }     //滚动到第三屏设为默认主屏。
    }

在尺寸设定完毕后,我们需要对ViewGroup中所有子View的位置进行设定,以满足他们的布局。

代码
@Override
  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        int childLeft = 0;
       
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != View.GONE) {
                final int childWidth = child.getMeasuredWidth();
                child.layout(childLeft, 0, childLeft + childWidth,                                

                  child.getMeasuredHeight());          //很明显是吧? 0 - 320 | 320 - 640 | 640 - 960 ...(假设屏幕宽320)
                childLeft += childWidth;
            }
        }
  }

布局设定OK。这样我们就了解到是如何为Workspace设定了屏幕数,并为他们做好布局、定好尺寸。(实际上在每次滑动结束后都会调用他们)下面我们需要知道的是具体的切屏操作是如何完成,在切屏时,又做了什么?

技术点 各屏幕被遮挡后如何重新获取焦点?

我们知道,View一般通过requestfocus获取焦点,当然前提是他们“能够”获得。通过复写addFocusables函数来为当前ViewGroup中的所有子View添加焦点获取能力 - -。然后当屏幕被遮盖又恢复后,ViewGroup会首先收到requestfocus消息,并在下列代码中作出相应处理:

代码
@Override
    protected boolean onRequestFocusInDescendants(int direction,                  

             Rect previouslyFocusedRect) {            int focusableScreen;
        if (mNextScreen != INVALID_SCREEN) {
            focusableScreen = mNextScreen;
        } else {
            focusableScreen = mCurrentScreen;
        }
        getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect);
           
        return false;
    }

应该是很清晰了,函数名称起的很好,即当前ViewGroup树形结构中的某个View获取焦点。条件判断主要是用于检测是否发生过切屏。

技术点 在发生切屏时,是如何进行绘制和切换屏幕的工作?

  有细心的朋友肯定已经注意到,在onMeasue函数调用中有一个scrollTo函数,用于将屏幕移动到指定坐标位置。程序初次启动时,显示默认第三屏便是在这里做了滚动操作,它会首先获得焦点。由ViewGroup调用dispatchDraw()操作,进行绘制和draw动作的传递。这里补充一个知识,除非ViewGroup本身有背景,否则调用onDraw是无效的。

  在dispatchDraw操作中,判断是否处于滑动状态,通过drawChild()来完成对当前主屏和滑动状态的下一主屏进行绘制。否则,当切屏完成后,屏幕上不会有Widget或Shortcut,但是点击他们的位置却能启动相应程序。

  滑动屏幕时有两个个非常重要的函数即scrollTo、scrollBy,我看了很多地方都没有对他们解释的很清楚。scrollTo,  scrollBy其实都是在对View的内容进行移动。在Touch  Move中发生scrollBy后并不会去调用computeScroll(),反而在move结束,up事件中调用startSrcoll,开始不断调整mScrollX,  mSrcollY,将当前的基准点重新置回标准的:0,0 320,0 640,0...

简单的说下整体的绘制操作:接受onTouchEvent事件,当在移动中计算移动的距离,调整基准点并不停的绘制屏幕和移动壁纸,当touch结束后,通过startScroll将滑动完成,并将基准点置为标准点(在computeScroll中),更新重绘屏幕。

这样,Workspace的主要工作已基本算是完成,还有一些它担任委托的工作就没有介绍了,应该不会很复杂,看看就好。

   
   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值