編輯:關於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 ArrayAdapter mArrayAdapter;
@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開發實例之登錄界面的實現
本文要演示的Android開發實例是如何完成一個Android中的miniTwitter登錄界面,下面將分步驟講解怎樣實現圖中的界面效果,讓大家都能輕松的做出美觀的登錄界
android webview 簡單浏覽器實現代碼
文件main.java復制代碼 代碼如下:package com.HHBrowser.android;import android.app.Activity;import
深入淺析Android坐標系統
1 背景去年有很多人私信告訴我讓說說自定義控件,其實通觀網絡上的很多博客都在講各種自定義控件,但是大多數都是授之以魚,卻很少有較為系統性授之於漁的文章,同時由