Launcher知识点整理

一、增加墙纸

图片放入/packages/apps/Launcher2/res/drawable-mdpi

/packages/apps/Launcher2/res/values-mdpi/wallpapers.xml 

<resources>
    <string-array name="wallpapers" translatable="false">
        <item>wallpaper_lake</item>
        <item>wallpaper_sunset</item>
        <item>wallpaper_beach</item>
        <item>wallpaper_snow_leopard</item>
        <item>wallpaper_path</item>
        <item>wallpaper_sunrise</item>
        <item>wallpaper_mountain</item>
        <item>wallpaper_road</item>
        <item>wallpaper_jellyfish</item>
        <item>wallpaper_zanzibar</item>
        <item>wallpaper_blue</item>
        <item>wallpaper_grey</item>
        <item>wallpaper_green</item>
        <item>wallpaper_pink</item>
    </string-array>
</resources>

设置默认墙纸

frameworks\base\core\res\res\drawable中default_wallpaper.jpg这个是默认的墙纸

二、源代码中设置wallpaper

在launcher.java中

    public boolean onCreateOptionsMenu(Menu menu) {
        if (isWorkspaceLocked()) {
            return false;
        }
        super.onCreateOptionsMenu(menu);

        menu.add(MENU_GROUP_ADD, MENU_ADD, 0, R.string.menu_add)
                .setIcon(android.R.drawable.ic_menu_add)
                .setAlphabeticShortcut('A');
        menu.add(0, MENU_MANAGE_APPS, 0, R.string.menu_manage_apps)
                .setIcon(android.R.drawable.ic_menu_manage)
                .setAlphabeticShortcut('M');
        menu.add(MENU_GROUP_WALLPAPER, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper)
                 .setIcon(android.R.drawable.ic_menu_gallery)
                 .setAlphabeticShortcut('W');
     … …
}

点击wallpaper后会调用

 public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case MENU_ADD:
                addItems();
                return true;
            case MENU_MANAGE_APPS:
                manageApps();
                return true;
            case MENU_WALLPAPER_SETTINGS:
                startWallpaper();
                return true;
            case MENU_SEARCH:
                onSearchRequested();
                return true;
            case MENU_NOTIFICATIONS:
                showNotifications();
                return true;
        }

        return super.onOptionsItemSelected(item);
    }

将进入

    private void startWallpaper() {
        closeAllApps(true);
        final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
        Intent chooser = Intent.createChooser(pickWallpaper,
                getText(R.string.chooser_wallpaper));
        startActivityForResult(chooser, REQUEST_PICK_WALLPAPER);
    }


三、launcher中launcher的搜索框和ProtipWidget(widget修改)

 Launcher2/res/xml/default_workspace.xml

    <!--  Middle screen [2] <search>为添加google搜索框;-->
    <search
        launcher:screen="2"
        launcher:x="0"
        launcher:y="0" />
<!--  <appwidget>为添加相应的widget; -->
    <appwidget
        launcher:packageName="com.android.protips"
        launcher:className="com.android.protips.ProtipWidget"
        launcher:screen="2"
        launcher:x="0"
        launcher:y="1"
        launcher:spanX="4"
        launcher:spanY="1" />

此xml解析在Launcherprovider.java文件loadFavorites方法中

 

 

四、launcher屏数和默认屏

在launcher.java的Launcher类中有

    static final int SCREEN_COUNT = 5;  //屏数

    static final int DEFAULT_SCREEN = 2;  //默认显示的第几屏(从0屏开始)。

 

修改屏数还需要修改

Launcher2/res/layout-port/Launcher.xml

    <!-- The workspace contains 3 screens of cells -->
    <com.android.launcher2.Workspace
        android:id="@+id/workspace"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="horizontal"
        android:fadeScrollbars="true"
        launcher:defaultScreen="2">

        <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
        <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
        <include android:id="@+id/cell3" layout="@layout/workspace_screen" />
        <include android:id="@+id/cell4" layout="@layout/workspace_screen" />
        <include android:id="@+id/cell5" layout="@layout/workspace_screen" />

    </com.android.launcher2.Workspace>

修改默认屏同时需要修改workspace.java中

    public Workspace(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        … …
        mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1);
        a.recycle();

        setHapticFeedbackEnabled(false);
        initWorkspace();
}

五、页面标记实现原理

Launcher2/res/drawable/ home_arrows_left.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:drawable="@drawable/all_apps_button_pressed" />
    <item android:state_focused="true" android:state_window_focused="true" android:drawable="@drawable/all_apps_button_focused" />
    <item android:state_focused="true" android:state_window_focused="false" android:drawable="@drawable/all_apps_button_normal" />
    <item android:drawable="@drawable/all_apps_button_normal" />
</selector>

Launcher2/res/layout-port/Launcher.xml

    <ImageView
        android:id="@+id/previous_screen"
        android:layout_width="93dip"
        android:layout_height="@dimen/button_bar_height"
        android:layout_gravity="bottom|left"
        android:layout_marginLeft="6dip"

        android:scaleType="center"
        android:src="@drawable/home_arrows_left"
        
        android:onClick="previousScreen"    <!—点击事件处理-->

        android:focusable="true"
        android:clickable="true" />

在launcher.java中实现

private void setupViews() {

… …
        mPreviousView = (ImageView) dragLayer.findViewById(R.id.previous_screen);
        mNextView = (ImageView) dragLayer.findViewById(R.id.next_screen);

        Drawable previous = mPreviousView.getDrawable();
        Drawable next = mNextView.getDrawable();
        mWorkspace.setIndicators(previous, next);
   	… …
}

获取这个两旁的图片设置到mCurrentScreen屏幕上

Workspace.java

    void setIndicators(Drawable previous, Drawable next) {
        mPreviousIndicator = previous;
        mNextIndicator = next;
        previous.setLevel(mCurrentScreen);
        next.setLevel(mCurrentScreen);
}

在launcher.xml中定义

android:onClick="previousScreen"    <!—点击事件处理-->

在launcher.java中的处理为

    @SuppressWarnings({"UnusedDeclaration"})
    public void previousScreen(View v) {
        if (!isAllAppsVisible()) {
            mWorkspace.scrollLeft();
        }
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public void nextScreen(View v) {
        if (!isAllAppsVisible()) {
            mWorkspace.scrollRight();
        }
    }

进入到workspace中

public void scrollLeft() {
        clearVacantCache();
        if (mScroller.isFinished()) {
            if (mCurrentScreen > 0) snapToScreen(mCurrentScreen - 1);
        } else {
            if (mNextScreen > 0) snapToScreen(mNextScreen - 1);            
        }
    }

    public void scrollRight() {
        clearVacantCache();
        if (mScroller.isFinished()) {
            if (mCurrentScreen < getChildCount() -1) snapToScreen(mCurrentScreen + 1);
        } else {
            if (mNextScreen < getChildCount() -1) snapToScreen(mNextScreen + 1);            
        }
    }

先判断滑动是否完成,然后跳转到目标屏。

void snapToScreen(int whichScreen) {
        snapToScreen(whichScreen, 0, false);
    }

    private void snapToScreen(int whichScreen, int velocity, boolean settle) {
        //if (!mScroller.isFinished()) return;

        whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
        
        clearVacantCache();
        enableChildrenCache(mCurrentScreen, whichScreen);

        mNextScreen = whichScreen;

        mPreviousIndicator.setLevel(mNextScreen);
        mNextIndicator.setLevel(mNextScreen);

        View focusedChild = getFocusedChild();
        if (focusedChild != null && whichScreen != mCurrentScreen &&
                focusedChild == getChildAt(mCurrentScreen)) {
            focusedChild.clearFocus();
        }
        
        final int screenDelta = Math.max(1, Math.abs(whichScreen - mCurrentScreen));
        final int newX = whichScreen * getWidth();
        final int delta = newX - mScrollX;
        int duration = (screenDelta + 1) * 100;

        if (!mScroller.isFinished()) {
            mScroller.abortAnimation();
        }
        
        if (settle) {
            mScrollInterpolator.setDistance(screenDelta);
        } else {
            mScrollInterpolator.disableSettle();
        }
        
        velocity = Math.abs(velocity);
        if (velocity > 0) {
            duration += (duration / (velocity / BASELINE_FLING_VELOCITY))
                    * FLING_VELOCITY_INFLUENCE;
        } else {
            duration += 100;
        }

        awakenScrollBars(duration);
        mScroller.startScroll(mScrollX, 0, delta, 0, duration);
        invalidate();  
    }

六、左右滑动切换屏幕

在workspace中实现屏幕切换主要重写了以下几个方法:onMeasure()、onLayout()、onInterceptTouchEvent()、onTouchEvent()方法。

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        final int width = MeasureSpec.getSize(widthMeasureSpec);
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        if (widthMode != MeasureSpec.EXACTLY) {
            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
        }

        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (heightMode != MeasureSpec.EXACTLY) {
            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
        }

        // The children are given the same width and height as the workspace
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
        }


        if (mFirstLayout) {
            setHorizontalScrollBarEnabled(false);
            scrollTo(mCurrentScreen * width, 0);
            setHorizontalScrollBarEnabled(true);
            updateWallpaperOffset(width * (getChildCount() - 1));
            mFirstLayout = false;
        }
    }


onInterceptTouchEvent()方法和onTouchEvent()主要是来响应手指按下划动时所需要捕获的消息,例如划动的速度,划动的距离等。当手指起来时,根据划动的速度与跨度来判断是向左滑动一页还是向右滑动一页,确保每次用户操作结束之后显示的都是整体的一个子view.

 

在View.java中framework\base\core\java\android\view

将当前视图内容偏移至(x , y)坐标处,即显示(可视)区域位于(x , y)坐标

    public void scrollTo(int x, int y) {
        if (mScrollX != x || mScrollY != y) {
            int oldX = mScrollX;
            int oldY = mScrollY;
            mScrollX = x;
            mScrollY = y;
            onScrollChanged(mScrollX, mScrollY, oldX, oldY);
            if (!awakenScrollBars()) {
                invalidate();
            }
        }
    }

在当前视图内容继续偏移(x , y)个单位,显示(可视)区域也跟着偏移(x,y)个单位

    public void scrollBy(int x, int y) {
        scrollTo(mScrollX + x, mScrollY + y);
    }
mScrollX 与 mScrollY 代表我们当前偏移的位置 , 在当前位置继续偏移(x ,y)个单位 


七、获取应用列表(相当于mainmenu)

在launcher.java中

Launcher 布局文件初始化方法中

private void setupViews() {
… … 
mHandleView = (HandleView) findViewById(R.id.all_apps_button);
mHandleView.setLauncher(this);
mHandleView.setOnClickListener(this);
mHandleView.setOnLongClickListener(this);
… …
}

点击中间button

    public void onClick(View v) {
        Object tag = v.getTag();
        if (tag instanceof ShortcutInfo) {
		… …
        } else if (tag instanceof FolderInfo) {
            handleFolderClick((FolderInfo) tag);
        } else if (v == mHandleView) {
            if (isAllAppsVisible()) {
                closeAllApps(true);
            } else {
                showAllApps(true);
            }
        }

进入到showAllApps()方法

    void showAllApps(boolean animated) {
        mAllAppsGrid.zoom(1.0f, animated);

        ((View) mAllAppsGrid).setFocusable(true);
        ((View) mAllAppsGrid).requestFocus();
        
        // TODO: fade these two too
        mDeleteZone.setVisibility(View.GONE);
    }

在上面使用到了mAllAppsGrid

 privateAllAppsView mAllAppsGrid; //声明

在setupViews()方法中

private void setupViews() {
… …
        mAllAppsGrid = (AllAppsView)dragLayer.findViewById(R.id.all_apps_view);
        mAllAppsGrid.setLauncher(this);
        mAllAppsGrid.setDragController(dragController);
        ((View) mAllAppsGrid).setWillNotDraw(false); // We don't want a hole punched in our window.
        // Manage focusability manually since this thing is always visible
        ((View) mAllAppsGrid).setFocusable(false);
		… …
}

all_apps_view是在all_app_2d.xml中定义

位于res/layout-port中

故找到AllApps2D.java可以找到addApps,removeApps,zoom这几个方法。

    public void zoom(float zoom, boolean animate) {
//        Log.d(TAG, "zooming " + ((zoom == 1.0) ? "open" : "closed"));
        cancelLongPress();

        mZoom = zoom;

        if (isVisible()) {
            getParent().bringChildToFront(this);
            setVisibility(View.VISIBLE);
            mGrid.setAdapter(mAppsAdapter);
            if (animate) {
                startAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.all_apps_2d_fade_in));
            } else {
                onAnimationEnd();
            }
        } else {
            if (animate) {
                startAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.all_apps_2d_fade_out));
            } else {
                onAnimationEnd();
            }
        }
    }

在zoom()方法里面有mGrid.setAdapter(mAppsAdapter),在构造方法中,给adapter已经赋值。

    public AllApps2D(Context context, AttributeSet attrs) {
        super(context, attrs);
        setVisibility(View.GONE);
        setSoundEffectsEnabled(false);

        mAppsAdapter = new AppsAdapter(getContext(), mAllAppsList);
        mAppsAdapter.setNotifyOnChange(false);
    }

在launcher.java函数中

private void loadHotseats() {
… … 
PackageManager pm = getPackageManager();
… …
ResolveInfo bestMatch = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
List<ResolveInfo> allMatches = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
… …
}

如此获取了应用列表



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值