ViewGroup_listView_hideTitle(Listview多tab上滑悬浮Title)

ViewGroup_listView_hideTitle

今天是2015年3月12日,学习安卓开发也快半年了。一直想写一些自己的东西,一来是自己比较懒,其实还是自己肚子里的油墨太少了。
其实年前就已经准备了这个demo。不过有一些bug就搁置在一边了。昨天把这期的项目完成了,我又把之前的demo给捡起来了。一下子就解决了bug,很开心。 就把这个demo给写出来。
开发过程中有着各种各样的需求。 之前就遇到了这个问题:一个页面由3个listview组成。可实现raodiobutton切换,同时还能左右切换。这个比较好实现,用ViewPager+Fragment 就可以实现,但是加入GA统计时候就有点麻烦了,viewpager一次性需要加载完毕页面,否则进入页面有点卡顿,listview滑动时也有一些卡顿,体验感很差,可能还是技术不行的原因吧。

需要实现可左右滑动向上滑动隐藏个人信息,当listview向下滑动到第一项时,显示个人信息栏。我利用ViewGroup+listview即可实现此效果,以下是代码:

###com.example.viewgroup_lv
Src
: MainActivity
: ScreenUtils
: ScrollLayout

libs
android-support-v4.jar
nineoldandroids-2.4.0.jar
Layout
activity_main.xml
listview.xml
radio_group.xml

MainActivity

public class MainActivity extends Activity implements AnimatorListener {

	ScrollLayout sl;
	RadioGroup rg;
	ImageView cursorView;
	int cursorWidth = 0;// 下滑红线的宽度
	final int length = 3;
	int[] rb_id = { R.id.rb1, R.id.rb2, R.id.rb3 };
	RadioButton[] rb = new RadioButton[length];
	int currentTag = 0;// 当前RadioGroup的Index值
	private View mHeadLayout;
	private View mLayout;
	private View mRg;
	LayoutInflater inflater;
	View[] view = new View[length];
	ListView[] lv_list = new ListView[length];
	MyArrayAdapter[] mAdapter = new MyArrayAdapter[length];
	private MyOnTouchListener myOnTouchListener;
	private boolean mIsTitleHide = false;
	private boolean mIsAnim = false;
	private float lastX = 0;
	private float lastY = 0;
	private boolean isDown = false;
	private boolean isUp = false;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		inflater = LayoutInflater.from(this);
		initViews();
		initListener();
		registerListener();
	}

	private void initViews() {
		mLayout = findViewById(R.id.ll_ab);
		mHeadLayout = findViewById(R.id.ll_a);
		sl = (ScrollLayout) findViewById(R.id.sl);
		mRg = findViewById(R.id.view_rg);
		cursorView = (ImageView) mRg.findViewById(R.id.iv_record_cursor);
		rg = (RadioGroup) mRg.findViewById(R.id.rg);
		Screen sc = ScreenUtils.getScreenPix(getApplicationContext());
		cursorWidth = sc.widthPixels / 3;// 获取当前屏幕上的下滑横线的宽度
		LayoutParams lp = (LayoutParams) cursorView.getLayoutParams();
		lp.width = cursorWidth;
		cursorView.setLayoutParams(lp);
		for (int i = 0; i < length; i++) {
			rb[i] = (RadioButton) mRg.findViewById(rb_id[i]);
			view[i] = inflater.inflate(R.layout.listview, null);
			lv_list[i] = (ListView) view[i].findViewById(R.id.list_a);
			sl.addView(view[i]);
			switch (i) {
			case 0:
				mAdapter[0] = new MyArrayAdapter( this
						, android.R.layout.simple_list_item_1, getData());
				lv_list[0].setAdapter(mAdapter[0]);
				break;
			case 1:
				mAdapter[1] = new MyArrayAdapter( this
						, android.R.layout.simple_list_item_1, getDataTwo());
				lv_list[1].setAdapter(mAdapter[1]);
				break;
			default:
				break;
			}

		}
		sl.setToScreen(currentTag);

	}
	private void registerListener() {
		rb[currentTag].setChecked(true);
//		sl.setChange(true);
		sl.setListener(listener);
		rg.setOnCheckedChangeListener(changeListener);
	}
	
OnScrollLayoutlistener listener = new OnScrollLayoutlistener() {

		@Override
		public void getViewItem(int whichScreen) {
			rb[whichScreen].setChecked(true);
			currentTag = whichScreen;
		}

	};
	
	OnCheckedChangeListener changeListener = new OnCheckedChangeListener() {

		@Override
		public void onCheckedChanged(RadioGroup group, int checkedId) {
			int i = 0;
			for (i = 0; i < length; i++) {
				if (checkedId == rb_id[i]) {
					currentTag = i;
					setCurrentPositionAndMoveCursor(currentTag);
				}

			}
			sl.switchToScreen(currentTag);
		}
	};
	
	/**
	 * 设置当前位置下标,并且移动游标
	 * 
	 * @param position
	 */
	private void setCurrentPositionAndMoveCursor(int position) {
		try {
			LayoutParams lp = (LayoutParams) cursorView.getLayoutParams();
			lp.leftMargin = position * cursorWidth;
			lp.width = cursorWidth;
			cursorView.setLayoutParams(lp);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	private class MyArrayAdapter extends ArrayAdapter<String> {
		List<String> mObjects;

		public MyArrayAdapter(Context context, int resource,
				List<String> objects) {
			super(context, resource, objects);
			mObjects = objects;
		}

		@Override
		public long getItemId(int position) {
			return getItem(position).hashCode();
		}

		public void add(int position, String item) {
			mObjects.add(position, item);
			notifyDataSetChanged();
		}
	}

	private ArrayList<String> getData() {
		ArrayList<String> result = new ArrayList<String>();
		for (int i = 0; i < 5; ++i) {
			result.add("Page One Hello item " + i);
		}
		return result;
	}

	private ArrayList<String> getDataTwo() {
		ArrayList<String> result = new ArrayList<String>();
		for (int i = 0; i < 20; ++i) {
			result.add("Page Two Hello item " + i);
		}
		return result;
	}
	
	 private ArrayList<MyOnTouchListener> onTouchListeners = new ArrayList<MyOnTouchListener>(10);
	    @Override
	    public boolean dispatchTouchEvent(MotionEvent ev) {
	        for (MyOnTouchListener listener : onTouchListeners) {
	            listener.dispatchTouchEvent(ev);
	        }
	        return super.dispatchTouchEvent(ev);
	    }

	    public void registerMyOnTouchListener(MyOnTouchListener myOnTouchListener) {
	        onTouchListeners.add(myOnTouchListener);
	    }

	    public void unregisterMyOnTouchListener(MyOnTouchListener myOnTouchListener) {
	        onTouchListeners.remove(myOnTouchListener);
	    }

	    public interface MyOnTouchListener {
	        public boolean dispatchTouchEvent(MotionEvent ev);
	    }
	
	
	private void initListener() {
    	myOnTouchListener = new MyOnTouchListener(){
    		@Override
            public boolean dispatchTouchEvent(MotionEvent ev) {
                return dispathTouchEvent(ev);
    	}
    	};
    	this.registerMyOnTouchListener(myOnTouchListener);
    }
    
    
    private boolean dispathTouchEvent(MotionEvent event){
    	if (mIsAnim) {  
            return false;  
        }
    	
    	final int action = event.getAction();  
        
        float x = event.getX();  
        float y = event.getY();
        switch (action) {
     case MotionEvent.ACTION_DOWN:
         lastY = y;
         lastX = x;
         return false;
     case MotionEvent.ACTION_MOVE:
         float dY = Math.abs(y - lastY);
         float dX = Math.abs(x - lastX);
         boolean down = y > lastY ? true : false;
         lastY = y;
         lastX = x;
         isUp = dX < 8 && dY > 8 && !mIsTitleHide && !down ;
         isDown = dX < 8 && dY > 8 && mIsTitleHide && down;
         if (isUp) {
             View view = this.mLayout;
             float[] f = new float[2];
             f[0] = 0.0F;
             f[1] = -mHeadLayout.getHeight();
             ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, "translationY", f);
             animator1.setInterpolator(new LinearInterpolator());
             animator1.setDuration(2);
             animator1.start();
             animator1.addListener(this);
             setMarginTop(mLayout.getHeight() - mHeadLayout.getHeight());
         } else if (isDown) {
			if (null != lv_list[currentTag].getChildAt(0)
					&& lv_list[currentTag].getChildAt(0).getTop() >= 0) {
				View view = this.mLayout;
				float[] f = new float[2];
				f[0] = -mHeadLayout.getHeight();
				f[1] = 0F;
				ObjectAnimator animator1 = ObjectAnimator.ofFloat(view,
						"translationY", f);
				animator1.setDuration(2);
				animator1.setInterpolator(new LinearInterpolator());
				animator1.start();
				animator1.addListener(this);
			} else if (lv_list[currentTag].getFirstVisiblePosition() == 0) {
				View view = this.mLayout;
				float[] f = new float[2];
				f[0] = -mHeadLayout.getHeight();
				f[1] = 0F;
				ObjectAnimator animator1 = ObjectAnimator.ofFloat(view,
						"translationY", f);
				animator1.setDuration(2);
				animator1.setInterpolator(new LinearInterpolator());
				animator1.start();
				animator1.addListener(this);
			} else {
				return false;
			}
         } else {
             return false;
         }
         mIsTitleHide = !mIsTitleHide;
         mIsAnim = true;
         break;
     default:
         return false;    
         
         
         
     }
    	
    	
		return false;
    	
    }

    public void setMarginTop(int page){
        RelativeLayout.LayoutParams layoutParam = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); 
        layoutParam.setMargins(0, page, 0, 0);
        sl.setLayoutParams(layoutParam);
        sl.invalidate();
    }

	@Override
	public void onAnimationCancel(Animator arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onAnimationEnd(Animator arg0) {
		// TODO Auto-generated method stub
		if(isDown){
			setMarginTop(mLayout.getHeight());
        }
         mIsAnim = false;  
	}

	@Override
	public void onAnimationRepeat(Animator arg0) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void onAnimationStart(Animator arg0) {
		// TODO Auto-generated method stub
	}
}

当向上滑动时,通过ObjectAnimator使得mHeadLayout的高度向上减小到0;

	         float[] f = new float[2];
             f[0] = 0.0F;
             f[1] = -mHeadLayout.getHeight();

当向下滑动时,如果当前页的listview不为空,当滑动到第一个item时,item距离父容器的顶部距离大于0时,layout的高度恢复到原有高度。

if (null != lv_list[currentTag].getChildAt(0)
					&& lv_list[currentTag].getChildAt(0).getTop() >= 0)

如果listview为空时;listview第一个item为0时,(listivew为空时 FirstVisiblePosition()为0),显示个人信息栏。

lv_list[currentTag].getFirstVisiblePosition() == 0

ScrollLayout

public class ScrollLayout extends ViewGroup {

	private Scroller mScroller;
	private VelocityTracker mVelocityTracker;

	private int mCurScreen;
	private int mDefaultScreen = 0;

	private static final int TOUCH_STATE_REST = 0;
	private static final int TOUCH_STATE_SCROLLING = 1;

	private static final int SNAP_VELOCITY = 600;

	private int mTouchState = TOUCH_STATE_REST;
	private int mTouchSlop;
	private float mLastMotionX;
	private float mLastMotionY;
	private OnScrollLayoutlistener listener;
	private boolean change;
	private boolean isTouch=true;
	private int mheight=0;//当y小于该值时应用于有横向滑动的view,在这个范围内此时ScrollLayout的触屏事件不起作用
	public ScrollLayout(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public ScrollLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		mScroller = new Scroller(context);

		mCurScreen = mDefaultScreen;
		mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		// if (change || changed) {
		int childLeft = 0;
		final int childCount = getChildCount();

		for (int i = 0; i < childCount; i++) {
			final View childView = getChildAt(i);
			if (childView.getVisibility() != View.GONE) {
				final int childWidth = childView.getMeasuredWidth();
				childView.layout(childLeft, 0, childLeft + childWidth, childView.getMeasuredHeight());
				childLeft += childWidth;
			}
		}
		// }
	}

	/**
	 * 设置监听事件
	 * 
	 * @param listener
	 */
	public void setListener(OnScrollLayoutlistener listener) {
		this.listener = listener;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);

		final int width = MeasureSpec.getSize(widthMeasureSpec);
		final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
		if (widthMode != MeasureSpec.EXACTLY) {
			throw new IllegalStateException("ScrollLayout only canmCurScreen run at EXACTLY mode!");
		}

		final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
		if (heightMode != MeasureSpec.EXACTLY) {
			throw new IllegalStateException("ScrollLayout only can run at EXACTLY mode!");
		}

		// The children are given the same width and height as the scrollLayout
		final int count = getChildCount();
		for (int i = 0; i < count; i++) {
			getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
		}
		scrollTo(mCurScreen * width, 0);
	}

	/**
	 * According to the position of current layout scroll to the destination
	 * page.
	 */
	public void snapToDestination() {
		final int screenWidth = getWidth();
		final int destScreen = (getScrollX() + screenWidth / 2) / screenWidth;
		snapToScreen(destScreen);
	}

	/**
	 * 选中的view滑动
	 * 
	 * @param whichScreen
	 */
	public void snapToScreen(int whichScreen) {
		// get the valid layout page
		whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
		if (getScrollX() != (whichScreen * getWidth())) {

			final int delta = whichScreen * getWidth() - getScrollX();
			mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 2);
			mCurScreen = whichScreen;
			invalidate(); // Redraw the layout
			if (listener != null) {
				listener.getViewItem(whichScreen);
			}
		}
	}

	/**
	 * 选中的view滑动
	 * 
	 * @param whichScreen
	 */
	public void switchToScreen(int whichScreen) {
		// get the valid layout page
		whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
		if (getScrollX() != (whichScreen * getWidth())) {

			final int delta = whichScreen * getWidth() - getScrollX();
			mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 2);
			mCurScreen = whichScreen;
			invalidate(); // Redraw the layout
		}
	}

	/**
	 * 选中的view设置
	 * 
	 * @param whichScreen
	 */
	public void setToScreen(int whichScreen) {
		whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
		mCurScreen = whichScreen;
		scrollTo(whichScreen * getWidth(), 0);
		invalidate(); // Redraw the layout
	}

	public int getCurScreen() {
		return mCurScreen;
	}

	@Override
	public void computeScroll() {
		if (mScroller.computeScrollOffset()) {
			scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
			postInvalidate();
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if (!isTouch) {
			return false;
		}
		if (mVelocityTracker == null) {
			mVelocityTracker = VelocityTracker.obtain();
		}
		mVelocityTracker.addMovement(event);

		final int action = event.getAction();
		final float x = event.getX();
		final float y = event.getY();
		

		switch (action) {
		case MotionEvent.ACTION_DOWN:
			if (!mScroller.isFinished()) {
				mScroller.abortAnimation();
			}
			mLastMotionX = x;
			break;

		case MotionEvent.ACTION_MOVE:
			int deltaX = (int) ((mLastMotionX - x));
			mLastMotionX = x;

			if ((mCurScreen == 0 && deltaX < 0) || (mCurScreen == (getChildCount() - 1) && deltaX > 0)) {
				deltaX = (int) (deltaX / 2.0);
			} else {
				deltaX = (int) (deltaX / 1.5);
			}

			scrollBy(deltaX, 0);
			break;

		case MotionEvent.ACTION_UP:
			// if (mTouchState == TOUCH_STATE_SCROLLING) {
			final VelocityTracker velocityTracker = mVelocityTracker;
			velocityTracker.computeCurrentVelocity(1000);
			int velocityX = (int) velocityTracker.getXVelocity();

			if (velocityX > SNAP_VELOCITY && mCurScreen > 0) {
				// Fling enough to move left
				snapToScreen(mCurScreen - 1);
			} else if (velocityX < -SNAP_VELOCITY && mCurScreen < getChildCount() - 1) {
				// Fling enough to move right
				snapToScreen(mCurScreen + 1);
			} else {
				snapToDestination();
			}

			if (mVelocityTracker != null) {
                try{
				    mVelocityTracker.recycle();
                }catch (IllegalStateException e){
                    e.printStackTrace();
                }
				mVelocityTracker = null;
			}
			// }
			mTouchState = TOUCH_STATE_REST;
			break;
		case MotionEvent.ACTION_CANCEL:
			mTouchState = TOUCH_STATE_REST;
			break;
		}

		return true;
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {

		final int action = ev.getAction();
		if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
			return true;
		}

		final float x = ev.getX();
		final float y = ev.getY();
		if(mheight>y ){
			return false;
		}
		switch (action) {
		case MotionEvent.ACTION_MOVE:
			final int xDiff = (int) Math.abs(mLastMotionX - x);
			if (xDiff > mTouchSlop) {
				mTouchState = TOUCH_STATE_SCROLLING;

			}
			break;

		case MotionEvent.ACTION_DOWN:
			mLastMotionX = 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;
	}

	/**
	 * 设置ScrollLayout是否重新加载界面
	 * 
	 * @param change
	 *            (true 则刷新) the change to set
	 */
	public void setChange(boolean change) {
		this.change = change;
	}

	/**
	 * @return the change
	 */
	public boolean isChange() {
		return change;
	}

	/**
	 * @param isTouch
	 *            the isTouch to set
	 */
	public void setTouch(boolean isTouch) {
		this.isTouch = isTouch;
	}

	/**
	 * @return the isTouch
	 */
	public boolean isTouch() {
		return isTouch;
	}

	public interface OnScrollLayoutlistener {
		/**
		 * 监听滑动到什么位置
		 * 
		 * @param whichScreen
		 *            位置
		 */
		public void getViewItem(int whichScreen);
	}
	
	/**
	 * @param isTouch
	 *            the isTouch to set
	 */
	public void setHeight(int mheight) {
		this.mheight = mheight;
	}
}

ScrollLayout

public class ScreenUtils {
    //获取屏幕的大小
    public static Screen getScreenPix(Context context) {
        DisplayMetrics dm = new DisplayMetrics();
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        windowManager.getDefaultDisplay().getMetrics(dm);
        return new Screen(dm.widthPixels,dm.heightPixels);
    }
    public static class Screen{
        
        public int widthPixels;
        public int heightPixels;
        
        public Screen(){
            
        }
        
        public Screen(int widthPixels,int heightPixels){
            this.widthPixels=widthPixels;
            this.heightPixels=heightPixels;
        }

        @Override
        public String toString() {
            return "("+widthPixels+","+heightPixels+")";
        }
        
    }
}

demo稍后上传。
###更新源码地址
DEMO链接地址。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值