編輯:關於Android編程
/** * 這個類和SlidingLayout作用一樣,只是沒有實現觸摸監聽事件,直接在121行設置實現了 */ public class SlidingLayout1 extends LinearLayout { /** * 滾動顯示和隱藏左側布局時,手指滑動需要達到的速度。 */ public static final int SNAP_VELOCITY = 200; /** * 屏幕寬度值。 */ private int screenWidth; /** * 左側布局最多可以滑動到的左邊緣。值由左側布局的寬度來定,marginLeft到達此值之後,不能再減少。 */ private int leftEdge; /** * 左側布局最多可以滑動到的右邊緣。值恆為0,即marginLeft到達0之後,不能增加。 */ private int rightEdge = 0; /** * 左側布局完全顯示時,留給右側布局的寬度值。 */ private int leftLayoutPadding = 80; /** * 記錄手指按下時的橫坐標。 */ private float xDown; /** * 記錄手指移動時的橫坐標。 */ private float xMove; /** * 記錄手機抬起時的橫坐標。 */ private float xUp; /** * 左側布局當前是顯示還是隱藏。只有完全顯示或隱藏時才會更改此值,滑動過程中此值無效。 */ private boolean isLeftLayoutVisible; /** * 左側布局對象。 */ private View leftLayout; /** * 右側布局對象。 */ private View rightLayout; /** * 用於監聽側滑事件的View。 */ private View mBindView; /** * 左側布局的參數,通過此參數來重新確定左側布局的寬度,以及更改leftMargin的值。 */ private MarginLayoutParams leftLayoutParams; /** * 右側布局的參數,通過此參數來重新確定右側布局的寬度。 */ private MarginLayoutParams rightLayoutParams; /** * 用於計算手指滑動的速度。 */ private VelocityTracker mVelocityTracker; /** * 重寫SlidingLayout的構造函數,其中獲取了屏幕的寬度。 * * @param context * @param attrs */ public SlidingLayout1(Context context, AttributeSet attrs) { super(context, attrs); WindowManager wm = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); DisplayMetrics metrics = new DisplayMetrics(); display.getMetrics(metrics); // 獲得屏幕寬和高 screenWidth = metrics.widthPixels; // screenWidth = wm.getDefaultDisplay().getWidth(); } /** * 綁定監聽側滑事件的View,即在綁定的View進行滑動才可以顯示和隱藏左側布局。 * * @param bindView 需要綁定的View對象。 */ public void setScrollEvent(View bindView) { mBindView = bindView; mBindView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { createVelocityTracker(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 手指按下時,記錄按下時的橫坐標 xDown = event.getRawX(); break; case MotionEvent.ACTION_MOVE: // 手指移動時,對比按下時的橫坐標,計算出移動的距離,來調整左側布局的leftMargin值,從而顯示和隱藏左側布局 xMove = event.getRawX(); int distanceX = (int) (xMove - xDown); if (isLeftLayoutVisible) { leftLayoutParams.leftMargin = distanceX; } else { leftLayoutParams.leftMargin = leftEdge + distanceX; } if (leftLayoutParams.leftMargin < leftEdge) { leftLayoutParams.leftMargin = leftEdge; } else if (leftLayoutParams.leftMargin > rightEdge) { leftLayoutParams.leftMargin = rightEdge; } leftLayout.setLayoutParams(leftLayoutParams); break; case MotionEvent.ACTION_UP: // 手指抬起時,進行判斷當前手勢的意圖,從而決定是滾動到左側布局,還是滾動到右側布局 xUp = event.getRawX(); if (wantToShowLeftLayout()) { if (shouldScrollToLeftLayout()) { scrollToLeftLayout(); } else { scrollToRightLayout(); } } else if (wantToShowRightLayout()) { if (shouldScrollToContent()) { scrollToRightLayout(); } else { scrollToLeftLayout(); } } recycleVelocityTracker(); break; } return isBindBasicLayout(); } }); } /** * 將屏幕滾動到左側布局界面,滾動速度設定為30. */ public void scrollToLeftLayout() { new ScrollTask().execute(30); } /** * 將屏幕滾動到右側布局界面,滾動速度設定為-30. */ public void scrollToRightLayout() { new ScrollTask().execute(-30); } /** * 左側布局是否完全顯示出來,或完全隱藏,滑動過程中此值無效。 * * @return 左側布局完全顯示返回true,完全隱藏返回false。 */ public boolean isLeftLayoutVisible() { return isLeftLayoutVisible; } /** * 在onLayout中重新設定左側布局和右側布局的參數。 */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (changed) { // 獲取左側布局對象 leftLayout = getChildAt(0); leftLayoutParams = (MarginLayoutParams) leftLayout .getLayoutParams(); // 重置左側布局對象的寬度為屏幕寬度減去leftLayoutPadding leftLayoutParams.width = screenWidth - leftLayoutPadding; // 設置最左邊距為負的左側布局的寬度 leftEdge = -leftLayoutParams.width; leftLayoutParams.leftMargin = leftEdge; leftLayout.setLayoutParams(leftLayoutParams); // 獲取右側布局對象 rightLayout = getChildAt(1); rightLayoutParams = (MarginLayoutParams) rightLayout .getLayoutParams(); rightLayoutParams.width = screenWidth; rightLayout.setLayoutParams(rightLayoutParams); } } /** * 判斷當前手勢的意圖是不是想顯示右側布局。如果手指移動的距離是負數,且當前左側布局是可見的,則認為當前手勢是想要顯示右側布局。 * * @return 當前手勢想顯示右側布局返回true,否則返回false。 */ private boolean wantToShowRightLayout() { return xUp - xDown < 0 && isLeftLayoutVisible; } /** * 判斷當前手勢的意圖是不是想顯示左側布局。如果手指移動的距離是正數,且當前左側布局是不可見的,則認為當前手勢是想要顯示左側布局。 * * @return 當前手勢想顯示左側布局返回true,否則返回false。 */ private boolean wantToShowLeftLayout() { return xUp - xDown > 0 && !isLeftLayoutVisible; } /** * 判斷是否應該滾動將左側布局展示出來。如果手指移動距離大於屏幕的1/2,或者手指移動速度大於SNAP_VELOCITY, * 就認為應該滾動將左側布局展示出來。 * * @return 如果應該滾動將左側布局展示出來返回true,否則返回false。 */ private boolean shouldScrollToLeftLayout() { return xUp - xDown > screenWidth / 2 || getScrollVelocity() > SNAP_VELOCITY; } /** * 判斷是否應該滾動將右側布局展示出來。如果手指移動距離加上leftLayoutPadding大於屏幕的1/2, * 或者手指移動速度大於SNAP_VELOCITY, 就認為應該滾動將右側布局展示出來。 * * @return 如果應該滾動將右側布局展示出來返回true,否則返回false。 */ private boolean shouldScrollToContent() { return xDown - xUp + leftLayoutPadding > screenWidth / 2 || getScrollVelocity() > SNAP_VELOCITY; } /** * 判斷綁定滑動事件的View是不是一個基礎layout,不支持自定義layout,只支持四種基本layout, * AbsoluteLayout已被棄用。 * * @return 如果綁定滑動事件的View是LinearLayout, RelativeLayout, FrameLayout, * TableLayout之一就返回true,否則返回false。 */ private boolean isBindBasicLayout() { if (mBindView == null) { return false; } String viewName = mBindView.getClass().getName(); return viewName.equals(LinearLayout.class.getName()) || viewName.equals(RelativeLayout.class.getName()) || viewName.equals(FrameLayout.class.getName()) || viewName.equals(TableLayout.class.getName()); } /** * 創建VelocityTracker對象,並將觸摸事件加入到VelocityTracker當中。 * * @param event 右側布局監聽控件的滑動事件 */ private void createVelocityTracker(MotionEvent event) { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); } /** * 獲取手指在右側布局的監聽View上的滑動速度。 * * @return 滑動速度,以每秒鐘移動了多少像素值為單位。 */ private int getScrollVelocity() { mVelocityTracker.computeCurrentVelocity(1000); int velocity = (int) mVelocityTracker.getXVelocity(); return Math.abs(velocity); } /** * 回收VelocityTracker對象。 */ private void recycleVelocityTracker() { mVelocityTracker.recycle(); mVelocityTracker = null; } class ScrollTask extends AsyncTask首先在onLayout()方法中分別獲取到左側菜單、右側菜單和內容布局的參數,並將內容布局的寬度重定義成屏幕的寬度,這樣就可以保證內容 布局既能覆蓋住下面的菜單布局,還能偏移出屏幕。然後在onTouch()方法中監聽觸屏事件,以判斷用戶手勢的意圖。這裡事先定義好了幾種滑動狀 態,DO_NOTHING表示沒有進行任何滑動,SHOW_LEFT_MENU表示用戶想要滑出左側菜單,SHOW_RIGHT_MENU表示用戶想要滑 出右側菜單,HIDE_LEFT_MENU表示用戶想要隱藏左側菜單,HIDE_RIGHT_MENU表示用戶想要隱藏右側菜單,在 checkSlideState()方法中判斷出用戶到底是想進行哪一種滑動操作,並給slideState變量賦值,然後根據slideState的值 決定如何偏移內容布局。接著當用戶手指離開屏幕時,會根據當前的滑動距離,決定後續的滾動方向,通過LeftMenuScrollTask和 RightMenuScrollTask來完成完整的滑動過程。另外在滑動的過程,內容布局上的事件會被屏蔽掉,主要是通過一系列的return操作實現。 在布局中引用{ @Override protected Integer doInBackground(Integer... speed) { int leftMargin = leftLayoutParams.leftMargin; // 根據傳入的速度來滾動界面,當滾動到達左邊界或右邊界時,跳出循環。 while (true) { leftMargin = leftMargin + speed[0]; if (leftMargin > rightEdge) { leftMargin = rightEdge; break; } if (leftMargin < leftEdge) { leftMargin = leftEdge; break; } publishProgress(leftMargin); // 為了要有滾動效果產生,每次循環使線程睡眠20毫秒,這樣肉眼才能夠看到滾動動畫。 try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } } if (speed[0] > 0) { isLeftLayoutVisible = true; } else { isLeftLayoutVisible = false; } return leftMargin; } @Override protected void onProgressUpdate(Integer... leftMargin) { leftLayoutParams.leftMargin = leftMargin[0]; leftLayout.setLayoutParams(leftLayoutParams); } @Override protected void onPostExecute(Integer leftMargin) { leftLayoutParams.leftMargin = leftMargin; leftLayout.setLayoutParams(leftLayoutParams); } } }
使用了自定義的SlidingLayout2作為根布局,然後依次加入了三個子布局分別作為左側菜單、右側菜單和內容的布局。左側菜單和右側菜單中都只是簡單地放入了一個TextView用於顯示一段文字,內容布局中放入了一個ListView。注意要讓左側菜單和父布局左邊緣對齊,右側菜單和父布局右邊緣對齊。 用法也是參照上篇的寫法,我也寫在了一個Fragment中:xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/slidingLayout_fragment_double" android:layout_width="match_parent" android:layout_height="match_parent" > android:layout_width="280dip" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:background="#00ccff" android:visibility="invisible" > android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="This is left menu" android:textColor="#000000" android:textSize="28sp" /> android:layout_width="280dip" android:layout_height="match_parent" android:layout_alignParentRight="true" android:background="#00ffcc" android:visibility="invisible" > android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="This is right menu" android:textColor="#000000" android:textSize="28sp" /> android:id="@+id/content" android:layout_width="320dip" android:layout_height="match_parent" android:orientation="vertical" android:background="#e9e9e9" > android:layout_width="match_parent" android:layout_height="wrap_content" >
public class DoubleMenu extends Fragment { /** * 雙向滑動菜單布局 */ private SlidingLayout2 doubleSldingLayout; /** * 在內容布局上顯示的ListView */ private ListView contentList; /** * ListView的適配器 */ private ArrayAdaptercontentListAdapter; /** * 用於填充contentListAdapter的數據源。 */ private List dataList; @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_double, container, false); doubleSldingLayout = (SlidingLayout2) view.findViewById(R.id.slidingLayout_fragment_double); Button showLeftButton = (Button) view.findViewById(R.id.show_left_button); Button showRightButton = (Button) view.findViewById(R.id.show_right_button); contentList = (ListView) view.findViewById(R.id.contentList); dataList=new ArrayList<>(); for (int i=0;i<15;i++) dataList.add("Item "+i); contentListAdapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, dataList); contentList.setAdapter(contentListAdapter); doubleSldingLayout.setScrollEvent(contentList); showLeftButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (doubleSldingLayout.isLeftLayoutVisible()) { doubleSldingLayout.scrollToContentFromLeftMenu(); } else { doubleSldingLayout.initShowLeftState(); doubleSldingLayout.scrollToLeftMenu(); } } }); showRightButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (doubleSldingLayout.isRightLayoutVisible()) { doubleSldingLayout.scrollToContentFromRightMenu(); } else { doubleSldingLayout.initShowRightState(); doubleSldingLayout.scrollToRightMenu(); } } }); return view; } }
Android是消息驅動的,實現消息驅動有幾個要素:消息的表示:Message消息隊列:MessageQueue消息循環,用於循環取出消息進行處理:Looper消息處理,
概要 當手機Modem狀態改變後會將狀態變化信息通知到上層,通過《Android 4.4 Kitkat Phone工作流程淺析(八)__Phone狀態分析》
仿QQ消息列表item橫向滑動刪除ListView中item側滑刪除在最近的項目中,我的ListView中item選項是長按刪除的效果(Android的通常做法長按或點擊
在沒給大家分享代碼之前,先給大家展示下效果圖:1.activity/** * 采購需求 * Created by Administrator on 2016/10/13.