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

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

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

需要自定义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">


你可能感兴趣的:(实现)