最近看了hyman的博客:http://blog.csdn.net/lmj623565791/article/details/23692439 ,由於個人技術還比較薄弱,就在這個自定義控件的基礎上做了個拓展,支持水平和垂直的
下面是主要代碼;
GuideView:
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Point;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Scroller;
import android.widget.Toast;
@SuppressLint("ClickableViewAccessibility")
public class GuideView extends ViewGroup {
/**
*
<attr name="orientation"> <enum name="horizontal" value="1" /> <enum
* name="vertical" value="2" /> </attr>
**/
/** 滑動方向 */
private int mOrientation = 0;
/** 水平方向 */
private int mHorientation = 0;
/** 垂直方向 */
private int mVertical = 1;
/** 屏幕寬度 */
private int mScreenWidth;
/** 屏幕高度 */
private int mScreenHeight;
/** 滑動狀態 */
private boolean isScrolling;
/** 滑動輔助類 */
private Scroller mScroller;
/** 記錄當前的x/y的值 */
private PointF mPointF;
/** 記錄上一次的x、y值 */
private PointF mLastPointF;
/** Scroller 對應的開始座標 */
private Point mScrollStartPoint;
/** Scroller 對應的結束座標 */
private Point mScrollStopPoint;
/** 記錄滑動的距離 */
private PointF mDistancePointF;
/**ScrollXY 的差值*/
private Point mDistanceScrollPoint;
/** 加速度檢測 */
private VelocityTracker mVelocityTracker;
/**切換屏幕時的回調函數*/
private OnPageChangeListener mOnPageChangeListener;
/**
* 記錄當前頁
*/
private int currentPage = 0;
public GuideView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// TODO Auto-generated constructor stub
// 獲取自定義屬性
TypedArray mTypeArray = context.obtainStyledAttributes(
attrs, R.styleable.GuideView_orientation);
mOrientation = mTypeArray.getInteger(
R.styleable.GuideView_orientation_orientation, mOrientation);
mTypeArray.recycle();
// 獲取屏幕寬高
initialScreen(context);
mScroller = new Scroller(context);
mPointF = new PointF();
mLastPointF = new PointF();
mScrollStartPoint = new Point();
mScrollStopPoint = new Point();
mDistancePointF = new PointF();
mDistanceScrollPoint=new Point();
}
public GuideView(Context context, AttributeSet attrs) {
this(context, attrs, 1);
// TODO Auto-generated constructor stub
}
public GuideView(Context context) {
this(context, null);
// TODO Auto-generated constructor stub
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 獲取子佈局,重新測量子佈局寬高
int count = getChildCount();
for (int i = 0; i < count; i++) {
measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
if (changed) {
// 重新測量layout的位置
MarginLayoutParams params = (MarginLayoutParams) getLayoutParams();
int childCount = getChildCount();
if (mOrientation == mHorientation) {
params.width = mScreenWidth * getChildCount();
setLayoutParams(params);
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
if (view.getVisibility() != View.GONE) {
view.layout(i * mScreenWidth, t, i * mScreenWidth
+ mScreenWidth, b);
}
}
} else if (mOrientation == mVertical) {
params.height = mScreenHeight * getChildCount();
setLayoutParams(params);
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
// view 沒有隱藏掉,就重新定位
if (view.getVisibility() != View.GONE) {
view.layout(l, i * mScreenHeight, r, i * mScreenHeight
+ mScreenHeight);
}
}
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
// 先進行事件判斷攔截
if(currentPage==getChildCount()-1){
Toast.makeText(getContext(), "finish", Toast.LENGTH_SHORT).show();
return super.onTouchEvent(event);
}
if (isScrolling)
return super.onTouchEvent(event);
mPointF.x = event.getX();
mPointF.y = event.getY();
// 初始化加速度檢測器
initialVelocity(event);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// 當用戶觸摸時記錄下座標信息
Log.i("info"," *******mPoint value****"+"x:"+mPointF.x+"y:"+mPointF.y);
getStartScrollXY();
mLastPointF.x = mPointF.x;
mLastPointF.y = mPointF.y;
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
Log.i("info"," *******mLastPoint value****"+"x:"+mLastPointF.x+"y:"+mLastPointF.y);
Log.i("info"," *******mPoint value****"+"x:"+mPointF.x+"y:"+mPointF.y);
Log.i("info"," *******************************************");
Log.i("info"," *******************************************");
/**
* Stops the animation. Contrary to
* {@link #forceFinished(boolean)}, aborting the animating cause
* the scroller to move to the final x and y position 源碼說明:
* mScroller.abortAnimation() 如果滑動還沒有結束,那麼就終止滑動。
*
* @see #forceFinished(boolean)
*/
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
mDistancePointF.x = mLastPointF.x - mPointF.x;
mDistancePointF.y = mLastPointF.y - mPointF.y;
Log.i("info"," *******mDistancePointF value ******"+"dx: "+mDistancePointF.x+" dy: "+mDistancePointF.y);
getStopScrollXY();
// 先判斷滑動的方向確定滑動的距離 scrollBy(x,y)
// 1.y軸---向上滑動--下一個視圖
// 2.y軸---向下滑動--上一個視圖
// 3.x軸---向左滑動--下一個視圖
// 4.x軸---向右滑動--上一個視圖
/**
* 320*480 -8 mlasty=-10 currenty=-2
* distance=mlasty-currenty=-8《0 scrolly+distance<0?
*
* 條件都滿足時,確定視圖向上滑動,加載 下一個視圖
*
* 重新定義distanceY的值以便於ScrollBy(x,y)調用
*
* 補充說明:
* getScrollX()說明:=手機屏幕顯示區域左上角x座標減去MultiViewGroup視圖左上角x座標=320
*
* getScrollY()說明:=手機屏幕顯示區域左上角y座標減去MultiViewGroup視圖左上角y座標=0(
* 因爲子視圖的高度和手機屏幕高度一樣)
*
*
**/
if (mOrientation == mHorientation) {
if (mDistancePointF.x > 0
&& mScrollStopPoint.x + mDistancePointF.x > getWidth()-mScreenWidth) {
mDistancePointF.x = getWidth() - mScreenWidth -mScrollStopPoint.x;
} else if (mDistancePointF.x < 0
&& mScrollStopPoint.x + mDistancePointF.x < 0) {
mDistancePointF.x = - mScrollStopPoint.x;
}
scrollBy((int) mDistancePointF.x, 0);
} else if (mOrientation == mVertical) {
if (mDistancePointF.y < 0
&& mScrollStopPoint.y + mDistancePointF.y < 0) {
mDistancePointF.y = -mScrollStopPoint.y;
}
if (mDistancePointF.y > 0
&& mScrollStopPoint.y + mDistancePointF.y > getHeight()
- mScreenHeight) {
mDistancePointF.y = getHeight() - mScreenHeight
- mScrollStopPoint.y;
}
scrollBy(0, (int) mDistancePointF.y);
}
mLastPointF.x = mPointF.x;
mLastPointF.y = mPointF.y;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
getStopScrollXY();
getDistanceScrollXY();
//比較滑動方向趨勢
//判斷是上滑動還是下滑動
if(checkDirection()){
//上滑動《加載更多》
if(isScrollToNext()){
//能滑動到下一頁
if(mOrientation==mHorientation){
mScroller.startScroll(getScrollX(), 0,mScreenWidth - mDistanceScrollPoint.x,0);
}else if (mOrientation==mVertical){
mScroller.startScroll(0, getScrollY(), 0, mScreenHeight
- mDistanceScrollPoint.y);
}
}else{
//不能滑動到下一頁
if(mOrientation==mHorientation){
mScroller.startScroll(getScrollX(), 0,-mDistanceScrollPoint.x,0);
}else if (mOrientation==mVertical){
mScroller.startScroll(0, getScrollY(), 0, -mDistanceScrollPoint.y);
}
}
}else{
//《下滑動,刷新》
if(isScrollToprivew()){
//能滑動到上一頁
if(mOrientation==mHorientation){
mScroller.startScroll( getScrollX(), 0,
-mScreenWidth - mDistanceScrollPoint.x,0);
}else if (mOrientation==mVertical){
mScroller.startScroll(0, getScrollY(), 0,
-mScreenHeight - mDistanceScrollPoint.y);
}
}else{
//不能滑動到上一頁
if(mOrientation==mHorientation){
mScroller.startScroll(getScrollX(),0, -mDistanceScrollPoint.x, 0);
}else if (mOrientation==mVertical){
mScroller.startScroll(0, getScrollY(), 0, -mDistanceScrollPoint.y);
}
}
}
isScrolling = true;
postInvalidate();
recycleVelocity();
}
return true;
}
/**
* Called by a parent to request that a child update its values for mScrollX
* and mScrollY if necessary. This will typically be done if the child is
* animating a scroll using a {@link android.widget.Scroller Scroller}
* object.
*
* 爲了易於控制滑屏控制,Android框架提供了 computeScroll()方法去控制這個流程。在繪製View時,會在draw()過程調用該
* 方法。因此, 再配合使用Scroller實例,我們就可以獲得當前應該的偏移座標,手動使View/ViewGroup偏移至該處。
* computeScroll()方法原型如下,該方法位於ViewGroup.java類中
*/
@Override
public void computeScroll() {
// TODO Auto-generated method stub
super.computeScroll();
if (mOrientation== mVertical) {
if (mScroller.computeScrollOffset()) {
scrollTo(0, mScroller.getCurrY());
postInvalidate();
} else {
int position = getScrollY() / mScreenHeight;
if (position != currentPage) {
if (mOnPageChangeListener != null) {
currentPage = position;
mOnPageChangeListener.onPageChange(currentPage);
}
}
}
} else if (mOrientation== mHorientation) {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), 0);
postInvalidate();
} else {
int position = getScrollX() / mScreenWidth;
if (position != currentPage) {
if (mOnPageChangeListener != null) {
currentPage = position;
mOnPageChangeListener.onPageChange(currentPage);
}
}
}
}
isScrolling = false;
}
/************************************ Method *********************************************/
/**
* 獲取屏幕寬高
*/
public void initialScreen(Context context) {
WindowManager mWindowManager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
mWindowManager.getDefaultDisplay().getMetrics(outMetrics);
mScreenWidth = outMetrics.widthPixels;
mScreenHeight = outMetrics.heightPixels;
}
/**
* 初始化加速度檢測器
*
* @param event
*/
private void initialVelocity(MotionEvent event) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
}
/**
* 初始化scrollX scrollY
*/
private void getStartScrollXY(){
mScrollStartPoint.x = getScrollX();
mScrollStartPoint.y = getScrollY();
}
/**
* 停止滑動後的ScrollX ScrollY
*/
private void getStopScrollXY(){
mScrollStopPoint.x = getScrollX();
mScrollStopPoint.y = getScrollY();
}
/**
* 比較滑動的ScrollX ScrollY差值
*/
private void getDistanceScrollXY(){
mDistanceScrollPoint.x = mScrollStopPoint.x-mScrollStartPoint.x;
mDistanceScrollPoint.y = mScrollStopPoint.y-mScrollStartPoint.y;
}
/**
* 檢查滑動方向
* @return true 加載更多 false 刷新
*/
public boolean checkDirection(){
boolean mDirection =false;
if (mOrientation == mVertical) {
mDirection = mDistanceScrollPoint.y > 0 ? true : false;
} else if (mOrientation== mHorientation) {
mDirection = - mDistanceScrollPoint.x < 0 ? true : false;
}
return mDirection;
}
/**
* 根據滑動距離判斷 是否能夠滑動到下一屏
* 加載跟多
* @return
*/
private boolean isScrollToNext() {
boolean isScrollTo = false;
if (mOrientation == mVertical) {
isScrollTo = mDistanceScrollPoint.y > mScreenHeight / 2
|| Math.abs(getVelocity()) > 600;
} else if (mOrientation == mHorientation) {
isScrollTo = mDistanceScrollPoint.x > mScreenWidth / 2
|| Math.abs(getVelocitx()) > 600;
}
return isScrollTo;
}
/**
* 根據滑動距離判斷 是否能夠滑動到上一屏
* 刷新
* @return
*/
private boolean isScrollToprivew() {
boolean isScrollTo = false;
if (mOrientation == mVertical) {
isScrollTo = -mDistanceScrollPoint.y > mScreenHeight / 2
|| Math.abs(getVelocity()) > 600;
} else if (mOrientation == mHorientation) {
isScrollTo = -mDistanceScrollPoint.x > mScreenWidth / 2
|| Math.abs(getVelocitx()) > 600;
}
return isScrollTo;
}
/**
* 獲取x方向的加速度
*
* @return
*/
private int getVelocitx() {
mVelocityTracker.computeCurrentVelocity(1000);
int velocitx = (int) mVelocityTracker.getXVelocity(1000);
velocitx = (int) mVelocityTracker.getXVelocity(1000);
return velocitx;
}
/**
* 獲取y方向的加速度
*
* @return
*/
private int getVelocity() {
mVelocityTracker.computeCurrentVelocity(1000);
int velocity = (int) mVelocityTracker.getYVelocity(1000);
velocity = (int) mVelocityTracker.getYVelocity(1000);
return velocity;
}
/**
* 釋放資源
*/
private void recycleVelocity() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
/**
* 設置回調接口
*
* @param onPageChangeListener
*/
public void setOnPageChangeListener(
OnPageChangeListener onPageChangeListener) {
mOnPageChangeListener = onPageChangeListener;
}
/**
* 回調接口
*
* @author zhy
*
*/
public interface OnPageChangeListener {
void onPageChange(int currentPage);
}
}
attrs:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="GuideView_orientation">
<attr name="orientation">
<enum name="Horientation" value="0" />
<enum name="Vertical" value="1" />
</attr>
</declare-styleable>
</resources>
源碼下載地址: http://download.csdn.net/detail/anddroid_lanyan/8660753
在項目還存在遺留問題:(取消page的攔截事件)當我把背景色改成白色時,水平佈局,左右滑動反覆,會出現白邊,還沒搞明白具體問題,誰若是知道麻煩告知。
改完這個自定義控件後,我發現一件事,只要你願意堅持去做一件事,就一定會有所收穫。