zoukankan      html  css  js  c++  java
  • Android自定义组件系列【4】——自定义ViewGroup实现双侧滑动

    在上一篇文章《Android自定义组件系列【3】——自定义ViewGroup实现侧滑》中实现了仿Facebook和人人网的侧滑效果,这一篇我们将接着上一篇来实现双面滑动的效果。

    1、布局示意图:


    2、核心代码

    	@Override
    	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    		mWidth = MeasureSpec.getSize(widthMeasureSpec);  //获取MyScrollView的宽度
    		mHeight = MeasureSpec.getSize(heightMeasureSpec); //获取MyScrollView的高度
    		if(!isLocked){
    			initX = getScrollX();
    			isLocked = true;
    		}
    	}
    在该方法中获取到初始的视图坐标偏移量getScrollX()

    	@Override
    	public boolean onTouchEvent(MotionEvent event) {
    		float x = event.getX();
    		switch (event.getAction()) {
    		case MotionEvent.ACTION_DOWN:
    			System.out.println("ACTION_DOWN");
    			mDownX = x;          //记录按下时的x坐标
    			break;
    		case MotionEvent.ACTION_UP:
    			System.out.println("ACTION_UP");
    			int dis = (int) (x - mDownX);   //滑动的距离
    			if(Math.abs(dis) > (mWidth * mMenuWeight / 2)){
    				if(dis > 0){          //如果>0则是向右滑动
    					toRightMove();
    				}else{				  //如果<0则是向左滑动
    					toLeftMove();
    				}
    			}
    			break;
    		default:
    			break;
    		}
    		
    		return true;
    	}
    监听函数记录下按下和移动的屏幕坐标,求差计算出移动距离,如果这个距离大于阀值 (mWidth * mMenuWeight / 2)则滑动
    	public void toRightMove(){
    		 System.out.println("maxRight = " + maxRight);
    		 System.out.println("X = "  + getScrollX());
    		 if(getScrollX() >= initX){
    			 int dx = (int)(mWidth * mMenuWeight);
    			 mScroller.startScroll(getScrollX(), 0, -dx, 0, 500);
    			 if(mListener != null){
    				 mListener.onChanged();
    			 }
    			 invalidate();
    		 }
    	}
    如果是向右滑动则,如果当前是初始位置(centerView在中间)则可以向右滑动(getScrollX == initX),或者当前左边View可以看见,则可以向右滑动将centerView移动到中间(getScrollX > initX).同理有向左滑动的方法。
    	public void toLeftMove(){
    		System.out.println("maxLeft = " + maxLeft);
    		 System.out.println("X = "  + getScrollX());
    		if(getScrollX() <= initX){
    			int dx = (int)(mWidth * mMenuWeight);
    			mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
    			if(mListener != null){
    				mListener.onChanged();
    			}
    			invalidate();
    		}
    	}
    3、全部代码

    MyScrollView.java

    package com.example.testscrollto;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.LinearLayout;
    import android.widget.Scroller;
    
    public class MyScrollView extends LinearLayout{
    	
    	private Context mContext;
    	private int mWidth;
    	private int mHeight;
    	
    	private float mMenuWeight = 3.0f / 5; //菜单界面比例
    	
    	private View mMenuView;   //菜单界面
    	private View mPriView;	  //内容界面
    	private View mRightView;  //右边界面
    	
    	private int maxLeft;
    	private int maxRight;
    	private int initX;
    	
    	private boolean isLocked = false;
    	
    	private Scroller mScroller;
    	
    	private OnMenuChangedListener mListener;
    	
    	public MyScrollView(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		mContext = context;
    		mScroller = new Scroller(mContext);
    		
    	}
    
    	@Override
    	protected void onLayout(boolean changed, int l, int t, int r, int b) {
    		super.onLayout(changed, l, t, r, b);
    		mMenuView.layout(-(int)(mWidth * mMenuWeight), 0, 0, mHeight);
    		mPriView.layout(0, 0, mWidth, mHeight);
    		mRightView.layout(mWidth, 0, mWidth + (int)(mWidth * mMenuWeight), mHeight);
    	}
    	
    	@Override
    	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    		mWidth = MeasureSpec.getSize(widthMeasureSpec);  //获取MyScrollView的宽度
    		mHeight = MeasureSpec.getSize(heightMeasureSpec); //获取MyScrollView的高度
    		if(!isLocked){
    			initX = getScrollX();
    			isLocked = true;
    		}
    	}
    	
    	/**设置右滑的菜单View*/
    	public void setMenu(View menu){
    		mMenuView = menu;
    		addView(mMenuView);
    	}
    	
    	/**
    	 * 设置主界面View
    	 */
    	public void setPrimary(View primary){
    		mPriView = primary;
    		addView(mPriView);
    	}
    	
    	public void setRightView(View rightview){
    		mRightView = rightview;
    		addView(mRightView);
    	}
    	
    	private float mDownX;
    	
    	@Override
    	public boolean onTouchEvent(MotionEvent event) {
    		float x = event.getX();
    		switch (event.getAction()) {
    		case MotionEvent.ACTION_DOWN:
    			System.out.println("ACTION_DOWN");
    			mDownX = x;          //记录按下时的x坐标
    			break;
    		case MotionEvent.ACTION_UP:
    			System.out.println("ACTION_UP");
    			int dis = (int) (x - mDownX);   //滑动的距离
    			if(Math.abs(dis) > (mWidth * mMenuWeight / 2)){
    				if(dis > 0){          //如果>0则是向右滑动
    					toRightMove();
    				}else{				  //如果<0则是向左滑动
    					toLeftMove();
    				}
    			}
    			break;
    		default:
    			break;
    		}
    		
    		return true;
    	}
    	
    	@Override
    	public void computeScroll() {
    		super.computeScroll();
    		if(mScroller.computeScrollOffset()){
    			scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
    			postInvalidate();
    		}
    	}
    	
    	
    	public void toRightMove(){
    		 System.out.println("maxRight = " + maxRight);
    		 System.out.println("X = "  + getScrollX());
    		 if(getScrollX() >= initX){
    			 int dx = (int)(mWidth * mMenuWeight);
    			 mScroller.startScroll(getScrollX(), 0, -dx, 0, 500);
    			 if(mListener != null){
    				 mListener.onChanged();
    			 }
    			 invalidate();
    		 }
    	}
    	
    	
    	public void toLeftMove(){
    		System.out.println("maxLeft = " + maxLeft);
    		 System.out.println("X = "  + getScrollX());
    		if(getScrollX() <= initX){
    			int dx = (int)(mWidth * mMenuWeight);
    			mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
    			if(mListener != null){
    				mListener.onChanged();
    			}
    			invalidate();
    		}
    	}
    	
    
    	 
    	public interface OnMenuChangedListener{
    		 public void onChanged();
    	}	
    	
    	public void setOnMenuChangedListener(OnMenuChangedListener listener){
    		mListener = listener;
    	}
    }
    
    MainActivity.java
    package com.example.testscrollto;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    
    import com.example.testscrollto.MyScrollView.OnMenuChangedListener;
    
    public class MainActivity extends Activity {
    	
    	private MyScrollView mScrollView;
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		mScrollView = (MyScrollView)findViewById(R.id.rightscrollview);
    		final View menu = getLayoutInflater().inflate(R.layout.rightscrollview_menu, null);
    		final View primary = getLayoutInflater().inflate(R.layout.rightscrollview_primary, null);
    		final View rightview = getLayoutInflater().inflate(R.layout.rightscrollview_right_menu, null);
    		mScrollView.setMenu(menu);
    		mScrollView.setPrimary(primary);
    		mScrollView.setRightView(rightview);
    		mScrollView.setOnMenuChangedListener(new OnMenuChangedListener() {
    			
    			@Override
    			public void onChanged() {
    				System.out.println("窗口切换了一次");
    			}
    		});
    	}
    }
    activity_main.xml
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >
        
        <com.example.testscrollto.MyScrollView
            android:id="@+id/rightscrollview"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
    </LinearLayout>
    其余三个视图界面无限制,可以自由定义,这里就不贴出来了。

    4、运行效果:


    源代码下载:http://download.csdn.net/detail/lxq_xsyu/7232701

    这样就可以实现左右滑动了,没有任何bug吗?

    其实这样看似是没有什么问题了,上面用于判断界面位置的代码在逻辑上看似是对的,但是在实际的应用中偶尔会出现错乱。

    下面我们用另外一种方式解决:

    package com.example.jaohangui.view;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.View;
    import android.widget.LinearLayout;
    import android.widget.Scroller;
    
    public class MyScrollLeftRightView extends LinearLayout{
    
    	private Scroller mScroller;
    	
    	private View mLeftView;  //坐标界面
    	private View mMainView;  //中间主界面
    	private View mRightView; //右边界面
    	
    	private float mMeasureWight = 3.0f / 5; //菜单界面比例
    	private int mWidth;	
    	private int mHeight;
    	
    	private boolean isLocked = false;
    	private boolean isToLeft = false;
    	private static int CENTER_PAGE = 1;
    	private static int LEFT_PAGE = 0;
    	private static int RIGHT_PAGE = 2;
    	private int currentPage = CENTER_PAGE;
    	
    	public MyScrollLeftRightView(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		mScroller = new Scroller(context);
    	}
    	
    	@Override
    	protected void onLayout(boolean changed, int l, int t, int r, int b) {
    		super.onLayout(changed, l, t, r, b);
    		mLeftView.layout(-(int)(mWidth * mMeasureWight), 0, 0, mHeight);
    		mMainView.layout(0, 0, mWidth, mHeight);
    		mRightView.layout(mWidth, 0, mWidth + (int)(mWidth * mMeasureWight), mHeight);
    	}
    	
    	@Override
    	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    		mWidth = MeasureSpec.getSize(widthMeasureSpec);
    		mHeight = MeasureSpec.getSize(heightMeasureSpec);
    	}
    	
    	/**
    	 * 添加左边界面内容
    	 * @param view
    	 */
    	public void setLeftView(View view){
    		mLeftView = view;
    		addView(mLeftView);
    	}
    	
    	/**
    	 * 添加主界面内容
    	 * @param view
    	 */
    	public void setMainView(View view){
    		mMainView = view;
    		addView(mMainView);
    	}
    	
    	/**
    	 * 添加右边界面内容
    	 * @param view
    	 */
    	public void setRightView(View view){
    		mRightView = view;
    		addView(mRightView);
    	}
    	
    	private float mDownX;
    	@Override
    	public boolean onTouchEvent(MotionEvent event) {
    		float x = event.getX();
    		switch (event.getAction()) {
    		case MotionEvent.ACTION_DOWN:
    			mDownX = x;
    			break;
    		case MotionEvent.ACTION_UP:
    			int dis = (int)(x - mDownX); //滑动的距离
    			if(Math.abs(dis) > (mWidth * mMeasureWight / 3)){
    				if(dis > 0){
    					toRightMove();
    				}else{
    					toLeftMove();
    				}
    			}
    			break;
    
    		default:
    			break;
    		}
    		return true;
    	}
    	
    	@Override
    	public void computeScroll() {
    		super.computeScroll();
    		if(mScroller.computeScrollOffset()){
    			isLocked = true;
    			scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
    			postInvalidate();
    		}else{
    			if(currentPage == CENTER_PAGE){
    				if(isToLeft){
    					currentPage = RIGHT_PAGE;
    				}else{
    					currentPage = LEFT_PAGE;
    				}
    			}else{
    				currentPage = CENTER_PAGE;
    			}
    			isLocked = false;
    		}
    	}
    	
    	public void toRightMove(){
    		if(currentPage == LEFT_PAGE || isLocked){
    			return;
    		}
    		int dx = (int)(mWidth * mMeasureWight);
    		mScroller.startScroll(getScrollX(), 0, -dx, 0, 500);
    		invalidate();
    		isToLeft = false;
    	}
    	
    	public void toLeftMove(){
    		if(currentPage == RIGHT_PAGE || isLocked){
    			return;
    		}
    		System.out.println("ok");
    		int dx = (int)(mWidth * mMeasureWight);
    		mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
    		invalidate();
    		isToLeft = true;
    	}
    }

    上面使用了两个用来判断的变量和一个锁定状态(不让进入toLeftMove和toRightMove)的变量。

    1、在进入toLeftMove或者toRightMove方法的时候首先会判断是否isLocked为true,如果为true则说明当前是正在滑动状态,不可以执行这两个方法。如果不这样去控制,在界面正在滑动的时候上面的currentPage就会发生错乱。

    2、将要滑动之前告诉computeScroll()方法,是从toLeftMove和toRightMove的那个方法中出来的(使用isToLeft)。

    3、最后判断Scroller是否已经停止滑动(移动),如果停止则改变当前页面的状态(currentPage的值)

    有的时候我们虽然实现了一个功能或者某个逻辑,而如何才能使这段代码更加健壮和有效更值得我们去认真思考。。。

  • 相关阅读:
    如何恢复包含损坏记录的物理文件
    启动日志
    如何在各种环境中处理多成员的物理文件
    如何找出物理文件中损坏的记录
    如何重新找回物理文件中已经被删除的记录
    folder的操作
    如何将AS/400英文界面改为中文界面?
    如何从OS/400里直接发送电子邮件到Internet
    如何在AS/400上发送带有颜色的MESSAGE
    关于命令RGZPFM
  • 原文地址:https://www.cnblogs.com/lanzhi/p/6469035.html
Copyright © 2011-2022 走看看