編輯:關於Android編程
package cc.cd; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.Toast; import android.app.Activity; import android.content.Context; /** * Demo描述: * 將Patience5中使用的側滑菜單封裝為一個自定義控件從而便於復用 * * 該示例在Patience5的不同之處: * 1 Patience5中采用的是LinearLayout布局,而該自定義控件繼承自RelativeLayout * 2 在Patience5中是不斷改變menuView的leftMargin而在此處是不斷改變contentView的 * rightMargin.這一點在閱讀代碼時要注意. * * 參考資料: * http://blog.csdn.net/guolin_blog/article/details/8744400 * Thank you very much */ public class MainActivity extends Activity { private Context mContext; private ListView mContentListView; private Button mContentMenuButton; private SlidingMenuRelativeLayout mSlidingMenuRelativeLayout; private String [] listViewItems=new String [20]; private ArrayAdaptermArrayAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); init(); } private void init(){ mContext=this; mContentListView=(ListView) findViewById(R.id.contentListView); mContentMenuButton=(Button) findViewById(R.id.contentMenuButton); mContentMenuButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { boolean isMenuVisible=mSlidingMenuRelativeLayout.isMenuVisible(); if (isMenuVisible) { mSlidingMenuRelativeLayout.scrollToContent(); } else { mSlidingMenuRelativeLayout.scrollToMenu(); } } }); mSlidingMenuRelativeLayout=(SlidingMenuRelativeLayout) findViewById(R.id.slidingMenuRelativeLayout); //滑動事件綁定在contentListView上 mSlidingMenuRelativeLayout.setBindView(mContentListView); initListViewData(); mArrayAdapter=new ArrayAdapter (mContext, android.R.layout.simple_list_item_1, listViewItems); mContentListView.setAdapter(mArrayAdapter); mContentListView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView> arg0, View arg1, int position, long arg3) { String item = listViewItems[position]; Toast.makeText(mContext, item, Toast.LENGTH_SHORT).show(); } }); } private void initListViewData(){ String temp=null; for (int i = 0; i < 20; i++) { temp="This is "+i; listViewItems[i]=temp; } } }
SlidingMenuRelativeLayout如下:
package cc.cd; import android.content.Context; import android.os.AsyncTask; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.WindowManager; import android.widget.Button; import android.widget.RelativeLayout; import android.widget.Toast; /** * 示例說明: * 1 該自定義控件繼承自RelativeLayout * 2 在布局文件中有兩部分menuView和contentView.因為是RelativeLayout布局 * 所以這兩者是重疊的. * 3 通過不斷改變contentView的rightMargin值來顯示和隱藏menuView * 4 請注意在xml布局文件中設置了contentView對齊方式為layout_alignParentRight * 設置了menuView對齊方式為 android:layout_alignParentLeft. * 所以注意代碼: * //設置contentView的最小和最大rightMargin值 * contentParamsMinRightMargin=-mMenuLayoutParams.width; * contentParamsMaxRightMargin=0; * 這個還是挺巧妙的.因為contentView是相對於父控件右對齊的.所以在原始狀態下 * 他的rightMargin的值為0.當手指按在contentView上滑向屏幕的右側時可以不斷減小 * 它的rightMargin,從而導致contentView移向屏幕的右側本來被其遮蓋的menuView * 也就隨之顯現. * 所以contentView的最大rightMargin值=0,這個也就是我們進入App後看到的: * 顯示了contentView,遮蓋了menuView.它的rightMargin=0;與父控件右側對齊. * 當contentView移向屏幕的右邊時,它的rightMargin在逐漸減小直到rightMargin * 的絕對值對於menuView的寬度. * * * 代碼細節: * 1 mMenuLayoutParams和mContentLayoutParams都是 * MarginLayoutParams類型的,因為menuView和conentView的父控件 * 是自定義的SlidingMenuRelativeLayout,而不是常用的系統XXXLayout * 2 在手指在ListView上滑動然後抬起,有時會出現ListView的item被按下且一直沒有 * 彈起的情況.造成該現象的原因暫時不明,但可用unFocusBindView()方法使得 * ListView失去焦點.該方法在示例中兩次被調用.可以將其注釋後觀察效果. * 關於這點,在代碼中仍然存在小bug.需以後繼續優化. */ public class SlidingMenuRelativeLayout extends RelativeLayout { private int screenWidth; private View contentView; private View menuView; private View mBindView; private float xDown; private float xMove; private float xUp; private float yDown; private float yMove; private float yUp; private Button mButton; private Context mContext; // 當前是否在滑動 private boolean isSliding; // 在被判定為滾動之前用戶手指可移動的最大值 private int scaledTouchSlop; // menu是否可見的標志位,該值在滑動過程中無效. // 只有在滑動結束後,完全顯示或隱藏menu時才會更改此值 private boolean isMenuVisible = false; private int contentParamsMaxRightMargin = 0; private int contentParamsMinRightMargin = 0; // 速度追蹤 private VelocityTracker mVelocityTracker; // 阈值 public static final int VELOCITY_THRESHOLD = 200; // TAG private final static String TAG = "SlidingMenuRelativeLayout"; // menu的布局LayoutParams private MarginLayoutParams mMenuLayoutParams; // content的布局LayoutParams private MarginLayoutParams mContentLayoutParams; public SlidingMenuRelativeLayout(Context context) { super(context); } public SlidingMenuRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public SlidingMenuRelativeLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (changed) { // 初始化contentView contentView = getChildAt(1); mContentLayoutParams = (MarginLayoutParams) contentView.getLayoutParams(); // 將contentView的寬度設置為屏幕的寬度 mContentLayoutParams.width = screenWidth; contentView.setLayoutParams(mContentLayoutParams); // 初始化menuView menuView = getChildAt(0); mMenuLayoutParams = (MarginLayoutParams) menuView.getLayoutParams(); // 設置contentView的最小和最大rightMargin值. contentParamsMinRightMargin = -mMenuLayoutParams.width; contentParamsMaxRightMargin = 0; mButton = (Button) menuView.findViewById(R.id.menuButton); mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { Toast.makeText(mContext, "Hello", Toast.LENGTH_SHORT).show(); } }); } } private void init(Context context) { mContext = context; // 獲取屏幕寬度 WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); screenWidth = windowManager.getDefaultDisplay().getWidth(); // 獲取ScaledTouchSlop scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } /** * 注冊處理Touch事件的View 在改示例中處理了ListView的Touch來顯示和隱藏menuView的. * 在實際開發中可以依據需求設置其他為其他控件 */ public void setBindView(View bindView) { mBindView = bindView; mBindView.setOnTouchListener(new TouchListenerImpl()); } // 使BindView失去焦點 public void unFocusBindView() { if (mBindView != null) { mBindView.setPressed(false); mBindView.setFocusable(false); mBindView.setFocusableInTouchMode(false); } } private class TouchListenerImpl implements OnTouchListener { @Override public boolean onTouch(View v, MotionEvent event) { // 開始速度追蹤 startVelocityTracker(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: xDown = event.getRawX(); yDown = event.getRawY(); break; case MotionEvent.ACTION_MOVE: xMove = event.getRawX(); yMove = event.getRawY(); int distanceX = (int) (xMove - xDown); int distanceY = (int) (yMove - yDown); // 手指滑向屏幕右側,distanceX為正數 if (!isMenuVisible&& distanceX >= scaledTouchSlop && (isSliding || Math.abs(distanceY) <= scaledTouchSlop)) { isSliding = true; mContentLayoutParams.rightMargin = -distanceX; // 處理越界的情況 if (mContentLayoutParams.rightMargin < contentParamsMinRightMargin) { mContentLayoutParams.rightMargin = contentParamsMinRightMargin; } // 設置contentView的LayoutParams contentView.setLayoutParams(mContentLayoutParams); } // 手指滑向屏幕左側,distanceX為負數 if (isMenuVisible && -distanceX >= scaledTouchSlop) { isSliding = true; mContentLayoutParams.rightMargin = contentParamsMinRightMargin - distanceX; // 處理越界的情況 if (mContentLayoutParams.rightMargin > contentParamsMaxRightMargin) { mContentLayoutParams.rightMargin = contentParamsMaxRightMargin; } // 設置contentView的LayoutParams contentView.setLayoutParams(mContentLayoutParams); } break; case MotionEvent.ACTION_UP: xUp = event.getRawX(); int upDistanceX = (int) (xUp - xDown); if (isSliding) { // 判斷手勢意圖想顯示menu if (wantToShowMenu()) { // 判斷是否顯示menu if (shouldScrollToMenu()) { scrollToMenu(); } else { scrollToContent(); } } // 判斷手勢意圖想顯示content if (wantToShowContent()) { // 判斷是否顯示content if (shouldScrollToContent()) { scrollToContent(); } else { scrollToMenu(); } } } else { // 當完全顯示菜單界面時,點擊僅能看到的contentView可將其完全顯示 if (upDistanceX < scaledTouchSlop && isMenuVisible) { scrollToContent(); } } // 終止速度追蹤 stopVelocityTracker(); break; default: break; } /** * 在處理完DOWN,MOVE,UP後進行該if...else判斷. * 1 如果不是在滑動狀態,則返回false.否則ListView無法滑動且其Item無法點擊. * 2 若在滑動,則讓ListView失去焦點. */ if (!isSliding) { return false; } else { unFocusBindView(); } return true; } } /** * 判斷當前手勢是否想顯示菜單Menu * 判斷條件: * 1 抬起坐標大於按下坐標 * 2 menu本身不可見 */ private boolean wantToShowMenu() { return ((xUp - xDown > 0) && (!isMenuVisible)); } /** * 判斷是否應該將menu完整顯示出來 * 判斷條件: 滑動距離大於菜單的二分之一 * 或者滑動速度大於速度阈值VELOCITY_THRESHOLD */ private boolean shouldScrollToMenu() { return ((xUp - xDown > mMenuLayoutParams.width / 2) || (getScrollVelocity() > VELOCITY_THRESHOLD)); } /** * 將屏幕滾動到menu.即將menu完整顯示. */ public void scrollToMenu() { new ScrollAsyncTask().execute(-30); } /** * 判斷當前手勢是否想顯示菜單Content * 判斷條件: * 1 抬起坐標小於按下坐標 * 2 menu本身可見 */ private boolean wantToShowContent() { return ((xUp - xDown < 0) && (isMenuVisible)); } /** * 判斷是否應該將content完整顯示出來 * 判斷條件: 滑動距離大於菜單的二分之一 * 或者滑動速度大於速度阈值VELOCITY_THRESHOLD */ private boolean shouldScrollToContent() { return ((xDown - xUp > mMenuLayoutParams.width / 2) || (getScrollVelocity() > VELOCITY_THRESHOLD)); } /** * 將屏幕滾動到content.即將content完整顯示 */ public void scrollToContent() { new ScrollAsyncTask().execute(30); } public boolean isMenuVisible() { return isMenuVisible; } /** * 開始速度追蹤 */ private void startVelocityTracker(MotionEvent event) { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); } /** * 獲取在content上X方向的手指滑動速度 */ private int getScrollVelocity() { // 設置VelocityTracker單位.1000表示1秒時間內運動的像素 mVelocityTracker.computeCurrentVelocity(1000); // 獲取在1秒內X方向所滑動像素值 int xVelocity = (int) mVelocityTracker.getXVelocity(); return Math.abs(xVelocity); } /** * 終止速度追蹤 */ private void stopVelocityTracker() { if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } } /** * 利用異步任務不斷修改contentView的LayoutParams中的rightMargin從而達到 contentView視圖移動的效果 */ private class ScrollAsyncTask extends AsyncTask{ @Override protected Integer doInBackground(Integer... speed) { int contentLayoutParamsRightMargin = mContentLayoutParams.rightMargin; while (true) { // 每次變化的speed contentLayoutParamsRightMargin = contentLayoutParamsRightMargin + speed[0]; // 若越界,則處理越界且跳出循環 if (contentLayoutParamsRightMargin > contentParamsMaxRightMargin) { contentLayoutParamsRightMargin = contentParamsMaxRightMargin; break; } // 若越界,則處理越界且跳出循環 if (contentLayoutParamsRightMargin < contentParamsMinRightMargin) { contentLayoutParamsRightMargin = contentParamsMinRightMargin; break; } // 通知進度更新 publishProgress(contentLayoutParamsRightMargin); // 線程睡眠15毫秒,便於體現滾動效果 try { Thread.sleep(15); } catch (Exception e) { } } // 依據滑動的速度設置標志位isMenuVisible if (speed[0] > 0) { isMenuVisible = false; } else { isMenuVisible = true; } isSliding = false; return contentLayoutParamsRightMargin; } @Override protected void onProgressUpdate(Integer... rightMargin) { super.onProgressUpdate(rightMargin); mContentLayoutParams.rightMargin = rightMargin[0]; contentView.setLayoutParams(mContentLayoutParams); unFocusBindView(); } @Override protected void onPostExecute(Integer rightMargin) { super.onPostExecute(rightMargin); mContentLayoutParams.rightMargin = rightMargin; contentView.setLayoutParams(mContentLayoutParams); } } }
Android手勢解鎖密碼效果圖 首先呢想寫這個手勢密碼的想法呢,完全是憑空而來的,然後筆者就花了一天時間弄出
本文要演示的Android開發實例是如何完成一個Android中的miniTwitter登錄界面,下面將分步驟講解怎樣實現圖中的界面效果,讓大家都能輕松的做出美觀的登錄界
文件main.java復制代碼 代碼如下:package com.HHBrowser.android;import android.app.Activity;import
1 背景去年有很多人私信告訴我讓說說自定義控件,其實通觀網絡上的很多博客都在講各種自定義控件,但是大多數都是授之以魚,卻很少有較為系統性授之於漁的文章,同時由