編輯:Android開發實例
應該就是傳說中的手機人人客戶端的實現方法了吧
主要是老外蛋疼的代碼 改了一下
需要自定義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 沒什麼好說的
attrs.xml 配置抽屜的各種組件 放到valuse 文件夾下
- <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>
- <?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>
- 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">
登錄應用程序的屏幕,詢問憑據登錄到一些特定的應用。可能需要登錄到Facebook,微博等本章介紹了,如何創建一個登錄界面,以及如何管理安全問題和錯誤嘗試。首先,必須定義兩
本文實例講述了Android編程ProgressBar自定義樣式之動畫模式實現方法。分享給大家供大家參考,具體如下: 忘記在哪裡看到的那位仁兄寫的,就是通過用動畫
Android IMF(Input Method Framework)是自An
例如,屏幕的上方或下方。要實現這種效果。就需要獲得對話框的Window對象,獲得這個Window對象有多種方法。最容易的就是直接通過AlertDialog類的ge