實現自定義view(2):仿Android QQ多螢幕顯示ListView的效果
阿新 • • 發佈:2019-01-07
本文在《仿 UC,墨跡天氣左右拖動 多螢幕顯示效果》的基礎上對程式碼進行修改,模仿Android QQ主介面的分屏ListView滑動效果。
當進行橫向滑動時,會切換螢幕,當縱向滑動時,ListView會滾動。
效果圖如下:
程式碼如下:
FlingGallery.java
FlingGalleryActivity.javapackage com.droidful.flinggallery; import android.content.Context; import android.util.Log; import android.view.GestureDetector; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.view.animation.Transformation; import android.widget.Adapter; import android.widget.FrameLayout; import android.widget.LinearLayout; // TODO: // 1. In order to improve performance Cache screen bitmap and use for animation // 2. Establish superfluous memory allocations and delay or replace with reused objects // Probably need to make sure we are not allocating objects (strings, etc.) in loops public class FlingGallery extends FrameLayout { // Constants private final int swipe_min_distance = 120; private final int swipe_max_off_path = 250; private final int swipe_threshold_veloicty = 400; // Properties private int mViewPaddingWidth = 0; private int mAnimationDuration = 250; private float mSnapBorderRatio = 0.5f; private boolean mIsGalleryCircular = true; private int mDetectScrollX = 50; // Members private int mGalleryWidth = 0; private boolean mIsTouched = false; private boolean mIsDragging = false; private float mCurrentOffset = 0.0f; private long mScrollTimestamp = 0; private int mFlingDirection = 0; private int mCurrentPosition = 0; private int mCurrentViewNumber = 0; private Context mContext; private Adapter mAdapter; private FlingGalleryView[] mViews; private FlingGalleryAnimation mAnimation; private GestureDetector mGestureDetector; private FlingGestureDetectorListener mGestureDetectorListener; private GestureDetector mInterruptDetector; private Interpolator mDecelerateInterpolater; public FlingGallery(Context context) { super(context); mContext = context; mAdapter = null; mViews = new FlingGalleryView[3]; mViews[0] = new FlingGalleryView(0, this); mViews[1] = new FlingGalleryView(1, this); mViews[2] = new FlingGalleryView(2, this); mAnimation = new FlingGalleryAnimation(); mGestureDetectorListener = new FlingGestureDetectorListener(); mGestureDetector = new GestureDetector(mGestureDetectorListener); mInterruptDetector = new GestureDetector(new InterruptGestureDetectorListener()); mDecelerateInterpolater = AnimationUtils.loadInterpolator(mContext, android.R.anim.decelerate_interpolator); } public void setPaddingWidth(int viewPaddingWidth) { mViewPaddingWidth = viewPaddingWidth; } public void setAnimationDuration(int animationDuration) { mAnimationDuration = animationDuration; } public void setSnapBorderRatio(float snapBorderRatio) { mSnapBorderRatio = snapBorderRatio; } public void setIsGalleryCircular(boolean isGalleryCircular) { if (mIsGalleryCircular != isGalleryCircular) { mIsGalleryCircular = isGalleryCircular; if (mCurrentPosition == getFirstPosition()) { // We need to reload the view immediately to the left to change it to circular view or blank mViews[getPrevViewNumber(mCurrentViewNumber)].recycleView(getPrevPosition(mCurrentPosition)); } if (mCurrentPosition == getLastPosition()) { // We need to reload the view immediately to the right to change it to circular view or blank mViews[getNextViewNumber(mCurrentViewNumber)].recycleView(getNextPosition(mCurrentPosition)); } } } public int getGalleryCount() { return (mAdapter == null) ? 0 : mAdapter.getCount(); } public int getFirstPosition() { return 0; } @Override public boolean onInterceptTouchEvent (MotionEvent ev) { return mInterruptDetector.onTouchEvent(ev); } @Override public boolean onTouchEvent (MotionEvent ev) { Log.d("Test", "test" ); boolean value = onGalleryTouchEvent(ev); Log.d("Test", "" + value); return true; } public int getLastPosition() { return (getGalleryCount() == 0) ? 0 : getGalleryCount() - 1; } private int getPrevPosition(int relativePosition) { int prevPosition = relativePosition - 1; if (prevPosition < getFirstPosition()) { prevPosition = getFirstPosition() - 1; if (mIsGalleryCircular == true) { prevPosition = getLastPosition(); } } return prevPosition; } private int getNextPosition(int relativePosition) { int nextPosition = relativePosition + 1; if (nextPosition > getLastPosition()) { nextPosition = getLastPosition() + 1; if (mIsGalleryCircular == true) { nextPosition = getFirstPosition(); } } return nextPosition; } private int getPrevViewNumber(int relativeViewNumber) { return (relativeViewNumber == 0) ? 2 : relativeViewNumber - 1; } private int getNextViewNumber(int relativeViewNumber) { return (relativeViewNumber == 2) ? 0 : relativeViewNumber + 1; } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); // Calculate our view width mGalleryWidth = right - left; if (changed == true) { // Position views at correct starting offsets mViews[0].setOffset(0, 0, mCurrentViewNumber); mViews[1].setOffset(0, 0, mCurrentViewNumber); mViews[2].setOffset(0, 0, mCurrentViewNumber); } } public void setAdapter(Adapter adapter) { mAdapter = adapter; mCurrentPosition = 0; mCurrentViewNumber = 0; // Load the initial views from adapter mViews[0].recycleView(mCurrentPosition); mViews[1].recycleView(getNextPosition(mCurrentPosition)); mViews[2].recycleView(getPrevPosition(mCurrentPosition)); // Position views at correct starting offsets mViews[0].setOffset(0, 0, mCurrentViewNumber); mViews[1].setOffset(0, 0, mCurrentViewNumber); mViews[2].setOffset(0, 0, mCurrentViewNumber); } private int getViewOffset(int viewNumber, int relativeViewNumber) { // Determine width including configured padding width int offsetWidth = mGalleryWidth + mViewPaddingWidth; // Position the previous view one measured width to left if (viewNumber == getPrevViewNumber(relativeViewNumber)) { return offsetWidth; } // Position the next view one measured width to the right if (viewNumber == getNextViewNumber(relativeViewNumber)) { return offsetWidth * -1; } return 0; } void movePrevious() { // Slide to previous view mFlingDirection = 1; processGesture(); } void moveNext() { // Slide to next view mFlingDirection = -1; processGesture(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_DPAD_LEFT: movePrevious(); return true; case KeyEvent.KEYCODE_DPAD_RIGHT: moveNext(); return true; case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_ENTER: } return super.onKeyDown(keyCode, event); } public boolean onGalleryTouchEvent(MotionEvent event) { boolean consumed = mGestureDetector.onTouchEvent(event); if (event.getAction() == MotionEvent.ACTION_UP) { if (mIsTouched || mIsDragging) { processScrollSnap(); processGesture(); } } return consumed; } void processGesture() { int newViewNumber = mCurrentViewNumber; int reloadViewNumber = 0; int reloadPosition = 0; mIsTouched = false; mIsDragging = false; if (mFlingDirection > 0) { if (mCurrentPosition > getFirstPosition() || mIsGalleryCircular == true) { // Determine previous view and outgoing view to recycle newViewNumber = getPrevViewNumber(mCurrentViewNumber); mCurrentPosition = getPrevPosition(mCurrentPosition); reloadViewNumber = getNextViewNumber(mCurrentViewNumber); reloadPosition = getPrevPosition(mCurrentPosition); } } if (mFlingDirection < 0) { if (mCurrentPosition < getLastPosition() || mIsGalleryCircular == true) { // Determine the next view and outgoing view to recycle newViewNumber = getNextViewNumber(mCurrentViewNumber); mCurrentPosition = getNextPosition(mCurrentPosition); reloadViewNumber = getPrevViewNumber(mCurrentViewNumber); reloadPosition = getNextPosition(mCurrentPosition); } } if (newViewNumber != mCurrentViewNumber) { mCurrentViewNumber = newViewNumber; // Reload outgoing view from adapter in new position mViews[reloadViewNumber].recycleView(reloadPosition); } // Ensure input focus on the current view mViews[mCurrentViewNumber].requestFocus(); // Run the slide animations for view transitions mAnimation.prepareAnimation(mCurrentViewNumber); this.startAnimation(mAnimation); // Reset fling state mFlingDirection = 0; } void processScrollSnap() { // Snap to next view if scrolled passed snap position float rollEdgeWidth = mGalleryWidth * mSnapBorderRatio; int rollOffset = mGalleryWidth - (int) rollEdgeWidth; int currentOffset = mViews[mCurrentViewNumber].getCurrentOffset(); if (currentOffset <= rollOffset * -1) { // Snap to previous view mFlingDirection = 1; } if (currentOffset >= rollOffset) { // Snap to next view mFlingDirection = -1; } } private class FlingGalleryView { private int mViewNumber; private FrameLayout mParentLayout; private FrameLayout mInvalidLayout = null; private LinearLayout mInternalLayout = null; private View mExternalView = null; public FlingGalleryView(int viewNumber, FrameLayout parentLayout) { mViewNumber = viewNumber; mParentLayout = parentLayout; // Invalid layout is used when outside gallery mInvalidLayout = new FrameLayout(mContext); mInvalidLayout.setLayoutParams(new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); // Internal layout is permanent for duration mInternalLayout = new LinearLayout(mContext); mInternalLayout.setLayoutParams(new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); mParentLayout.addView(mInternalLayout); } public void recycleView(int newPosition) { if (mExternalView != null) { mInternalLayout.removeView(mExternalView); } if (mAdapter != null) { if (newPosition >= getFirstPosition() && newPosition <= getLastPosition()) { mExternalView = mAdapter.getView(newPosition, mExternalView, mInternalLayout); } else { mExternalView = mInvalidLayout; } } if (mExternalView != null) { mInternalLayout.addView(mExternalView, new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); } } public void setOffset(int xOffset, int yOffset, int relativeViewNumber) { // Scroll the target view relative to its own position relative to currently displayed view mInternalLayout.scrollTo(getViewOffset(mViewNumber, relativeViewNumber) + xOffset, yOffset); } public int getCurrentOffset() { // Return the current scroll position return mInternalLayout.getScrollX(); } public void requestFocus() { mInternalLayout.requestFocus(); } } private class FlingGalleryAnimation extends Animation { private boolean mIsAnimationInProgres; private int mRelativeViewNumber; private int mInitialOffset; private int mTargetOffset; private int mTargetDistance; public FlingGalleryAnimation() { mIsAnimationInProgres = false; mRelativeViewNumber = 0; mInitialOffset = 0; mTargetOffset = 0; mTargetDistance = 0; } public void prepareAnimation(int relativeViewNumber) { // If we are animating relative to a new view if (mRelativeViewNumber != relativeViewNumber) { if (mIsAnimationInProgres == true) { // We only have three views so if requested again to animate in same direction we must snap int newDirection = (relativeViewNumber == getPrevViewNumber(mRelativeViewNumber)) ? 1 : -1; int animDirection = (mTargetDistance < 0) ? 1 : -1; // If animation in same direction if (animDirection == newDirection) { // Ran out of time to animate so snap to the target offset mViews[0].setOffset(mTargetOffset, 0, mRelativeViewNumber); mViews[1].setOffset(mTargetOffset, 0, mRelativeViewNumber); mViews[2].setOffset(mTargetOffset, 0, mRelativeViewNumber); } } // Set relative view number for animation mRelativeViewNumber = relativeViewNumber; } // Note: In this implementation the targetOffset will always be zero // as we are centering the view; but we include the calculations of // targetOffset and targetDistance for use in future implementations mInitialOffset = mViews[mRelativeViewNumber].getCurrentOffset(); mTargetOffset = getViewOffset(mRelativeViewNumber, mRelativeViewNumber); mTargetDistance = mTargetOffset - mInitialOffset; // Configure base animation properties this.setDuration(mAnimationDuration); this.setInterpolator(mDecelerateInterpolater); // Start/continued animation mIsAnimationInProgres = true; } @Override protected void applyTransformation(float interpolatedTime, Transformation transformation) { // Ensure interpolatedTime does not over-shoot then calculate new offset interpolatedTime = (interpolatedTime > 1.0f) ? 1.0f : interpolatedTime; int offset = mInitialOffset + (int) (mTargetDistance * interpolatedTime); for (int viewNumber = 0; viewNumber < 3; viewNumber++) { // Only need to animate the visible views as the other view will always be off-screen if ((mTargetDistance > 0 && viewNumber != getNextViewNumber(mRelativeViewNumber)) || (mTargetDistance < 0 && viewNumber != getPrevViewNumber(mRelativeViewNumber))) { mViews[viewNumber].setOffset(offset, 0, mRelativeViewNumber); } } } @Override public boolean getTransformation(long currentTime, Transformation outTransformation) { if (super.getTransformation(currentTime, outTransformation) == false) { // Perform final adjustment to offsets to cleanup animation mViews[0].setOffset(mTargetOffset, 0, mRelativeViewNumber); mViews[1].setOffset(mTargetOffset, 0, mRelativeViewNumber); mViews[2].setOffset(mTargetOffset, 0, mRelativeViewNumber); // Reached the animation target mIsAnimationInProgres = false; return false; } // Cancel if the screen touched if (mIsTouched || mIsDragging) { // Note that at this point we still consider ourselves to be animating // because we have not yet reached the target offset; its just that the // user has temporarily interrupted the animation with a touch gesture return false; } return true; } } private class InterruptGestureDetectorListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { // Stop animation mIsTouched = true; // Reset fling state mFlingDirection = 0; return false; } //返回true由FlingGallery的onTouchEvent(MotionEvent)來處理觸控訊息,返回false則先由子view(listview)來處理觸控訊息 @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { if (e2.getAction() == MotionEvent.ACTION_MOVE) { float maxVelocity = mGalleryWidth / (mAnimationDuration / 1000.0f); long timestampDelta = System.currentTimeMillis() - mScrollTimestamp; float maxScrollDelta = maxVelocity * (timestampDelta / 1000.0f); float currentScrollDelta = e1.getX() - e2.getX(); if (currentScrollDelta < maxScrollDelta * -1) currentScrollDelta = maxScrollDelta * -1; if (currentScrollDelta > maxScrollDelta) currentScrollDelta = maxScrollDelta; if(Math.abs(currentScrollDelta) > mDetectScrollX){//如果當前x方向滾動的距離大於50,則由FlingGallery的onTouchEvent(MotionEvent)來處理觸控訊息 mGestureDetectorListener.setDownEvent(e1);//重新設定mGestureDetectorListener的down訊息 return true; } } return false;//不打斷,交給子view來處理觸控訊息 } } private class FlingGestureDetectorListener extends GestureDetector.SimpleOnGestureListener { private MotionEvent mDownEvent; public void setDownEvent(MotionEvent downEvent) {//復原丟失的Down觸控訊息 mDownEvent = downEvent; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { if (e2.getAction() == MotionEvent.ACTION_MOVE) { if (mIsDragging == false) { // Stop animation mIsTouched = true; // Reconfigure scroll mIsDragging = true; mFlingDirection = 0; mScrollTimestamp = System.currentTimeMillis(); mCurrentOffset = mViews[mCurrentViewNumber].getCurrentOffset(); } float maxVelocity = mGalleryWidth / (mAnimationDuration / 1000.0f); long timestampDelta = System.currentTimeMillis() - mScrollTimestamp; float maxScrollDelta = maxVelocity * (timestampDelta / 1000.0f); float currentScrollDelta = mDownEvent.getX() - e2.getX(); if (currentScrollDelta < maxScrollDelta * -1) currentScrollDelta = maxScrollDelta * -1; if (currentScrollDelta > maxScrollDelta) currentScrollDelta = maxScrollDelta; int scrollOffset = Math.round(mCurrentOffset + currentScrollDelta); // We can't scroll more than the width of our own frame layout if (scrollOffset >= mGalleryWidth) scrollOffset = mGalleryWidth; if (scrollOffset <= mGalleryWidth * -1) scrollOffset = mGalleryWidth * -1; mViews[0].setOffset(scrollOffset, 0, mCurrentViewNumber); mViews[1].setOffset(scrollOffset, 0, mCurrentViewNumber); mViews[2].setOffset(scrollOffset, 0, mCurrentViewNumber); } return false; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { if (Math.abs(mDownEvent.getY() - e2.getY()) <= swipe_max_off_path) { if (e2.getX() - mDownEvent.getX() > swipe_min_distance && Math.abs(velocityX) > swipe_threshold_veloicty) { movePrevious(); } if(mDownEvent.getX() - e2.getX() > swipe_min_distance && Math.abs(velocityX) > swipe_threshold_veloicty) { moveNext(); } } return false; } @Override public void onLongPress(MotionEvent e) { // Finalise scrolling mFlingDirection = 0; processGesture(); } @Override public void onShowPress(MotionEvent e) { } @Override public boolean onSingleTapUp(MotionEvent e) { // Reset fling state mFlingDirection = 0; return false; } } }
package com.droidful.flinggallery; import android.app.Activity; import android.os.Bundle; import android.content.Context; import android.graphics.Color; import android.util.Log; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.View.OnClickListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TableLayout; import android.widget.TextView; public class FlingGalleryActivity extends Activity { private final String[] mLabelArray = {"View1", "View2", "View3"}; private String[] mAStrings = { "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi", "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese", "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell", "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc", "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr"}; private String[] mBStrings = {"Baby Swiss", "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon", "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase", "Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese", "Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy", "Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille", "Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore", "Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)", "Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves", "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur", "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon", "Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin", "Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)", "Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine", "Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza", "Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)", "Buxton Blue", }; private String[] mCStrings = {"Cabecou", "Caboc", "Cabrales", "Cachaille", "Caciocavallo", "Caciotta", "Caerphilly", "Cairnsmore", "Calenzana", "Cambazola", "Camembert de Normandie", "Canadian Cheddar", "Canestrato", "Cantal", "Caprice des Dieux", "Capricorn Goat", "Capriole Banon", "Carre de l'Est", "Casciotta di Urbino", "Cashel Blue", "Castellano", "Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain", "Celtic Promise", "Cendre d'Olivet", "Cerney", "Chabichou", "Chabichou du Poitou", "Chabis de Gatine", "Chaource", "Charolais", "Chaumes", "Cheddar", "Cheddar Clothbound", "Cheshire", "Chevres", "Chevrotin des Aravis", "Chontaleno", "Civray", "Coeur de Camembert au Calvados", "Coeur de Chevre", "Colby", "Cold Pack", "Comte", "Coolea", "Cooleney", "Coquetdale", "Corleggy", "Cornish Pepper", "Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)", "Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese", "Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza", "Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley", "Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino", "Cypress Grove Chevre"}; private String[][] strings = {mAStrings,mBStrings,mCStrings}; private FlingGallery mGallery; private CheckBox mCheckBox; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mGallery = new FlingGallery(this); mGallery.setPaddingWidth(5); mGallery.setAdapter(new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, mLabelArray) { @Override public View getView(int position, View convertView, ViewGroup parent) { ListView lv = new ListView(getApplicationContext()); lv.setAdapter(new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, strings[position])); return lv;//返回position位置的listview } }); LinearLayout layout = new LinearLayout(getApplicationContext()); layout.setOrientation(LinearLayout.VERTICAL); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); layoutParams.setMargins(10, 10, 10, 10); layoutParams.weight = 1.0f; layout.addView(mGallery, layoutParams); mCheckBox = new CheckBox(getApplicationContext()); mCheckBox.setText("Gallery is Circular"); mCheckBox.setPadding(50, 10, 0, 10); mCheckBox.setTextSize(30); mCheckBox.setChecked(false); mGallery.setIsGalleryCircular(mCheckBox.isChecked()); mCheckBox.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { mGallery.setIsGalleryCircular(mCheckBox.isChecked()); } }); layout.addView(mCheckBox, new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); setContentView(layout); } }
程式碼解析:
這裡不詳細解析分屏效果的實現,只說明ViewGroup對觸控訊息的分發規則。
FlingGallery繼承FrameLayout,FrameLayout的父類是ViewGroup,因此FlingGallery是ViewGroup的派生類。
預設規則下,當觸控訊息(MotionEvent)到達ViewGroup時,ViewGroup會先把訊息發給子View的onTouchEvent(MotionEvent)方法進行處理,如果子View消耗了訊息,那麼ViewGroup的onTouchEvent(MotionEvent)將不會接收到訊息。
這個例子我們過載了ViewGroup的onInterceptTouchEvent (MotionEvent ev)方法,如果方法返回true則表示交由ViewGroup的onTouchEvent(MotionEvent)來處理觸控訊息,false則按預設規則交由子view處理。我們在此方法中檢測是否橫向滾動,如果是橫向滾動,則返回true,訊息傳遞給ViewGroup的onTouchEvent(MotionEvent),如果不是橫向滑動則有子view處理訊息。
希望覺得有用的同學都頂一下,寫部落格不容易!