android:在Gallery中放webview,实现滚动查看网页

当然,这里说的gallery不是系统的gallery,而是自定义的一个控件,实现和gallery一样的滑动效果。

一开始我试过使用系统自带的gallery控件来实现可以左右滑动并且显示的是webview控件里面的内容,但是这种方法没有成功,具体情况是webview可以响应手势事件,可以上下滑动等,但是外层的gallery根本拿不到手势事件。其原因是webview首先获得手势事件,并且全部吞掉了用于处理webview中的网页缩放或者滑动查看,外层的activity和gallery都得不到这个事件。如果把webview的事件屏蔽掉(自定义个一个webview,并且在ontouchevent中拦截,返回false),这样的话gallery可以正常滑动,可是webview中的内容就没有办法滚动来查看。

出现这种问题,我的另一个思路就是自定义一个控件来实现这样的效果,网上有使用viewflipper实现的,不过那种效果是程序一下就切换过去,不是手势慢慢控制移动。这里借鉴了另一篇博文,他介绍了如何自定义一个viewgroup,并且使用scroller来实现触摸滑屏的效果。

首先上效果图:



最重要的一个自定义viewgroup控件

public class WebViewGallery extends ViewGroup {

	private final static String TAG = WebViewGallery.class.getSimpleName();
	
	private Context mContext;
    private int curScreen 		= 0 ;  //当前屏
    private Scroller mScroller 	= null ;
    
    private static final int TOUCH_STATE_REST 				= 0;
	private static final int TOUCH_STATE_SCROLLING 			= 1;
	private int mTouchState 								= TOUCH_STATE_REST;
	
	//处理触摸事件 ~
	public static int  SNAP_VELOCITY 				= 600 ;		//大于
	private int mTouchSlop 							= 100 ;		//初始化一个最小滑动距离,手指滑动距离需要大于此值系统才会开始移动控件
	private float mLastionMotionX 					= 0 ;
	private VelocityTracker mVelocityTracker 		= null ;	//处理触摸的速率
	
	//----------------
	private OnWebviewSwitchListener mOnWebviewSwitchListener;
	
	
	
	public WebViewGallery(Context context, AttributeSet attrs) {
		super(context, attrs);
		this.mContext = context;
		init();
	}

	private void init() {
		mScroller = new Scroller(mContext);
	}
	
	public void addWebViews(List<WebView> webviewlist){
		if (webviewlist != null && webviewlist.size() != 0){
			Iterator<WebView> iterator = webviewlist.iterator();
			while (iterator.hasNext()){
				addView(iterator.next());
			}
			
			invalidate();
		}
	}
	
	public void setOnWebviewSwitchListener(OnWebviewSwitchListener onWebviewSwitchListener){
		mOnWebviewSwitchListener = onWebviewSwitchListener;
	}
	
	
	// 只有当前LAYOUT中的某个CHILD导致SCROLL发生滚动,才会致使自己的COMPUTESCROLL被调用
	@Override
	public void computeScroll() {

		// 如果返回true,表示动画还没有结束
		// 因为前面startScroll,所以只有在startScroll完成时 才会为false
		if (mScroller.computeScrollOffset()) {

			// 产生了动画效果 每次滚动一点
			scrollTo(mScroller.getCurrX(), mScroller.getCurrY());

			// 刷新View 否则效果可能有误差
			postInvalidate();
		}
	}
		
	
	// 这个感觉没什么作用 不管true还是false 都是会执行onTouchEvent的 因为子view里面onTouchEvent返回false了
		@Override
		public boolean onInterceptTouchEvent(MotionEvent ev) {

			final int action = ev.getAction();
			//表示已经开始滑动了,不需要走该Action_MOVE方法了(第一次时可能调用)。
			if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
				return true;
			}

			final float x = ev.getX();
			final float y = ev.getY();

			switch (action) {
			case MotionEvent.ACTION_MOVE:
				final int xDiff = (int) Math.abs(mLastionMotionX - x);
				//超过了最小滑动距离,可以响应滑动事件
				if (xDiff > mTouchSlop) {
					mTouchState = TOUCH_STATE_SCROLLING;
				}
				
				break;

			case MotionEvent.ACTION_DOWN:
				mLastionMotionX = x;
//				mLastMotionY = y;
				mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
				break;

			case MotionEvent.ACTION_CANCEL:
			case MotionEvent.ACTION_UP:
				mTouchState = TOUCH_STATE_REST;
				break;
			}
			
			return mTouchState != TOUCH_STATE_REST;
		}
		
		public boolean onTouchEvent(MotionEvent event){
			if (mVelocityTracker == null) {
				mVelocityTracker = VelocityTracker.obtain();
			}
			
			mVelocityTracker.addMovement(event);
			
			//TODO--此句好像没有影响
			super.onTouchEvent(event);
			
			//手指位置地点
			float x = event.getX();
			float y = event.getY();
			
			
			switch(event.getAction()){
			
			case MotionEvent.ACTION_DOWN:
				//如果屏幕的动画还没结束,你就按下了,我们就结束该动画
				if(mScroller != null){
					if(!mScroller.isFinished()){
						mScroller.abortAnimation();
					}
				}
				mLastionMotionX = x ;
				break ;
				
			case MotionEvent.ACTION_MOVE:
				//视图跟着手指的移动慢慢移动
				int detaX = (int)(mLastionMotionX - x);
				scrollBy(detaX, 0);
				mLastionMotionX = x;
				break ;
				
			case MotionEvent.ACTION_UP:
				mVelocityTracker.computeCurrentVelocity(1000);
				int velocityX = (int) mVelocityTracker.getXVelocity() ;
				
				//滑动速率达到了一个标准(快速向右滑屏,返回上一个屏幕) 马上进行切屏处理
				if (velocityX > SNAP_VELOCITY && curScreen > 0) {
					// Fling enough to move left
					snapToScreen(curScreen - 1);
					
				}else if(velocityX < -SNAP_VELOCITY && curScreen < (getChildCount()-1)){
					//快速向左滑屏,返回下一个屏幕)
					snapToScreen(curScreen + 1);
					
				}else{
					//以上为快速移动的 ,强制切换屏幕
					//我们是缓慢移动的,因此先判断是保留在本屏幕还是到下一屏幕
					snapToDestination();
				}
				
				if (mVelocityTracker != null) {
					mVelocityTracker.recycle();
					mVelocityTracker = null;
				}
				
				mTouchState = TOUCH_STATE_REST ;
			    break;
			    
			case MotionEvent.ACTION_CANCEL:
				mTouchState = TOUCH_STATE_REST ;
				break;
			}
			
			return true ;
		}
	
	
	private void snapToDestination(){
		//当前的偏移位置
		int scrollX = getScrollX() ;
		int scrollY = getScrollY() ;
		
		//判断是否超过下一屏的中间位置,如果达到就抵达下一屏,否则保持在原屏幕	
		//直接使用这个公式判断是哪一个屏幕 前后或者自己
		//判断是否超过下一屏的中间位置,如果达到就抵达下一屏,否则保持在原屏幕
		// 这样的一个简单公式意思是:假设当前滑屏偏移值即 scrollCurX 加上每个屏幕一半的宽度,除以每个屏幕的宽度就是
		//  我们目标屏所在位置了。 假如每个屏幕宽度为320dip, 我们滑到了500dip处,很显然我们应该到达第二屏	
		int destScreen = (getScrollX() + getWidth() / 2 ) / getWidth() ;
		
		snapToScreen(destScreen);
	}
	
	
    private void snapToScreen(int whichScreen){	
    	
	    //简单的移到目标屏幕,可能是当前屏或者下一屏幕
	    //直接跳转过去,不太友好
	    //scrollTo(mLastScreen * getWidth(), 0);
	    //为了友好性,我们在增加一个动画效果
	    //需要再次滑动的距离 屏或者下一屏幕的继续滑动距离

    	if(whichScreen > getChildCount() - 1){			//防止出界
    		whichScreen = getChildCount() - 1;
    	}
    	
    	
    	if (curScreen != whichScreen){
    		//切换到新的一个view
    		curScreen = whichScreen ;
    		
    		//TODO----此处响应屏幕切换事件----------------
//        	Log.e(TAG, "snapToScreen,滑动:" + whichScreen);
        	
        	if (null != mOnWebviewSwitchListener){
        		mOnWebviewSwitchListener.onWebviewSwitch(curScreen);
        	}
    	}
	    
	    int dx = curScreen*getWidth() - getScrollX() ;
	    
	    mScroller.startScroll(getScrollX(), 0, dx, 0, Math.abs(dx) * 2);
	    
	    //此时需要手动刷新View 否则没效果
	    invalidate();
    }
    
    /**
     * 显示指定页面的webview
     * @param whichWebview
     */
    public void setDisplayedWebview(int whichWebview){
    	snapToScreen(whichWebview);
    }
    
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

		// 设置该ViewGroup的大小
		int width = MeasureSpec.getSize(widthMeasureSpec);
		int height = MeasureSpec.getSize(heightMeasureSpec);
		setMeasuredDimension(width, height);

		int childCount = getChildCount();
		for (int i = 0; i < childCount; i++) {
			View child = getChildAt(i);
			// 设置每个子视图的大小 , 即全屏
			child.measure(getWidth(), MainActivity.scrrenHeight);
		}
	}

	
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		
		int startLeft = 0; // 每个子视图的起始布局坐标
		int startTop = 0; // 间距设置为10px 相当于 android:marginTop= "10px"
		int childCount = getChildCount();

		for (int i = 0; i < childCount; i++) {
			View child = getChildAt(i);
			
			//即使可见的,才划到屏幕上
			if(child.getVisibility() != View.GONE){
			    child.layout(startLeft, startTop, 
					   startLeft + getWidth(), 
					   startTop + MainActivity.scrrenHeight );
			}
			    
			startLeft = startLeft + getWidth() ; //校准每个子View的起始布局位置
			//三个子视图的在屏幕中的分布如下 [0 , 320] / [320,640] / [640,960]
		}
	}

}

如果你看了我推荐的另一篇博文,那这个代码看起来不会很难,需要注意的是为了达到这样的效果:左右滑动效果不要太容易出现,因为当用户操作webview内部网页的时候也有一定的手势,很容易触发外部自定义gallery的左右滑动事件。在这里我们定一个整数mTouchSlop,使用该量来控制左右滑动效果触发的最低长度。这样左右滑动事件就不会很容易触发。


源码下载地址:http://download.csdn.net/detail/u011421480/5857373

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值