实现从左边滑动出来的抽屉

点击查看原图

应该就是传说中的手机人人客户端的实现方法了吧

主要是老外蛋疼的代码 改了一下

 

需要自定义view

 

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:panel="http://schemas.android.com/apk/res/com.su.houses"//和包名一致!

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    android:background="#FFFFFFFF"

    android:orientation="horizontal" >

 

    <com.su.houses.panel.Panel

        android:id="@+id/leftPanel1"

        android:layout_width="wrap_content"

        android:layout_height="fill_parent"

        panel:closedHandle="@xml/left_switcher_collapsed_background"

        panel:openedHandle="@xml/left_switcher_expanded_background"

        panel:position="left" >

 

        <Button

            android:id="@+id/panelHandle"

            android:layout_width="10dip"

            android:layout_height="fill_parent" />

 

        <!-- 下面是抽屉的布局 -->

 

        <LinearLayout

            android:id="@id/panelContent"

            android:layout_width="fill_parent"

            android:layout_height="fill_parent"

            android:background="#999932"

            android:gravity="center"

            android:orientation="vertical" >

 

            <Button

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"

                android:background="@drawable/button"

                android:text="项目介绍" />

 

            <Button

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"

                android:background="@drawable/button"

                android:text="总体规划" />

 

            <Button

                android:layout_width= "wrap_content"

                android:layout_height="wrap_content"

                android:background="@drawable/button"

                android:text="交通动线"

                wrap_content="" />

 

            <Button

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"

                android:background="@drawable/button"

                android:text="生活配套" />

 

            <Button

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"

                android:background="@drawable/button"

                android:text="未来生活" />

        </LinearLayout>

    </com.su.houses.panel.Panel>

 

    <ImageView

        android:id="@+id/project_image"

        android:layout_width="fill_parent"

        android:layout_height="fill_parent"

        android:src="@drawable/dogs" />

 

</LinearLayout>

 

Panel.java

 package com.su.houses.panel;

 

 

import com.su.houses.R;

 

import android.content.Context;

import android.content.res.TypedArray;

import android.graphics.Canvas;

import android.graphics.drawable.Drawable;

import android.util.AttributeSet;

import android.view.GestureDetector;

import android.view.GestureDetector.OnGestureListener;

import android.view.MotionEvent;

import android.view.View;

import android.view.animation.Animation;

import android.view.animation.Animation.AnimationListener;

import android.view.animation.Interpolator;

import android.view.animation.LinearInterpolator;

import android.view.animation.TranslateAnimation;

import android.widget.LinearLayout;

 

public class Panel extends LinearLayout

{

    

    /**

     * Callback invoked when the panel is opened/closed.

     */

    public static interface OnPanelListener

    {

        /**

         * Invoked when the panel becomes fully closed.

         */

        public void onPanelClosed(Panel panel);

        

        /**

         * Invoked when the panel becomes fully opened.

         */

        public void onPanelOpened(Panel panel);

    }

    

    private boolean mIsShrinking; // 判断是否是正在收缩状态

    

    private int mPosition;

    

    private int mDuration;

    

    private boolean mLinearFlying;

    

    private View mHandle;

    

    private View mContent;

    

    private Drawable mOpenedHandle;

    

    private Drawable mClosedHandle;

    

    private float mTrackX;// X方向上的隐藏追踪

    

    private float mTrackY;//  Y方向上的隐藏跟踪

    

    private float mVelocity;// Fling状态每秒钟划的像素。

    

    private OnPanelListener panelListener;

    

    public static final int TOP = 0;

    

    public static final int BOTTOM = 1;

    

    public static final int LEFT = 2;

    

    public static final int RIGHT = 3;

    

    private enum State

    {

        ABOUT_TO_ANIMATE, // 由READY进入这个状态,但还没有发生动画,滚动,和滑动

        ANIMATING, // 正在动画

        READY, // 就绪状态什么都不做

        TRACKING, // 拖动

        FLYING,

    };

    

    private State mState;

    

    private Interpolator mInterpolator;

    

    private GestureDetector mGestureDetector; // 手势监听器

    

    private int mContentHeight;

    

    private int mContentWidth;

    

    private int mOrientation; // 这个方向是动画出现的方向。可以修改mPositon来修改实验一下

    

    private PanelOnGestureListener mGestureListener; // 手势监听器的回调

    

    public Panel(Context context, AttributeSet attrs)

    {

        super(context, attrs);

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

        mDuration = a.getInteger(R.styleable.Panel_animationDuration, 750); // duration defaults to 750 ms

        mPosition = a.getInteger(R.styleable.Panel_position, BOTTOM); // position defaults to BOTTOM

        mLinearFlying = a.getBoolean(R.styleable.Panel_linearFlying, false); // linearFlying defaults to false

        mOpenedHandle = a.getDrawable(R.styleable.Panel_openedHandle);

        mClosedHandle = a.getDrawable(R.styleable.Panel_closedHandle);

        a.recycle();

        mOrientation = (mPosition == TOP || mPosition == BOTTOM) ? VERTICAL : HORIZONTAL;

        setOrientation(mOrientation);

        mState = State.READY; // 记录控件的各个状态

        mGestureListener = new PanelOnGestureListener();

        mGestureDetector = new GestureDetector(mGestureListener);

        mGestureDetector.setIsLongpressEnabled(false);// 长按没有返回值所以去掉

    }

    

    /**

     * Sets the listener that receives a notification when the panel becomes open/close.

     * 

     * @param onPanelListener The listener to be notified when the panel is opened/closed.

     */

    public void setOnPanelListener(OnPanelListener onPanelListener)

    {

        panelListener = onPanelListener;

    }

    

    /**

     * Gets Panel's mHandle

     * 

     * @return Panel's mHandle

     */

    public View getHandle()

    {

        return mHandle;

    }

    

    /**

     * Gets Panel's mContent

     * 

     * @return Panel's mContent

     */

    public View getContent()

    {

        return mContent;

    }

    

    /**

     * Sets the acceleration curve for panel's animation.

     * 

     * @param i The interpolator which defines the acceleration curve

     */

    public void setInterpolator(Interpolator i)

    {

        mInterpolator = i;

    }

    

    /**

     * Set the opened state of Panel.

     * 

     * @param open True if Panel is to be opened, false if Panel is to be closed.

     * @param animate True if use animation, false otherwise.

     * 

     */

    public void setOpen(boolean open, boolean animate)

    {

        if (isOpen() ^ open)

        {

            mIsShrinking = !open;

            if (animate)

            {

                mState = State.ABOUT_TO_ANIMATE;

                if (!mIsShrinking)

                {

                    // this could make flicker so we test mState in dispatchDraw()

                    // to see if is equal to ABOUT_TO_ANIMATE

                    mContent.setVisibility(VISIBLE);

                }

                post(startAnimation);

            }

            else

            {

                mContent.setVisibility(open ? VISIBLE : GONE);

                postProcess();

            }

        }

    }

    

    /**

     * Returns the opened status for Panel.

     * 

     * @return True if Panel is opened, false otherwise.

     */

    public boolean isOpen()

    {

        return mContent.getVisibility() == VISIBLE;

    }

    

    /*

     * 在这里完成了一些handler与Content的初始化工作

     */

    @Override

    protected void onFinishInflate()

    {

        super.onFinishInflate(); // 调用超类方法

        mHandle = findViewById(R.id.panelHandle); // 获取handle引用设置Handle的监听器

        if (mHandle == null)

        {

            throw new RuntimeException("Your Panel must have a View whose id attribute is 'R.id.panelHandle'");

        }

        mHandle.setOnTouchListener(touchListener);

        

        mContent = findViewById(R.id.panelContent); // 获取Content的引用

        if (mContent == null)

        {

            throw new RuntimeException("Your Panel must have a View whose id attribute is 'R.id.panelContent'");

        }

        

        // reposition children

        removeView(mHandle);

        removeView(mContent);

        if (mPosition == TOP || mPosition == LEFT)

        {

            addView(mContent);

            addView(mHandle);

        }

        else

        {

            addView(mHandle);

            addView(mContent);

        }

        

        if (mClosedHandle != null) // 设置handle的背景

        {

            mHandle.setBackgroundDrawable(mClosedHandle);

        }

        mContent.setVisibility(GONE); // 隐藏内容

    }

    

    /*

     * 主要是获取了Content的宽高

     */

    @Override

    protected void onLayout(boolean changed, int l, int t, int r, int b)

    {

        super.onLayout(changed, l, t, r, b);

        mContentWidth = mContent.getWidth();

        mContentHeight = mContent.getHeight();

    }

    

    /*

     * 根据控件的不同的状态修改画布的偏移量

     */

    @Override

    protected void dispatchDraw(Canvas canvas)

    {

        // String name = getResources().getResourceEntryName(getId());

        // this is why 'mState' was added:

        // avoid flicker before animation start

        if (mState == State.ABOUT_TO_ANIMATE && !mIsShrinking)

        {

            int delta = mOrientation == VERTICAL ? mContentHeight : mContentWidth;

            if (mPosition == LEFT || mPosition == TOP)

            {

                delta = -delta;

            }

            if (mOrientation == VERTICAL)

            {

                canvas.translate(0, delta);// translate(float,float)的作用。将控件的起始坐标平移到(0,delta)的位置

            }

            else

            {

                // 这里平移到了delta,0的位置,但是等控件完全展开后如果还是这个矩阵的话,

                // 那Content应该是看不到,现在能 看到,那在哪里转化了呢。动画完了就还原

                canvas.translate(delta, 0);

                

            }

        }

        if (mState == State.TRACKING || mState == State.FLYING)

        {

            canvas.translate(mTrackX, mTrackY);

            /*

             * 激活状态的Content和handler以mTranceX和mTranckY为参数不断的移动就能形成动画

             */

            /*

             * 不会,translate只能使view看起来发生了变化,但layout(),不会被调用 也就是,外部的处罚事件还是会按照translate以前的标准触发。

             */

            /*

             * 没什么关系,translate不会改变框架中事件的判断。

             */

 

        }

        super.dispatchDraw(canvas);

    }

    

    private float ensureRange(float v, int min, int max)

    {

        v = Math.max(v, min);

        v = Math.min(v, max);

        return v;

    }

    

    OnTouchListener touchListener = new OnTouchListener()

    {

        int initX;

        

        int initY;

        

        boolean setInitialPosition; // 判断是否设置了初始滚动位置 它是类的成员函数

        

        public boolean onTouch(View v, MotionEvent event)

        {

            int action = event.getAction();

            // if 用户向下按

            // Content的X方向和Y方向滚动都为0

            // if 内容为隐藏状态 根据方向和位置修改X方向或Y方向上的滚动

            if (action == MotionEvent.ACTION_DOWN)

            {

                initX = 0;

                initY = 0;

                if (mContent.getVisibility() == GONE)

                {

                    // since we may not know content dimensions we use factors here

                    if (mOrientation == VERTICAL)

                    {

                        initY = mPosition == TOP ? -1 : 1;

                    }

                    else

                    {

                        initX = mPosition == LEFT ? -1 : 1;

                    }

                }

                // 设置初始化的记录为true

                setInitialPosition = true;

            }

            else

            {

                // if 用户按下的不是向下

                // if 已经设置了初始滚动位置

                // 修改内容X,Y方向上的滚动。

                // 设置控件滚动的初始化位置

                // 设置初始化的记录为false

                /*

                 * ACTION_DOWN的操作都是根据handle的,Content激活后重新layout,handle的位置发生了改变, 而以后所有的操作的坐标又都是根据现在的handler的位置的,这样就存在了误差

                 * 所有需要offsetLocation把这个误差修正过来。

                 */

                if (setInitialPosition)

                {

                    // now we know content dimensions, so we multiply factors...

                    initX *= mContentWidth;

                    initY *= mContentHeight;

                    // ... and set initial panel's position

                    mGestureListener.setScroll(initX, initY);

                    setInitialPosition = false;

                    // for offsetLocation we have to invert values

                    initX = -initX;

                    initY = -initY;

                }

                // offset every ACTION_MOVE & ACTION_UP event

                // 如果这里去掉,开始拖动不响应,拖动了一段距离后才能拉出Content

                event.offsetLocation(initX, initY); //  在X,Y的方向上分别加上initX,initY。

 

                /*

                 * 不加这一句会使distanceX的值差异非常大,而distanceX会影响到mTranceX。

                 */

                

                /*

                 * mTranceX是有范围的,如果最小值小于这个范围就不会在发生该变了, 所有 会出现延迟的现象。其实那是还没有到达最小值。

                 */

            }

            

            // if 手势监听器没有处理

            // if 用户事件为抬起事件 启动动画线程

            boolean result = mGestureDetector.onTouchEvent(event);

            if (!result)

            {

                if (action == MotionEvent.ACTION_UP)

                {

                    // tup up after scrolling

                    post(startAnimation);

                }

            }

            return false;

        }

    };

    

    Runnable startAnimation = new Runnable()

    {

        public void run()

        {

            // this is why we post this Runnable couple of lines above:

            // now its save to use mContent.getHeight() && mContent.getWidth()

            TranslateAnimation animation; // 负责移动的动画

            int fromXDelta = 0, toXDelta = 0, fromYDelta = 0, toYDelta = 0;// ???() 动画的起始、终点坐标。相对于器parent View

            // if 控件状态为Fling

            // 判断是否处于收缩状态

            if (mState == State.FLYING)

            {

                mIsShrinking = (mPosition == TOP || mPosition == LEFT) ^ (mVelocity > 0);

            }

            // if 控件为纵向

            // 获取Y方向Delta

            // if 控件是拖动

            // 计算周期时间 

            

            int calculatedDuration;

            if (mOrientation == VERTICAL)

            {

                int height = mContentHeight;

                if (!mIsShrinking)

                {

                    fromYDelta = mPosition == TOP ? -height : height;

                }

                else

                {

                    toYDelta = mPosition == TOP ? -height : height;

                }

                if (mState == State.TRACKING)

                {

                    if (Math.abs(mTrackY - fromYDelta) < Math.abs(mTrackY - toYDelta))

                    {

                        mIsShrinking = !mIsShrinking;

                        toYDelta = fromYDelta;

                    }

                    fromYDelta = (int)mTrackY;

                }

                else if (mState == State.FLYING)

                {

                    fromYDelta = (int)mTrackY;

                }

                // for FLYING events we calculate animation duration based on flying velocity

                // also for very high velocity make sure duration >= 20 ms

                if (mState == State.FLYING && mLinearFlying)

                {

                    calculatedDuration = (int)(1000 * Math.abs((toYDelta - fromYDelta) / mVelocity));

                    calculatedDuration = Math.max(calculatedDuration, 20);

                }

                else

                {

                    calculatedDuration = mDuration * Math.abs(toYDelta - fromYDelta) / mContentHeight;

                }

            }

            else

            {

                int width = mContentWidth;

                if (!mIsShrinking)

                {

                    fromXDelta = mPosition == LEFT ? -width : width;

                }

                else

                {

                    toXDelta = mPosition == LEFT ? -width : width;

                }

                if (mState == State.TRACKING)

                {

                    if (Math.abs(mTrackX - fromXDelta) < Math.abs(mTrackX - toXDelta))

                    {

                        mIsShrinking = !mIsShrinking;

                        toXDelta = fromXDelta;

                    }

                    fromXDelta = (int)mTrackX;

                }

                else if (mState == State.FLYING)

                {

                    fromXDelta = (int)mTrackX;

                }

                // for FLYING events we calculate animation duration based on flying velocity

                // also for very high velocity make sure duration >= 20 ms

                if (mState == State.FLYING && mLinearFlying)

                {

                    calculatedDuration = (int)(1000 * Math.abs((toXDelta - fromXDelta) / mVelocity));

                    calculatedDuration = Math.max(calculatedDuration, 20);

                }

                else

                {

                    calculatedDuration = mDuration * Math.abs(toXDelta - fromXDelta) / mContentWidth;

                }

            }

            

            mTrackX = mTrackY = 0;

            // if 计算周期为0

            // toXDelta跟fromXDelta相等

            if (calculatedDuration == 0)

            {

                mState = State.READY;

                if (mIsShrinking)

                {

                    mContent.setVisibility(GONE);

                }

                postProcess();

                return;

            }

            

            

            animation = new TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta);

            animation.setDuration(calculatedDuration);

            animation.setAnimationListener(animationListener);

            if (mState == State.FLYING && mLinearFlying)

            {

                animation.setInterpolator(new LinearInterpolator());

            }

            else if (mInterpolator != null)

            {

                animation.setInterpolator(mInterpolator);

            }

            startAnimation(animation);

        }

    };

    

    

    private AnimationListener animationListener = new AnimationListener()

    {

        public void onAnimationEnd(Animation animation)

        {

            mState = State.READY;

            if (mIsShrinking)

            {

                mContent.setVisibility(GONE);

            }

            postProcess();

        }

        

        public void onAnimationRepeat(Animation animation)

        {

        }

        

        public void onAnimationStart(Animation animation)

        {

            mState = State.ANIMATING;

        }

    };

    

    /**

     * 处理背景的drawable

     */

    private void postProcess()

    {

        if (mIsShrinking && mClosedHandle != null)

        {

            mHandle.setBackgroundDrawable(mClosedHandle);

        }

        else if (!mIsShrinking && mOpenedHandle != null)

        {

            mHandle.setBackgroundDrawable(mOpenedHandle);

        }

        // invoke listener if any

        if (panelListener != null)

        {

            if (mIsShrinking)

            {

                panelListener.onPanelClosed(Panel.this);

            }

            else

            {

                panelListener.onPanelOpened(Panel.this);

            }

        }

    }

    

    class PanelOnGestureListener implements OnGestureListener

    {

        float scrollY;

        

        float scrollX;

        

        public void setScroll(int initScrollX, int initScrollY)

        {

            scrollX = initScrollX;

            scrollY = initScrollY;

        }

        

        public boolean onDown(MotionEvent e)

        {

            scrollX = scrollY = 0;

            if (mState != State.READY)

            {

                // we are animating or just about to animate

                return false;

            }

            mState = State.ABOUT_TO_ANIMATE;

            mIsShrinking = mContent.getVisibility() == VISIBLE;

            if (!mIsShrinking)

            {

                // this could make flicker so we test mState in dispatchDraw()

                // to see if is equal to ABOUT_TO_ANIMATE

                mContent.setVisibility(VISIBLE);

            }

            return true;

        }

        

        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)

        {

            mState = State.FLYING;

            mVelocity = mOrientation == VERTICAL ? velocityY : velocityX;

            post(startAnimation);

            return true;

        }

        

        public void onLongPress(MotionEvent e)

        {

            

        }

        

        /*

         * 该变mTranceX和mTranceY,更新UI

         */

        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)

        {

            mState = State.TRACKING;

            float tmpY = 0, tmpX = 0;

            if (mOrientation == VERTICAL)

            {

                scrollY -= distanceY;

                if (mPosition == TOP)

                {

                    tmpY = ensureRange(scrollY, -mContentHeight, 0);

                }

                else

                {

                    tmpY = ensureRange(scrollY, 0, mContentHeight);

                }

            }

            else

            {

                scrollX -= distanceX;

                if (mPosition == LEFT)

                {

                    tmpX = ensureRange(scrollX, -mContentWidth, 0);

                }

                else

                {

                    tmpX = ensureRange(scrollX, 0, mContentWidth);

                }

            }

            if (tmpX != mTrackX || tmpY != mTrackY)

            {

                mTrackX = tmpX;

                mTrackY = tmpY;

                invalidate();

            }

            return true;

        }

        

        public void onShowPress(MotionEvent e)

        {

            

        }

        

        public boolean onSingleTapUp(MotionEvent e)

        {

            // simple tap: click

            post(startAnimation);

            return true;

        }

    }

}

 
 
left_switcher_collapsed_background.xml 一个 selector 没什么好说的
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_window_focused="false" android:state_pressed="false" android:drawable="@drawable/notes_button" />
    <item android:state_window_focused="true" android:state_pressed="true" android:drawable="@drawable/notes_button" />
    <item android:state_focused="true" android:drawable="@drawable/notes_button" />
</selector>
 
 
attrs.xml 配置抽屉的各种组件 放到valuse 文件夹下
<?xml version="1.0" encoding="utf-8"?>
<resources>
 
    <declare-styleable name="Panel">
 
        <!-- Defines panel animation duration in ms. -->
        <attr format="integer" name="animationDuration" />
        <!-- Defines panel position on the screen. -->
        <attr name="position">
 
            <!-- Panel placed at top of the screen. -->
            <enum name="top" value="0" />
            <!-- Panel placed at bottom of the screen. -->
            <enum name="bottom" value="1" />
            <!-- Panel placed at left of the screen. -->
            <enum name="left" value="2" />
            <!-- Panel placed at right of the screen. -->
            <enum name="right" value="3" />
        </attr>
        <!-- Defines if flying gesture forces linear interpolator in animation. -->
        <attr format="boolean" name="linearFlying" />
        <!-- Defines opened handle (drawable/color). -->
        <attr format="reference|color" name="openedHandle" />
        <!-- Defines closed handle (drawable/color). -->
        <attr format="reference|color" name="closedHandle" />
    </declare-styleable>
    <declare-styleable name="SmoothButton">
        <attr format="reference" name="transitionDrawable" />
        <attr format="integer" name="transitionDrawableLength" />
        <attr format="color" name="transitionTextColorUp" />
        <attr format="color" name="transitionTextColorDown" />
    </declare-styleable>
 
    <item name="panelHandle" type="id"/>
    <item name="panelContent" type="id"/>
 
</resources>
 

 

 

 
 

android界面实用代码 总结

日期:2012年06月22日 ⁄ 分类: Android开发 ⁄ 围观:2 ⁄ 评论:0 ⁄ 引用:0

  1.要想让您的控件水平居中或垂直居中其实很简单,只要在控件的上一级中 android:gravity="center"

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    
android:gravity="center" 
    android:background="#000000"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <ImageView
    android:id="@+id/logo"
    android:src="@drawable/logo"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    />

</LinearLayout>

 

2.全局全屏

 

<application

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name" 

        android:theme="@android:style/Theme.NoTitleBar.Fullscreen" 

 android:screenOrientation="landscape" 

        >

3.横屏

 

 <activity

            android:label="@string/app_name"

            android:name=".HousesActivity" 

            android:screenOrientation="landscape">

 

 

 

 

 
 
posted @ 2012-06-22 18:28  sfshine  阅读(1448)  评论(2编辑  收藏  举报