文章出处:http://www.eoeandroid.com/blog-499812-2267.html
Java代码
import uk.co.jasonfry.android.tools.ui.PageControl.OnPageControlClickListener;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
public class SwipeView extends HorizontalScrollView
{
private static int DEFAULT_SWIPE_THRESHOLD = 60;
private LinearLayout mLinearLayout;
private Context mContext;
private int SCREEN_WIDTH;
private int mMotionStartX;
private int mMotionStartY;
private boolean mMostlyScrollingInX = false;
private boolean mMostlyScrollingInY = false;
private boolean mJustInterceptedAndIgnored = false;
protected boolean mCallScrollToPageInOnLayout = false;
private int mCurrentPage = 0;
private int mPageWidth = 0;
private OnPageChangedListener mOnPageChangedListener = null;
private SwipeOnTouchListener mSwipeOnTouchListener;
private View.OnTouchListener mOnTouchListener;
private PageControl mPageControl = null;
/**
* {@inheritDoc}
*/
public SwipeView(Context context)
{
super(context);
mContext = context;
initSwipeView();
}
/**
* {@inheritDoc}
*/
public SwipeView(Context context, AttributeSet attrs)
{
super(context, attrs);
mContext = context;
initSwipeView();
}
/**
* {@inheritDoc}
*/
public SwipeView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs,defStyle);
mContext = context;
initSwipeView();
}
private void initSwipeView()
{
Log.i("uk.co.jasonfry.android.tools.ui.SwipeView","Initialising SwipeView");
mLinearLayout = new LinearLayout(mContext);
mLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
super.addView(mLinearLayout, -1, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
setSmoothScrollingEnabled(true);
setHorizontalFadingEdgeEnabled(false);
setHorizontalScrollBarEnabled(false);
Display display = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
SCREEN_WIDTH = (int) (display.getWidth());
mPageWidth = SCREEN_WIDTH;
mCurrentPage = 0;
mSwipeOnTouchListener = new SwipeOnTouchListener();
super.setOnTouchListener(mSwipeOnTouchListener);
}
/**
* {@inheritDoc}
*/
@Override
public boolean onTrackballEvent(MotionEvent event)
{
return true;
}
@Override
protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)
{
//this will now pass trackball events down to onTrackballEvent
return false;
}
@Override
public void requestChildFocus(View child, View focused)
{
//this will now pass trackball events down to onRequestFocusInDescendants
requestFocus();
}
/**
* {@inheritDoc}
*/
@Override
public void addView(View child)
{
this.addView(child,-1);
}
/**
* {@inheritDoc}
*/
@Override
public void addView (View child, int index)
{
ViewGroup.LayoutParams params;
if(child.getLayoutParams()==null)
{
params = new LayoutParams(mPageWidth, LayoutParams.FILL_PARENT);
}
else
{
params = child.getLayoutParams();
params.width = mPageWidth;
}
this.addView(child, index, params);
}
/**
* {@inheritDoc}
*/
@Override
public void addView (View child, ViewGroup.LayoutParams params)
{
params.width = mPageWidth;
this.addView (child, -1, params);
}
/**
* {@inheritDoc}
*/
@Override
public void addView (View child, int index, ViewGroup.LayoutParams params)
{
requestLayout();
invalidate();
mLinearLayout.addView(child, index, params);
}
/**
* {@inheritDoc}
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
super.onLayout(changed, l, t, r, b);
if(mCallScrollToPageInOnLayout)
{
scrollToPage(mCurrentPage);
mCallScrollToPageInOnLayout = false;
}
}
/**
* {@inheritDoc}
*/
@Override
public void setOnTouchListener(View.OnTouchListener onTouchListener)
{
mOnTouchListener = onTouchListener;
}
/**
* Get the View object that contains all the children of this SwipeView. The same as calling getChildAt(0)
* A SwipeView behaves slightly differently from a normal ViewGroup, all the children of a SwipeView
* sit within a LinearLayout, which then sits within the SwipeView object.
*
* @return linearLayout The View object that contains all the children of this view
*/
public LinearLayout getChildContainer()
{
return mLinearLayout;
}
/**
* Get the swiping threshold distance to make the screens change
*
* @return swipeThreshold The minimum distance the finger should move to allow the screens to change
*/
public int getSwipeThreshold()
{
return DEFAULT_SWIPE_THRESHOLD;
}
/**
* Set the swiping threshold distance to make the screens change
*
* @param swipeThreshold The minimum distance the finger should move to allow the screens to change
*/
public void setSwipeThreshold(int swipeThreshold)
{
DEFAULT_SWIPE_THRESHOLD = swipeThreshold;
}
/**
* Get the current page the SwipeView is on
*
* @return The current page the SwipeView is on
*/
public int getCurrentPage()
{
return mCurrentPage;
}
/**
* Return the number of pages in this SwipeView
*
* @return Returns the number of pages in this SwipeView
*/
public int getPageCount()
{
return mLinearLayout.getChildCount();
}
/**
* Go directly to the specified page
*
* @param page The page to scroll to
*/
public void scrollToPage(int page)
{
scrollToPage(page,false);
}
/**
* Animate a scroll to the specified page
*
* @param page The page to animate to
*/
public void smoothScrollToPage(int page)
{
scrollToPage(page,true);
}
private void scrollToPage(int page, boolean smooth)
{
int oldPage = mCurrentPage;
if(page>=getPageCount() && getPageCount()>0)
{
page--;
}
else if(page<0)
{
page=0;
}
if(smooth)
{
smoothScrollTo(page*mPageWidth,0);
}
else
{
scrollTo(page*mPageWidth,0);
}
mCurrentPage = page;
if(mOnPageChangedListener!=null && oldPage!=page)
{
mOnPageChangedListener.onPageChanged(oldPage, page);
}
if(mPageControl!=null && oldPage!=page)
{
mPageControl.setCurrentPage(page);
}
mCallScrollToPageInOnLayout=!mCallScrollToPageInOnLayout;
}
/**
* Set the width of each page. This function returns an integer that should be added to the left margin of
* the first child and the right margin of the last child. This enables all the children to appear to be
* central
*
* @param pageWidth The width you wish to assign for each page
* @return An integer to add to the left margin of the first child and the right margin of the last child
*/
public int setPageWidth(int pageWidth)
{
mPageWidth = pageWidth;
return (SCREEN_WIDTH - mPageWidth)/2;
}
/**
* Set the width of each page by using the layout parameters of a child. Call this function before you add
* the child to the SwipeView to maintain the child's size. This function returns an integer that should
* be added to the left margin of the first child and the right margin of the last child. This enables all
* the children to appear to be central
*
* @param childLayoutParams A child view that you have added / will add to the SwipeView
* @return An integer to add to the left margin of the first child and the right margin of the last child
*/
public int calculatePageSize(MarginLayoutParams childLayoutParams)
{
return setPageWidth(childLayoutParams.leftMargin + childLayoutParams.width + childLayoutParams.rightMargin);
}
/**
* Return the current width of each page
*
* @return Returns the width of each page
*/
public int getPageWidth()
{
return mPageWidth;
}
/**
* Assign a PageControl object to this SwipeView. Call after adding all the children
*
* @param pageControl The PageControl object to assign
*/
public void setPageControl(PageControl pageControl)
{
mPageControl = pageControl;
pageControl.setPageCount(getPageCount());
pageControl.setCurrentPage(mCurrentPage);
pageControl.setOnPageControlClickListener(new OnPageControlClickListener()
{
public void goForwards()
{
smoothScrollToPage(mCurrentPage+1);
}
public void goBackwards()
{
smoothScrollToPage(mCurrentPage-1);
}
});
}
/**
* Return the current PageControl object
*
* @return Returns the current PageControl object
*/
public PageControl getPageControl()
{
return mPageControl;
}
/**
* Implement this listener to listen for page change events
*
* @author Jason Fry - jasonfry.co.uk
*
*/
public interface OnPageChangedListener
{
/**
* Event for when a page changes
*
* @param oldPage The page the view was on previously
* @param newPage The page the view has moved to
*/
public abstract void onPageChanged(int oldPage, int newPage);
}
/**
* Set the current OnPageChangedListsner
*
* @param onPageChangedListener The OnPageChangedListener object
*/
public void setOnPageChangedListener(OnPageChangedListener onPageChangedListener)
{
mOnPageChangedListener = onPageChangedListener;
}
/**
* Get the current OnPageChangeListsner
*
* @return The current OnPageChangedListener
*/
public OnPageChangedListener getOnPageChangedListener()
{
return mOnPageChangedListener;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev)
{
boolean result = super.onInterceptTouchEvent(ev);
if(ev.getAction() == MotionEvent.ACTION_DOWN)
{
mMotionStartX = (int) ev.getX();
mMotionStartY = (int) ev.getY();
if(!mJustInterceptedAndIgnored)
{
mMostlyScrollingInX = false;
mMostlyScrollingInY = false;
}
}
else if(ev.getAction()==MotionEvent.ACTION_MOVE)
{
detectMostlyScrollingDirection(ev);
}
if(mMostlyScrollingInY)
{
return false;
}
if(mMostlyScrollingInX)
{
mJustInterceptedAndIgnored = true;
return true;
}
return result;
}
private void detectMostlyScrollingDirection(MotionEvent ev)
{
if(!mMostlyScrollingInX && !mMostlyScrollingInY) //if we dont know which direction we're going yet
{
float xDistance = Math.abs(mMotionStartX - ev.getX());
float yDistance = Math.abs(mMotionStartY - ev.getY());
if(yDistance>xDistance+5)
{
mMostlyScrollingInY = true;
}
else if(xDistance>yDistance+5)
{
mMostlyScrollingInX = true;
}
}
}
private class SwipeOnTouchListener implements View.OnTouchListener
{
private boolean mSendingDummyMotionEvent = false;
private int mDistanceX;
private int mPreviousDirection;
private boolean mFirstMotionEvent = true;
public boolean onTouch(View v, MotionEvent event)
{
if(mOnTouchListener!=null && !mJustInterceptedAndIgnored || mOnTouchListener!=null && mSendingDummyMotionEvent) //send on touch event to onTouchListener set by an application implementing a SwipeView and setting their own onTouchListener
{
if(mOnTouchListener.onTouch(v, event))
{
if(event.getAction() == MotionEvent.ACTION_UP) //this comes back if a very quick movement event has happened over a view with an onClick
{
//need to call the actionUp directly so the view is not left between pages.
actionUp(event);
}
return true;
}
}
if(mSendingDummyMotionEvent)//if sending the fake action down event (to do with vertical scrolling within this horizontalscrollview) then just ignore it
{
mSendingDummyMotionEvent = false;
return false;
}
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN :
return actionDown(event);
case MotionEvent.ACTION_MOVE :
return actionMove(event);
case MotionEvent.ACTION_UP :
return actionUp(event);
}
return false;
}
private boolean actionDown(MotionEvent event)
{
mMotionStartX = (int) event.getX();
mMotionStartY = (int) event.getY();
mFirstMotionEvent = false;
return false;
}
private boolean actionMove(MotionEvent event)
{
int newDistance = mMotionStartX - (int) event.getX();
int newDirection;
if(newDistance<0) //backwards
{
newDirection = (mDistanceX+4 <= newDistance) ? 1 : -1; //the distance +4 is to allow for jitter
}
else //forwards
{
newDirection = (mDistanceX-4 <= newDistance) ? 1 : -1; //the distance -4 is to allow for jitter
}
if(newDirection != mPreviousDirection && !mFirstMotionEvent)//changed direction, so reset start point
{
mMotionStartX = (int) event.getX();
mDistanceX = mMotionStartX - (int) event.getX();
}
else
{
mDistanceX = newDistance;
}
mPreviousDirection = newDirection; //backwards -1, forwards is 1,
if(mJustInterceptedAndIgnored)//if the intercept picked it up first, we need to give the horizontalscrollview ontouch an action down to enable it to scroll and follow your finger
{
mSendingDummyMotionEvent = true;
dispatchTouchEvent(MotionEvent.obtain(event.getDownTime(), event.getEventTime(), MotionEvent.ACTION_DOWN, mMotionStartX, mMotionStartY, event.getPressure(), event.getSize(), event.getMetaState(), event.getXPrecision(), event.getYPrecision(), event.getDeviceId(), event.getEdgeFlags()));
mJustInterceptedAndIgnored = false;
return true;
}
return false;
}
private boolean actionUp(MotionEvent event)
{
float fingerUpPosition = getScrollX();
float numberOfPages = mLinearLayout.getMeasuredWidth() / mPageWidth;
float fingerUpPage = fingerUpPosition/mPageWidth;
float edgePosition = 0;
if(mPreviousDirection == 1) //forwards
{
if(mDistanceX > DEFAULT_SWIPE_THRESHOLD)//if over then go forwards
{
if(mCurrentPage<(numberOfPages-1))//if not at the end of the pages, you don't want to try and advance into nothing!
{
edgePosition = (int)(fingerUpPage+1)*mPageWidth;
}
else
{
edgePosition = (int)(mCurrentPage)*mPageWidth;
}
}
else //return to start position
{
if(Math.round(fingerUpPage)==numberOfPages-1)//if at the end
{
//need to correct for when user starts to scroll into
//nothing then pulls it back a bit, this becomes a
//kind of forwards scroll instead
edgePosition = (int)(fingerUpPage+1)*mPageWidth;
}
else //carry on as normal
{
edgePosition = mCurrentPage*mPageWidth;
}
}
}
else //backwards
{
if(mDistanceX < -DEFAULT_SWIPE_THRESHOLD)//go backwards
{
edgePosition = (int)(fingerUpPage)*mPageWidth;
}
else //return to start position
{
if(Math.round(fingerUpPage)==0)//if at beginning, correct
{
//need to correct for when user starts to scroll into
//nothing then pulls it back a bit, this becomes a
//kind of backwards scroll instead
edgePosition = (int)(fingerUpPage)*mPageWidth;
}
else //carry on as normal
{
edgePosition = mCurrentPage*mPageWidth;
}
}
}
smoothScrollToPage((int)edgePosition/mPageWidth);
mFirstMotionEvent = true;
mDistanceX = 0;
mMostlyScrollingInX = false;
mMostlyScrollingInY = false;
return true;
}
}
}
Java代码
import java.util.ArrayList;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.graphics.drawable.shapes.Shape;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
public class PageControl extends LinearLayout
{
private int mIndicatorSize = 7;
private Drawable activeDrawable;
private Drawable inactiveDrawable;
private ArrayList<ImageView> indicators;
private int mPageCount = 0;
private int mCurrentPage = 0;
private Context mContext;
private OnPageControlClickListener mOnPageControlClickListener = null;
public PageControl(Context context)
{
super(context);
mContext = context;
initPageControl();
}
public PageControl(Context context, AttributeSet attrs)
{
super(context, attrs);
mContext = context;
//will now wait until onFinishInflate to call initPageControl()
}
@Override
protected void onFinishInflate()
{
initPageControl();
}
private void initPageControl()
{
Log.i("uk.co.jasonfry.android.tools.ui.PageControl","Initialising PageControl");
indicators = new ArrayList<ImageView>();
activeDrawable = new ShapeDrawable();
inactiveDrawable = new ShapeDrawable();
activeDrawable.setBounds(0, 0, mIndicatorSize, mIndicatorSize);
inactiveDrawable.setBounds(0, 0, mIndicatorSize, mIndicatorSize);
Shape s1 = new OvalShape();
s1.resize(mIndicatorSize, mIndicatorSize);
Shape s2 = new OvalShape();
s2.resize(mIndicatorSize, mIndicatorSize);
int i[] = new int[2];
i[0] = android.R.attr.textColorSecondary;
i[1] = android.R.attr.textColorSecondaryInverse;
TypedArray a = mContext.getTheme().obtainStyledAttributes(i);
((ShapeDrawable) activeDrawable).getPaint().setColor(a.getColor(0, Color.DKGRAY));
((ShapeDrawable) inactiveDrawable).getPaint().setColor(a.getColor(1, Color.LTGRAY));
((ShapeDrawable) activeDrawable).setShape(s1);
((ShapeDrawable) inactiveDrawable).setShape(s2);
mIndicatorSize = (int) (mIndicatorSize * getResources().getDisplayMetrics().density);
setOnTouchListener(new OnTouchListener()
{
public boolean onTouch(View v, MotionEvent event)
{
if(mOnPageControlClickListener != null)
{
switch(event.getAction())
{
case MotionEvent.ACTION_UP :
if(PageControl.this.getOrientation() == LinearLayout.HORIZONTAL)
{
if(event.getX()<(PageControl.this.getWidth()/2)) //if on left of view
{
if(mCurrentPage>0)
{
mOnPageControlClickListener.goBackwards();
}
}
else //if on right of view
{
if(mCurrentPage<(mPageCount-1))
{
mOnPageControlClickListener.goForwards();
}
}
}
else
{
if(event.getY()<(PageControl.this.getHeight()/2)) //if on top half of view
{
if(mCurrentPage>0)
{
mOnPageControlClickListener.goBackwards();
}
}
else //if on bottom half of view
{
if(mCurrentPage<(mPageCount-1))
{
mOnPageControlClickListener.goForwards();
}
}
}
return false;
}
}
return true;
}
});
}
/**
* Set the drawable object for an active page indicator
*
* @param d The drawable object for an active page indicator
*/
public void setActiveDrawable(Drawable d)
{
activeDrawable = d;
indicators.get(mCurrentPage).setBackgroundDrawable(activeDrawable);
}
/**
* Return the current drawable object for an active page indicator
*
* @return Returns the current drawable object for an active page indicator
*/
public Drawable getActiveDrawable()
{
return activeDrawable;
}
/**
* Set the drawable object for an inactive page indicator
*
* @param d The drawable object for an inactive page indicator
*/
public void setInactiveDrawable(Drawable d)
{
inactiveDrawable = d;
for(int i=0; i<mPageCount; i++)
{
indicators.get(i).setBackgroundDrawable(inactiveDrawable);
}
indicators.get(mCurrentPage).setBackgroundDrawable(activeDrawable);
}
/**
* Return the current drawable object for an inactive page indicator
*
* @return Returns the current drawable object for an inactive page indicator
*/
public Drawable getInactiveDrawable()
{
return inactiveDrawable;
}
/**
* Set the number of pages this PageControl should have
*
* @param pageCount The number of pages this PageControl should have
*/
public void setPageCount(int pageCount)
{
mPageCount = pageCount;
for(int i=0;i<pageCount;i++)
{
final ImageView imageView = new ImageView(mContext);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mIndicatorSize, mIndicatorSize);
params.setMargins(mIndicatorSize/2, mIndicatorSize, mIndicatorSize/2, mIndicatorSize);
imageView.setLayoutParams(params);
imageView.setBackgroundDrawable(inactiveDrawable);
indicators.add(imageView);
addView(imageView);
}
}
/**
* Return the number of pages this PageControl has
*
* @return Returns the number of pages this PageControl has
*/
public int getPageCount()
{
return mPageCount;
}
/**
* Set the current page the PageControl should be on
*
* @param currentPage The current page the PageControl should be on
*/
public void setCurrentPage(int currentPage)
{
if(currentPage<mPageCount)
{
indicators.get(mCurrentPage).setBackgroundDrawable(inactiveDrawable);//reset old indicator
indicators.get(currentPage).setBackgroundDrawable(activeDrawable);//set up new indicator
mCurrentPage = currentPage;
}
}
/**
* Return the current page the PageControl is on
*
* @return Returns the current page the PageControl is on
*/
public int getCurrentPage()
{
return mCurrentPage;
}
/**
* Set the size of the page indicator drawables
*
* @param indicatorSize The size of the page indicator drawables
*/
public void setIndicatorSize(int indicatorSize)
{
mIndicatorSize=indicatorSize;
for(int i=0;i<mPageCount;i++)
{
indicators.get(i).setLayoutParams(new LayoutParams(mIndicatorSize, mIndicatorSize));
}
}
/**
* Return the size of the page indicator drawables
*
* @return Returns the size of the page indicator drawables
*/
public int getIndicatorSize()
{
return mIndicatorSize;
}
/**
*
* @author Jason Fry - jasonfry.co.uk
*
* Interface definition for a callback to be invoked when a PageControl is clicked.
*
*/
public interface OnPageControlClickListener
{
/**
* Called when the PageControl should go forwards
*
*/
public abstract void goForwards();
/**
* Called when the PageControl should go backwards
*
*/
public abstract void goBackwards();
}
/**
* Set the OnPageControlClickListener object for this PageControl
*
* @param onPageControlClickListener The OnPageControlClickListener you wish to set
*/
public void setOnPageControlClickListener(OnPageControlClickListener onPageControlClickListener)
{
mOnPageControlClickListener = onPageControlClickListener;
}
/**
* Return the OnPageControlClickListener that has been set on this PageControl
*
* @return Returns the OnPageControlClickListener that has been set on this PageControl
*/
public OnPageControlClickListener getOnPageControlClickListener()
{
return mOnPageControlClickListener;
}
}