第一部分:Launcher APP 组成分析(二)

上一章节介绍到Launcher的四大组件组成、Launcher layout xml的组成及使用到的数据库表 favorites | screens


本章简介:

  1. DragLayer组成分析

  2. Workspace及ScreenView组成分析

  3. CellLayout及CellScreen组成分析

  4. Shortcut组成分析

  5. Hotseats组成分析

  6. DeleteZone组成分析



第一部分:Launcher APP组成分析(二)


从Launcher.xml 中看到Launcher的布局由最外层的DragLayer包裹着,中间由Workspace、Hotseats及DeleteZone几个主要组成部件构成。先抛开桌面上的数据如何读取,如果读者希望在自己写代码的过程中有一个直观的显示感受,可以不妨放写图片或者文字来临时填放用于调试运行。


图(1)


1.DragLayer 组成分析:

    从组成来讲,DragLayer最重要的部分是用于显示壁纸背景,关于拖动的部分,等到分析到触摸再拿来分析。

    壁纸,MIUI系统中将壁纸显示的方式使用SharedPreferences的方式存储。


        /**
	 *	功能: 桌面背景显示, 从配置表中读取壁纸的显示方式
	 *	调用: 使用于Launcher.java
	 */
	public void updateWallpaper() {
		String wallpaperScrollType = PreferenceManager.getDefaultSharedPreferences(mContext).
				getString("pref_key_wallpaper_scroll_type", "byTheme");
        
		if (wallpaperScrollType.equals("byTheme")){
        	wallpaperScrollType = getResources().getString(R.string.wallpaper_scrolling);
        }

        mWpScrolling = false;
        if (wallpaperScrollType.equals("left")) {
        	mWpOffsetX = 0F;
        } else if (wallpaperScrollType.equals("center")) {
        	mWpOffsetX = 0.5F;
        } else if (wallpaperScrollType.equals("right")) {
        	mWpOffsetX = 1F;
        } else {
        	mWpScrolling = true;            
        }        
    
		mLauncher.getWindow().setFormat(PixelFormat.TRANSPARENT);
		mWallpaper = null;
		updateWallpaperOffset();
	}
	
	/**
	 *	功能: 壁纸显示起点
	 */
	public void updateWallpaperOffset() {
		if (mWallpaper == null) {
			mWallpaperManager.setWallpaperOffsetSteps(mWpStepX, mWpStepY);
			if (getWindowToken() == null) {
				removeCallbacks(OffsetUpdater);
				postDelayed(OffsetUpdater, 50L);
			}else {
				try {
					WindowManagerGlobal.getWindowSession(mContext.getMainLooper()).
						setWallpaperPosition(getWindowToken(), mWpOffsetX, mWpOffsetY, mWpStepX, mWpStepY);	
				} catch (RemoteException e) { }
			}
		}else {
			int offsetX = (int)((float)(mWpWidth - mScreenSize.x) * mWpOffsetX);
			if (mOldOffsetX != offsetX) {
				mOffsetChanged = true;
			}
			mOldOffsetX = offsetX;
		}
	}
	
	public void updateWallpaperOffset(float xStep, float yStep, float xOffset, float yOffset) {
		if (mWpScrolling && mWpOffsetX != xOffset) {
			mWpStepX = xStep;
			mWpStepY = yStep;
			mWpOffsetX = xOffset;
			mWpOffsetY = yOffset;
			updateWallpaperOffset();
		}
	}
	
	public void updateWallpaperOffsetAnimate(final float xStep, final float yStep, 
			final float xOffset, final float yOffset) {
		final float xStepDelta = xStep - mWpStepX;
		final float yStepDelta = yStep - mWpStepY;
		final float mWpOffsetXDelta = xOffset - mWpOffsetX;
                final float mWpOffsetYDelta = yOffset - mWpOffsetY;
        
                ValueAnimator valueAnimator = ValueAnimator.ofFloat(1.0f, 0.0f);
                valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
			@Override
			public void onAnimationUpdate(ValueAnimator animation) {
				float f = ((Float)animation.getAnimatedValue()).floatValue();
				
				updateWallpaperOffset(xStep - f * xStepDelta, yStep - f * yStepDelta, 
						xOffset - f * mWpOffsetXDelta, yOffset - f * mWpOffsetYDelta);
			}
		});
        
                valueAnimator.start();
	}



可以看到,Wallpaper在DragLayer中分三种类型“left”, "right", "center" 来显示,根据壁纸的大小来计算X,Y的偏移量,updateWallpaperOffsetAnimate主要是用于拖动时动画显示滑动到指定位置。


2.Workspace及ScreenView组成分析

    从代码中可以看到Workspace继承与DragableScreenView,DragableScreenView再继承与ScreenView,由于DragableScreenView主要是用于处理触摸动作,这里暂时不做分析,直接看ScreenView。ScreenView是一个ViewGroup,Workspace如果构成的ViewGroup呢?这里必须要涉及到CellLayout及CellScreen,因为这两个组件才是组成Workspace的Viewgroup的元素。实例图如下:


图(二)

CellScreen.xml的layout:

<cn.minking.launcher.CellScreen android:animationCache="false" 
    android:layout_width="fill_parent" android:layout_height="fill_parent"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <FrameLayout android:id="@id/background_container" 
        android:visibility="invisible" 
        android:animationCache="false" 
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent">
        
        ......
        ......
        
    </FrameLayout>
    <cn.minking.launcher.CellLayout android:id="@id/cell_layout" 
        android:paddingBottom="@dimen/workspace_cell_padding_bottom" 
        android:animationCache="false" 
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent" />
</cn.minking.launcher.CellScreen>


在Launcher的Model将数据读取给CellScreen,然后将每个CellScreen使用addView的方式添加至ViewGroup, 使用 onLayout显示出来,至于详细的步骤会在后续详细的分析。


3. CellScreen | CellLayout组成分析

    CellLayout被分割成Horizontal*Vetical个单元格,下图中示为4x4的CellLayout,此处可以根据屏幕的分辨率不同设置不同的大小,比如MIUI现在使用的则是4x5的布局;


图(三)

    // 横向及纵向的单元格个数
    mHCells = ResConfig.getCellCountX();
    mVCells = ResConfig.getCellCountY();


    每个单元格的大小及边界距离:

        Resources resources = context.getResources();
        mCellWidth = resources.getDimensionPixelSize(R.dimen.workspace_cell_width);
        mCellHeight = resources.getDimensionPixelSize(R.dimen.workspace_cell_height);
        mWidgetCellPaddingTop = resources.getDimensionPixelSize(R.dimen.workspace_widget_padding_top);
        mWidgetCellPaddingBottom = resources.getDimensionPixelSize(R.dimen.workspace_widget_padding_bottom);
        mPaddingTop = resources.getDimensionPixelSize(R.dimen.workspace_padding_top);
        mPaddingLeft = resources.getDimensionPixelSize(R.dimen.workspace_padding_side);
        mPaddingRight = mPaddingLeft;


    一个View控件可以占有单个单元格,比如APP图标或者Shortcut快捷方式,也可以一个View控件占有多个单元格,比如2x2的时钟,或者4x4的时钟。

        // 标识单元格视图的二维数组
        int ai[] = new int[]{mHCells, mVCells};
        int aiBak[] = new int[]{mHCells, mVCells};
        mOccupiedCell = (View[][])Array.newInstance(View.class, ai);
        mOccupiedCellBak = (View[][])Array.newInstance(View.class, aiBak);


    此部分和原生的Android的launcher是类似的,原生的launcher使用的pagedview,同样是继承与ViewGroup,同样也使用了CellLayout,区别是MIUI多了CellScreen,为何这么做?由于MIUI是单界面操作,所以在桌面需要更多的操作,比如进入Widget编辑模式。


4. ShortCut组成分析:

    这部分本应该是需要和Widget一起分析,但是MIUI中增加了Gadget,涉及到主题资源的引入,所以在此我们先不分析Widget。

    Launcher.java中创建Shortcut布局的接口,看到调用的是application layout

    Launcher.java
    /**
     * 功能: 以Layout application为样式创建快捷方式的布局
     * @param viewgroup
     * @param shortcutinfo
     * @return
     */
    private ShortcutIcon createShortcutIcon(ViewGroup viewgroup, ShortcutInfo shortcutinfo){
        return ShortcutIcon.fromXml(R.layout.application, this, viewgroup, shortcutinfo);
    }
    
    ShortcutIcon.java
    /**
	 * 功能: 从XML中的得到快捷图标的布局 
	 * @param layout
	 * @param launcher
	 * @param viewgroup
	 * @param shortcutinfo
	 * @return
	 */
	static ShortcutIcon fromXml(int layout, Launcher launcher, 
			ViewGroup viewgroup, ShortcutInfo shortcutinfo){
		ShortcutIcon shortcutIcon = (ShortcutIcon)LayoutInflater.from(launcher).inflate(layout, viewgroup, false);
		shortcutIcon.updateInfo(launcher, shortcutinfo);
		    
		return shortcutIcon;
	}


application.xml

<cn.minking.launcher.ShortcutIcon 
    android:background="@drawable/shortcut_selector" 
    android:focusable="true" 
    android:animationCache="false" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent"
  	xmlns:android="http://schemas.android.com/apk/res/android">
    
    <!-- 图标阴影 -->  	
    <include layout="@layout/icon_shadow" />
    
    <!-- 创建文件夹时显示的背景 -->
    <ImageView 
        android:layout_gravity="top|center" 
        android:id="@id/icon_folder_creation_bg" 
        android:visibility="invisible" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_marginLeft="@dimen/icon_side_margin" 
        android:layout_marginRight="@dimen/icon_side_margin" 
        android:src="@drawable/icon_folder" 
        android:scaleType="centerInside" />
    
    <!-- 图标 -->    
    <include layout="@layout/icon_icon" />
    <!-- 标题 -->
    <include layout="@layout/icon_title" />
    <!-- 消息个数 -->
    <include layout="@layout/icon_message" />
</cn.minking.launcher.ShortcutIcon>


5. Hotseats组成分析

    Hotseats采用的是线性布局LinearLayout, 根据MAX SEAT的个数,依次布局


图(四)

    Hotseats.java
    @Override
    protected void onFinishInflate() {
	super.onFinishInflate();
	for (int i = 0; i < MAX_SEATS; i++) {
		LayoutInflater.from(mContext).inflate(R.layout.hotseat_button, this, true);
	}
    }
    HotseatButton.java
    public void bind(ItemIcon itemicon, DragController dragcontroller){
    	mIcon = itemicon;
    	
    	// 构建一个FrameLayout, 将HOT Seat中的各个ItemIcon加人到Layout中
    	addView(itemicon);
    	if (itemicon instanceof DropTarget) {
	    dragcontroller.addDropTarget((DropTarget)itemicon);
	}
    }


6. DeleteZone组成分析

    DeleteZone默认是隐藏的,等有Item被拖动时才会出现在屏幕的顶部。布局很简单,就以launcher.xml中布局显示。此布局绑定了几个动画及拖动的操作,等后续分析拖动时再详细讲解。


@Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        // 垃圾箱图标
        mTrashIcon = (ImageView)findViewById(R.id.trash);
        // 提示信息
        mEditingTips = (TextView)findViewById(R.id.editing_tips);
        mEditingTips.setDrawingCacheEnabled(true);
        
        // 显示及消失的动画, 淡入淡出及伸缩的效果
        mFadeIn = AnimationUtils.loadAnimation(getContext(), R.anim.fade_in);
        mFadeOut = AnimationUtils.loadAnimation(getContext(), R.anim.fade_out);
        mShrinkToTop = AnimationUtils.loadAnimation(getContext(), R.anim.shrink_to_top);
        mShrinkToTop.setAnimationListener(this);
        mStretchFromTop = AnimationUtils.loadAnimation(getContext(), R.anim.stretch_from_top);
        mTransition = (TransitionDrawable)mTrashIcon.getBackground();
    }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值