zoukankan      html  css  js  c++  java
  • panel的抽屉效果

    TestPanels.java

    package com.panel;

    import java.util.ArrayList;
    import java.util.HashMap;

    import com.panel.EasingType;
    import com.panel.Panel.OnPanelListener;

    import android.app.Activity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.KeyEvent;
    import android.widget.GridView;
    import android.widget.SimpleAdapter;

    public class TestPanels extends Activity implements OnPanelListener {

    private Panel topPanel;
    private GridView gridView = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    initWidget();
    topPanel.setOnPanelListener(this);
    topPanel.setInterpolator(new BounceInterpolator(EasingType.INOUT));
    fillGridView();
    }

    private void initWidget(){
    topPanel = (Panel) findViewById(R.id.topPanel);
    gridView = (GridView) findViewById(R.id.gridview);
    }

    /**
    * 往GridView填充测试数据
    */
    private void fillGridView(){
    ArrayList<HashMap<String, Object>> lstImageItem = new ArrayList<HashMap<String, Object>>();
    for (int i = 0; i < 10; i++) {
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("ItemImage", R.drawable.ic_launcher);
    map.put("ItemText", "NO." + String.valueOf(i));
    lstImageItem.add(map);
    }

    SimpleAdapter saImageItems = new SimpleAdapter(this, lstImageItem,
    R.layout.item, new String[] { "ItemImage", "ItemText" },
    new int[] { R.id.ItemImage, R.id.ItemText });
    gridView.setAdapter(saImageItems);
    }

    /**
    * 当一个新的按键事件发生时,调用此方法
    */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_T) {
    topPanel.setOpen(!topPanel.isOpen(), false);
    return false;
    }
    return super.onKeyDown(keyCode, event);
    }

    public void onPanelClosed(Panel panel) {
    String panelName = getResources().getResourceEntryName(panel.getId());
    Log.d("TestPanels", "Panel [" + panelName + "] closed");
    }

    public void onPanelOpened(Panel panel) {
    String panelName = getResources().getResourceEntryName(panel.getId());
    Log.d("TestPanels", "Panel [" + panelName + "] opened");
    }
    }

    Panel.java

    package com.panel;

    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Canvas;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.GestureDetector;
    import android.view.GestureDetector.OnGestureListener;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.ViewParent;
    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.FrameLayout;
    import android.widget.LinearLayout;

    public class Panel extends LinearLayout {

    private static final String TAG = "Panel";

    //回调函数 用于监听 Panel 的开合 效果见:setOnClickLstener(OnClickListener listener)
    public static interface OnPanelListener {
    public void onPanelClosed(Panel panel);

    public void onPanelOpened(Panel panel);
    }

    private boolean mIsShrinking;//是否是收缩状态
    private int mPosition;
    private int mDuration;
    private boolean mLinearFlying;
    private int mHandleId;
    private int mContentId;
    private View mHandle;
    private View mContent;
    private Drawable mOpenedHandle;
    private Drawable mClosedHandle;
    private float mTrackX;
    private float mTrackY;
    private float mVelocity;

    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, ANIMATING, READY, TRACKING, FLYING,
    };

    private State mState;//枚举实例
    private Interpolator mInterpolator;//Interpolator 定义了动画的变化速度,可以实现匀速、正加速、负加速、无规则变加速等
    private GestureDetector mGestureDetector;//手势识别类
    private int mContentHeight;
    private int mContentWidth;
    private int mOrientation;
    private float mWeight;
    private PanelOnGestureListener mGestureListener;
    private boolean mBringToFront;

    public Panel(Context context, AttributeSet attrs) {
    super(context, attrs);
    //TypedArray实例是个属性的容器,context.obtainStyledAttributes()方法返回得到。AttributeSet是节点的属性集合
    //这儿com.panel.Panel是节点的属性集合
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Panel);
    mDuration = a.getInteger(R.styleable.Panel_position, 750);
    mPosition = a.getInteger(R.styleable.Panel_position, BOTTOM);
    mLinearFlying = a.getBoolean(R.styleable.Panel_linearFlying, false);
    mWeight = a.getFraction(R.styleable.Panel_weight, 0, 1, 0.0f);
    if (mWeight < 0 || mWeight > 1) {
    mWeight = 0.0f;
    Log.w(TAG, a.getPositionDescription()
    + ": weight must be > 0 and <= 1");
    }

    mOpenedHandle = a.getDrawable(R.styleable.Panel_openedHandle);
    mClosedHandle = a.getDrawable(R.styleable.Panel_closedHandle);
    RuntimeException e = null;
    mHandleId = a.getResourceId(R.styleable.Panel_handle, 0);
    if (mHandleId == 0) {
    e = new IllegalArgumentException(
    a.getPositionDescription()
    + ": The handle attribute is required and must refer to a valid child.");
    }

    mContentId = a.getResourceId(R.styleable.Panel_content, 0);
    if (mContentId == 0) {
    e = new IllegalArgumentException(
    a.getPositionDescription()
    + ": The content attribute is required and must refer to a valid child.");
    }
    a.recycle();//保持定义的属性一致
    if (e != null) {
    throw e;
    }

    mOrientation = (mPosition == TOP || mPosition == BOTTOM) ? VERTICAL
    : HORIZONTAL;
    setOrientation(mOrientation);
    mState = State.READY;
    mGestureListener = new PanelOnGestureListener();
    mGestureDetector = new GestureDetector(mGestureListener);
    mGestureDetector.setIsLongpressEnabled(false);

    //例子上加了这一句.
    setBaselineAligned(false);
    }

    public void setOnPanelListener(OnPanelListener onPanelListener) {
    panelListener = onPanelListener;
    }

    public View getHandle() {
    return mHandle;
    }

    public View getContent() {
    return mContent;
    }

    public void setInterpolator(Interpolator i) {
    mInterpolator = i;
    }

    public boolean setOpen(boolean open, boolean animate) {
    if (mState == State.READY && 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);//里面的参数类型是Runnable
    } else {
    mContent.setVisibility(open ? VISIBLE : GONE);
    postProcess();
    }
    return true;
    }
    return false;
    }

    public boolean isOpen() {
    return mContent.getVisibility() == VISIBLE;
    }

    /**
    * 回调函数 界面初始化快结束时调用 用于得到 mHandle/mContent
    * 当View和它的所有子对象从XML中导入之后,调用此方法
    */
    @Override
    protected void onFinishInflate() {
    super.onFinishInflate();
    mHandle = findViewById(mHandleId);
    if (mHandle == null) {
    String name = getResources().getResourceEntryName(mHandleId);////取得资源文件的名称
    throw new RuntimeException(
    "Your Panel must have a child View whose id attribute is 'R.id."
    + name + "'");
    }
    mHandle.setOnTouchListener(touchListener);
    mHandle.setOnClickListener(clickListener);

    mContent = findViewById(mContentId);
    if (mContent == null) {
    String name = getResources().getResourceEntryName(mHandleId);
    throw new RuntimeException(
    "Your Panel must have a child View whose id attribute is 'R.id."
    + name + "'");
    }

    //先移除mHandle/mContent 然后根据mPosition决定二者的添加次序
    removeView(mHandle);
    removeView(mContent);
    if (mPosition == TOP || mPosition == LEFT) {
    addView(mContent);
    addView(mHandle);
    } else {
    addView(mHandle);
    addView(mContent);
    }

    if (mClosedHandle != null) {
    mHandle.setBackgroundDrawable(mClosedHandle);
    }
    mContent.setClickable(true);
    mContent.setVisibility(GONE);
    if (mWeight > 0) {
    ViewGroup.LayoutParams params = mContent.getLayoutParams();
    if (mOrientation == VERTICAL) {
    params.height = ViewGroup.LayoutParams.FILL_PARENT;
    } else {
    params.width = ViewGroup.LayoutParams.FILL_PARENT;
    }
    mContent.setLayoutParams(params);
    }
    }

    /**
    * 当View附加到一个窗体上时,调用此方法
    */
    @Override
    protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    ViewParent parent = getParent();
    if (parent != null && parent instanceof FrameLayout) {
    mBringToFront = true;
    }
    }

    /**
    * View会调用此方法,来确认自己及所有子对象的大小
    */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (mWeight > 0 && mContent.getVisibility() == VISIBLE) {
    View parent = (View) getParent();
    if (parent != null) {
    if (mOrientation == VERTICAL) {
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(
    (int) (parent.getHeight() * mWeight),
    MeasureSpec.EXACTLY);
    } else {
    widthMeasureSpec = MeasureSpec.makeMeasureSpec(
    (int) (parent.getWidth() * mWeight),
    MeasureSpec.EXACTLY);
    }
    }
    }
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    /**
    * 当View要为所有子对象分配大小和位置时,调用此方法
    */
    @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();
    }

    /**
    * 当View要绘制它的内容时,调用此方法
    */
    @Override
    protected void dispatchDraw(Canvas canvas) {
    // String name = getResources().getResourceEntryName(getId());
    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);
    } else {
    canvas.translate(delta, 0);
    }
    }
    if (mState == State.TRACKING || mState == State.FLYING) {
    canvas.translate(mTrackX, mTrackY);
    }
    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) {
    if (mState == State.ANIMATING) {
    // we are animating
    return false;
    }
    // Log.d(TAG, "state: " + mState + " x: " + event.getX() + " y: " +
    // event.getY());
    int action = event.getAction();
    if (action == MotionEvent.ACTION_DOWN) {
    if (mBringToFront) {
    //Android中的ViewGroup是通过一个Array来保存其Children,当调用某个childView的bringToFront时,是将该childView放在其Parent的Array数组的最后,
    //ViewGroup的dispatchDraw在draw时是按照Array从前往后依次调用drawChild的,这样最后一个childView就在最前面了。
    bringToFront();
    }
    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;
    }
    }
    setInitialPosition = true;
    } else {
    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
    event.offsetLocation(initX, initY);
    }
    if (!mGestureDetector.onTouchEvent(event)) {
    if (action == MotionEvent.ACTION_UP) {
    // tup up after scrolling
    post(startAnimation);
    }
    }
    return false;
    }
    };

    OnClickListener clickListener = new OnClickListener() {
    public void onClick(View v) {
    if (mBringToFront) {
    bringToFront();
    }
    if (initChange()) {
    post(startAnimation);
    }
    }
    };

    public boolean initChange() {
    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;
    }

    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;
    if (mState == State.FLYING) {
    mIsShrinking = (mPosition == TOP || mPosition == LEFT)
    ^ (mVelocity > 0);
    }
    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 (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;
    }
    };

    ////善后工作 比如:改变mHandle背景图 通知开合监听器
    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;
    initChange();
    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) {
    // not used
    }

    //常规滚动
    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) {
    // not used
    }

    public boolean onSingleTapUp(MotionEvent e) {
    // not used
    return false;
    }
    }

    }

    BounceInterpolator.java

    package com.panel;

    import android.view.animation.Interpolator;

    public class BounceInterpolator implements Interpolator {
    private int type;

    public BounceInterpolator(int type) {
    this.type = type;
    }

    public float getInterpolation(float t) {
    if (type == EasingType.IN) {
    return in(t);
    } else if (type == EasingType.OUT) {
    return out(t);
    } else if (type == EasingType.INOUT) {
    return inout(t);
    }
    return 0;
    }

    private float out(float t) {
    if (t < (1 / 2.75)) {
    return 7.5625f * t * t;
    } else if (t < 2 / 2.75) {
    return 7.5625f * (t -= (1.5 / 2.75)) * t + .75f;
    } else if (t < 2.5 / 2.75) {
    return 7.5625f * (t -= (2.25 / 2.75)) * t + .9375f;
    } else {
    return 7.5625f * (t -= (2.625 / 2.75)) * t + .984375f;
    }
    }

    private float in(float t) {
    return 1 - out(1 - t);
    }

    private float inout(float t) {
    if (t < 0.5f) {
    return in(t * 2) * .5f;
    } else {
    return out(t * 2 - 1) * .5f + .5f;
    }
    }
    }

    EasingType.java

    package com.panel;

    public class EasingType {
    public static final int IN = 0;
    public static final int OUT = 1;
    public static final int INOUT = 2;
    }

    main.xml

    <?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.panel"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#767c9b"
    android:orientation="vertical" >

    <LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <com.panel.Panel
    android:id="@+id/topPanel"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="4dip"
    panel:animationDuration="1000"
    panel:closedHandle="@drawable/top_switcher_collapsed_background"
    panel:content="@+id/panelContent"
    panel:handle="@+id/panelHandle"
    panel:linearFlying="true"
    panel:openedHandle="@drawable/top_switcher_expanded_background"
    panel:position="top" >

    <Button
    android:id="@+id/panelHandle"
    android:layout_width="fill_parent"
    android:layout_height="33dip" />

    <LinearLayout
    android:id="@+id/panelContent"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <CheckBox
    android:layout_width="fill_parent"
    android:layout_height="60dip"
    android:background="#688"
    android:gravity="center"
    android:text="top check box" />

    <TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="#323299"
    android:gravity="center"
    android:padding="4dip"
    android:text="Bounce\nInterpolator"
    android:textColor="#eee"
    android:textSize="16dip"
    android:textStyle="bold" />
    </LinearLayout>
    </com.panel.Panel>

    <GridView
    android:id="@+id/gridview"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_below="@+id/SlidingDrawer"
    android:columnWidth="50dip"
    android:gravity="center"
    android:horizontalSpacing="10dip"
    android:numColumns="auto_fit"
    android:verticalSpacing="10dp" />
    </LinearLayout>

    </LinearLayout>

    item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="4dip" >

    <ImageView
    android:id="@+id/ItemImage"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" >
    </ImageView>

    <TextView
    android:id="@+id/ItemText"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@+id/ItemImage"
    android:text="TextView01" >
    </TextView>

    </RelativeLayout>

  • 相关阅读:
    闽江学院2015-2016学年下学期《软件测试》课程-第五次博客作业
    在Swift中应用Grand Central Dispatch(下)
    在Swift中应用Grand Central Dispatch(上)转载自的goldenfiredo001的博客
    Asp.net mvc 框架揭秘之Asp.net +Mvc简介
    JavaScript数组
    网页校验
    删除弹出提示框_MVC
    业务体会
    判断数组值是否有重复
    sql
  • 原文地址:https://www.cnblogs.com/xingmeng/p/2544680.html
Copyright © 2011-2022 走看看