to top
Android APIs
 

ApplicationsStackLayout.java

← Back

The file containing the source code shown below is located in the corresponding directory in <sdk>/samples/android-<version>/...

/*
 * Copyright (C) 2007 The Android开源工程
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.home;

import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.LayoutInflater;
import android.widget.TextView;

import java.util.List;

/**
 * The ApplicationsStackLayout is a specialized layout used for the purpose of the home screen
 * only. This layout stacks various icons in three distinct areas: the recents, the favorites
 * (or faves) and the button.
 *
 * This layout supports two different orientations: vertical and horizontal. When horizontal,
 * the areas are laid out this way:
 *
 * [RECENTS][FAVES][BUTTON]
 *
 * When vertical, the layout is the following:
 *
 * [RECENTS]
 * [FAVES]
 * [BUTTON]
 *
 * The layout operates from the "bottom up" (or from right to left.) This means that the button
 * area will first be laid out, then the faves area, then the recents. When there are too many
 * favorites, the recents area is not displayed.
 *
 * The following attributes can be set in XML:
 * 
 * orientation: horizontal or vertical
 * marginLeft: the left margin of each element in the stack
 * marginTop: the top margin of each element in the stack
 * marginRight: the right margin of each element in the stack
 * marginBottom: the bottom margin of each element in the stack
 */
public class ApplicationsStackLayout extends ViewGroup implements View.OnClickListener {
    public static final int HORIZONTAL = 0;
    public static final int VERTICAL = 1;

    private View mButton;
    private LayoutInflater mInflater;

    private int mFavoritesEnd;
    private int mFavoritesStart;

    private List<ApplicationInfo> mFavorites;
    private List<ApplicationInfo> mRecents;

    private int mOrientation = VERTICAL;

    private int mMarginLeft;
    private int mMarginTop;
    private int mMarginRight;
    private int mMarginBottom;

    private Rect mDrawRect = new Rect();

    private Drawable mBackground;
    private int mIconSize;

    public ApplicationsStackLayout(Context context) {
        super(context);
        initLayout();
    }

    public ApplicationsStackLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray a =
                context.obtainStyledAttributes(attrs, R.styleable.ApplicationsStackLayout);

        mOrientation = a.getInt(R.styleable.ApplicationsStackLayout_stackOrientation, VERTICAL);

        mMarginLeft = a.getDimensionPixelSize(R.styleable.ApplicationsStackLayout_marginLeft, 0);
        mMarginTop = a.getDimensionPixelSize(R.styleable.ApplicationsStackLayout_marginTop, 0);
        mMarginRight = a.getDimensionPixelSize(R.styleable.ApplicationsStackLayout_marginRight, 0);
        mMarginBottom = a.getDimensionPixelSize(R.styleable.ApplicationsStackLayout_marginBottom, 0);

        a.recycle();

        mIconSize = 42; //(int) getResources().getDimension(android.R.dimen.app_icon_size);

        initLayout();
    }

    private void initLayout() {
        mInflater = LayoutInflater.from(getContext());
        mButton = mInflater.inflate(R.layout.all_applications_button, this, false);
        addView(mButton);

        mBackground = getBackground();
        setBackgroundDrawable(null);
        setWillNotDraw(false);
    }

    /**
     * Return the current orientation, either VERTICAL (default) or HORIZONTAL.
     * 
     * @return the stack orientation
     */
    public int getOrientation() {
        return mOrientation;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        final Drawable background = mBackground;

        final int right = getWidth();
        final int bottom = getHeight();

        // Draw behind recents
        if (mOrientation == VERTICAL) {
            mDrawRect.set(0, 0, right, mFavoritesStart);
        } else {
            mDrawRect.set(0, 0, mFavoritesStart, bottom);
        }
        background.setBounds(mDrawRect);
        background.draw(canvas);

        // Draw behind favorites
        if (mFavoritesStart > -1) {
            if (mOrientation == VERTICAL) {
                mDrawRect.set(0, mFavoritesStart, right, mFavoritesEnd);
            } else {
                mDrawRect.set(mFavoritesStart, 0, mFavoritesEnd, bottom);
            }
            background.setBounds(mDrawRect);
            background.draw(canvas);
        }

        super.onDraw(canvas);
    }

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

        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);

        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY) {
            throw new IllegalStateException("ApplicationsStackLayout can only be used with "
                    + "measure spec mode=EXACTLY");
        }

        setMeasuredDimension(widthSize, heightSize);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        removeAllApplications();

        LayoutParams layoutParams = mButton.getLayoutParams();
        final int widthSpec = MeasureSpec.makeMeasureSpec(layoutParams.width, MeasureSpec.EXACTLY);
        final int heightSpec = MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
        mButton.measure(widthSpec, heightSpec);

        if (mOrientation == VERTICAL) {
            layoutVertical();
        } else {
            layoutHorizontal();
        }
    }

    private void layoutVertical() {
        int childLeft = 0;
        int childTop = getHeight();

        int childWidth = mButton.getMeasuredWidth();
        int childHeight = mButton.getMeasuredHeight();

        childTop -= childHeight + mMarginBottom;
        mButton.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
        childTop -= mMarginTop;
        mFavoritesEnd = childTop - mMarginBottom;

        int oldChildTop = childTop;
        childTop = stackApplications(mFavorites, childLeft, childTop);
        if (childTop != oldChildTop) {
            mFavoritesStart = childTop + mMarginTop;
        } else {
            mFavoritesStart = -1;
        }

        stackApplications(mRecents, childLeft, childTop);
    }

    private void layoutHorizontal() {
        int childLeft = getWidth();
        int childTop = 0;

        int childWidth = mButton.getMeasuredWidth();
        int childHeight = mButton.getMeasuredHeight();

        childLeft -= childWidth;
        mButton.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
        childLeft -= mMarginLeft;
        mFavoritesEnd = childLeft - mMarginRight;

        int oldChildLeft = childLeft;
        childLeft = stackApplications(mFavorites, childLeft, childTop);
        if (childLeft != oldChildLeft) {
            mFavoritesStart = childLeft + mMarginLeft;
        } else {
            mFavoritesStart = -1;
        }

        stackApplications(mRecents, childLeft, childTop);
    }

    private int stackApplications(List<ApplicationInfo> applications, int childLeft, int childTop) {
        LayoutParams layoutParams;
        int widthSpec;
        int heightSpec;
        int childWidth;
        int childHeight;

        final boolean isVertical = mOrientation == VERTICAL;

        final int count = applications.size();
        for (int i = count - 1; i >= 0; i--) {
            final ApplicationInfo info = applications.get(i);
            final View view = createApplicationIcon(mInflater, this, info);

            layoutParams = view.getLayoutParams();
            widthSpec = MeasureSpec.makeMeasureSpec(layoutParams.width, MeasureSpec.EXACTLY);
            heightSpec = MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
            view.measure(widthSpec, heightSpec);

            childWidth = view.getMeasuredWidth();
            childHeight = view.getMeasuredHeight();

            if (isVertical) {
                childTop -= childHeight + mMarginBottom;

                if (childTop < 0) {
                    childTop += childHeight + mMarginBottom;
                    break;
                }
            } else {
                childLeft -= childWidth + mMarginRight;

                if (childLeft < 0) {
                    childLeft += childWidth + mMarginRight;
                    break;
                }
            }

            addViewInLayout(view, -1, layoutParams);

            view.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);

            if (isVertical) {
                childTop -= mMarginTop;
            } else {
                childLeft -= mMarginLeft;
            }
        }

        return isVertical ? childTop : childLeft;
    }

    private void removeAllApplications() {
        final int count = getChildCount();
        for (int i = count - 1; i >= 0; i--) {
            final View view = getChildAt(i);
            if (view != mButton) {
                removeViewAt(i);
            }
        }
    }

    private View createApplicationIcon(LayoutInflater inflater,
            ViewGroup group, ApplicationInfo info) {

        TextView textView = (TextView) inflater.inflate(R.layout.favorite, group, false);

        info.icon.setBounds(0, 0, mIconSize, mIconSize);
        textView.setCompoundDrawables(null, info.icon, null, null);
        textView.setText(info.title);

        textView.setTag(info.intent);
        textView.setOnClickListener(this);

        return textView;
    }

    /**
     * Sets the list of favorites.
     *
     * @param applications the applications to put in the favorites area
     */
    public void setFavorites(List<ApplicationInfo> applications) {
        mFavorites = applications;
        requestLayout();
    }

    /**
     * Sets the list of recents.
     *
     * @param applications the applications to put in the recents area
     */
    public void setRecents(List<ApplicationInfo> applications) {
        mRecents = applications;
        requestLayout();
    }

    public void onClick(View v) {
        getContext().startActivity((Intent) v.getTag());
    }
}