框架学习三:仿美团购买框悬浮效果

转自夏神blog:http://blog.csdn.net/xiaanming/article/details/17761431

一、大概了解下

        美团网用的是Scrolling Tricks,但是只支持API11以上。夏神实现了的修改版可适用API11以下版本,说下夏神的思路:首先对ScrollView滚动监听,直接在OnScrollChanged()中就能获得滚动的Y值。

二、实现

第一步:界面引入自定义的ScrollView。注意需要两个地方,一个要固定的位置,另一个是实际显示的位置。第二步:添加自定义ScrollView的滚动监听事件。第三步:设置当主布局的状态或者控件的可见性发生改变时回调接口。

三、代码

1、自定义的ScrollView

package com.example.myscroll;

import android.widget.ScrollView;
import android.content.Context;  
import android.util.AttributeSet;  
/** 
 * @blog http://blog.csdn.net/xiaanming 
 *  
 * @author xiaanming 
 * 
 */  

public class MyScrollView extends ScrollView {

	private OnScrollListener onScrollListener;  

	public MyScrollView(Context context) {  
		this(context, null);  
	}  

	public MyScrollView(Context context, AttributeSet attrs) {  
		this(context, attrs, 0);  
	}  

	public MyScrollView(Context context, AttributeSet attrs, int defStyle) {  
		super(context, attrs, defStyle);  
	}  

	/** 
	 * 设置滚动接口 
	 * @param onScrollListener 
	 */  
	 public void setOnScrollListener(OnScrollListener onScrollListener) {  
		 this.onScrollListener = onScrollListener;  
	 }  


	 @Override  
	 public int computeVerticalScrollRange() {  
		 return super.computeVerticalScrollRange();  
	 }  


	 @Override  
	 protected void onScrollChanged(int l, int t, int oldl, int oldt) {  
		 super.onScrollChanged(l, t, oldl, oldt);  
		 if(onScrollListener != null){  
			 onScrollListener.onScroll(t);  
		 }  
	 }  

	 /** 
	  *  
	  * 滚动的回调接口 
	  *  
	  * @author xiaanming 
	  * 
	  */  
	 public interface OnScrollListener{  
		 /** 
		  * 回调方法, 返回MyScrollView滑动的Y方向距离 
		  * @param scrollY 
		  *              、 
		  */  
		 public void onScroll(int scrollY);  
	 }  
}
界面中引入:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:id="@+id/parent_layout"  
    xmlns:tools="http://schemas.android.com/tools"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:orientation="vertical" >  
  
    <ImageView  
        android:id="@+id/imageView1"  
        android:layout_width="match_parent"  
        android:layout_height="45dip"  
        android:scaleType="centerCrop"  
        android:src="@drawable/navigation_bar" />  
  
    <com.example.myscroll.MyScrollView  
        android:id="@+id/scrollView"  
        android:layout_width="fill_parent"  
        android:layout_height="fill_parent" >  
  
        <FrameLayout  
            android:layout_width="match_parent"  
            android:layout_height="wrap_content" >  
  
            <LinearLayout  
                android:layout_width="match_parent"  
                android:layout_height="wrap_content"  
                android:orientation="vertical" >  
  
                <ImageView  
                    android:id="@+id/iamge"  
                    android:layout_width="match_parent"  
                    android:layout_height="wrap_content"  
                    android:background="@drawable/pic"  
                    android:scaleType="centerCrop" />  
  
                <include  
                    android:id="@+id/buy"  
                    layout="@layout/buy_layout" />  
  
                <ImageView  
                    android:layout_width="match_parent"  
                    android:layout_height="wrap_content"  
                    android:background="@drawable/one"  
                    android:scaleType="centerCrop" />  
  
                <ImageView  
                    android:layout_width="match_parent"  
                    android:layout_height="wrap_content"  
                    android:background="@drawable/one"  
                    android:scaleType="centerCrop" />  
  
                <ImageView  
                    android:layout_width="match_parent"  
                    android:layout_height="wrap_content"  
                    android:background="@drawable/one"  
                    android:scaleType="centerCrop" />  
            </LinearLayout>  
  
            <include  
                android:id="@+id/top_buy_layout"  
                layout="@layout/buy_layout" />
        </FrameLayout>  
    </com.example.myscroll.MyScrollView>  
  
</LinearLayout> 
2、代码实现

public class MainActivity extends Activity implements OnScrollListener {

	/** 
	 * 自定义的MyScrollView 
	 */  
	private MyScrollView myScrollView;  
	/** 
	 * 在MyScrollView里面的购买布局 
	 */  
	private LinearLayout mBuyLayout;  
	/** 
	 * 位于顶部的购买布局 
	 */  
	private LinearLayout mTopBuyLayout;  

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		myScrollView = (MyScrollView) findViewById(R.id.scrollView);  
		mBuyLayout = (LinearLayout) findViewById(R.id.buy);  
		mTopBuyLayout = (LinearLayout) findViewById(R.id.top_buy_layout);  

		myScrollView.setOnScrollListener(this);  

		//当布局的状态或者控件的可见性发生改变回调的接口  
		findViewById(R.id.parent_layout).getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {  

			@Override  
			public void onGlobalLayout() {  
				//这一步很重要,使得上面的购买布局和下面的购买布局重合  
				onScroll(myScrollView.getScrollY());  

			}  
		});  
	}

	@Override
	public void onScroll(int scrollY) {
		int mBuyLayout2ParentTop = Math.max(scrollY, mBuyLayout.getTop());  
		mTopBuyLayout.layout(0, mBuyLayout2ParentTop, mTopBuyLayout.getWidth(), mBuyLayout2ParentTop + mTopBuyLayout.getHeight());  
	}

}

四、含有多个购买布局的效果

即下一个购买布局将上一个购买布局顶上去,使用方法也简单:引入自定义ScrollView,不同于上面的;然后将需要设置的布局设置Tag为sticky。如:

<FrameLayout  
       android:layout_width="fill_parent"  
       android:layout_height="100dip"  
       android:background="#ff00ffff"  
       android:tag="sticky" >  
  
       <Button  
           android:id="@+id/button"  
           android:layout_width="fill_parent"  
           android:layout_height="wrap_content"  
           android:text="Button" />  
</FrameLayout>  

自定义ScrollView:

package com.example.stikyscrollview;

import java.util.LinkedList;
import java.util.List;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ScrollView;

public class StickyScrollView extends ScrollView {
	private static final String STICKY = "sticky";
	private View mCurrentStickyView ;
	private Drawable mShadowDrawable;
	private List<View> mStickyViews;
	private int mStickyViewTopOffset;
	private int defaultShadowHeight = 10;
	private float density;
	private boolean redirectTouchToStickyView;
	
	/**
	 * 当点击Sticky的时候,实现某些背景的渐变
	 */
	private Runnable mInvalidataRunnable = new Runnable() {
		
		@Override
		public void run() {
			if(mCurrentStickyView != null){
				int left = mCurrentStickyView.getLeft();
				int top = mCurrentStickyView.getTop();
				int right = mCurrentStickyView.getRight();
				int bottom = getScrollY() + (mCurrentStickyView.getHeight() + mStickyViewTopOffset);
				
				invalidate(left, top, right, bottom);
			}
			
			postDelayed(this, 16);
			
			
		}
	};
	
	
	public StickyScrollView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public StickyScrollView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		mShadowDrawable = context.getResources().getDrawable(R.drawable.sticky_shadow_default);
		mStickyViews = new LinkedList<View>();
		density = context.getResources().getDisplayMetrics().density;
	}

	/**
	 * 找到设置tag的View
	 * @param viewGroup
	 */
	private void findViewByStickyTag(ViewGroup viewGroup){
		int childCount = ((ViewGroup)viewGroup).getChildCount();
		for(int i=0; i<childCount; i++){
			View child = viewGroup.getChildAt(i);
			
			if(getStringTagForView(child).contains(STICKY)){
				mStickyViews.add(child);
			}
			
			if(child instanceof ViewGroup){
				findViewByStickyTag((ViewGroup)child);
			}
		}
	}
	
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
		if(changed){
			findViewByStickyTag((ViewGroup)getChildAt(0));
		}
		showStickyView();
	}

	@Override
	protected void onScrollChanged(int l, int t, int oldl, int oldt) {
		super.onScrollChanged(l, t, oldl, oldt);
		showStickyView();
	}
	
	/**
	 * 
	 */
	private void showStickyView(){
		View curStickyView = null;
		View nextStickyView = null;
		
		for(View v : mStickyViews){
			int topOffset = v.getTop() - getScrollY();
			
			if(topOffset <= 0){
				if(curStickyView == null || topOffset > curStickyView.getTop() - getScrollY()){
					curStickyView = v;
				}
			}else{
				if(nextStickyView == null || topOffset < nextStickyView.getTop() - getScrollY()){
					nextStickyView = v;
				}
			}
		}
		
		if(curStickyView != null){
			mStickyViewTopOffset = nextStickyView == null ? 0 : Math.min(0, nextStickyView.getTop() - getScrollY() - curStickyView.getHeight());
			mCurrentStickyView = curStickyView;
			post(mInvalidataRunnable);
		}else{
			mCurrentStickyView = null;
			removeCallbacks(mInvalidataRunnable);
		}
	}
	
	
	private String getStringTagForView(View v){
		Object tag = v.getTag();
		return String.valueOf(tag);
	}

	/**
	 * 将sticky画出来
	 */
	@Override
	protected void dispatchDraw(Canvas canvas) {
		super.dispatchDraw(canvas);
		if(mCurrentStickyView != null){
			//先保存起来
			canvas.save();
			//将坐标原点移动到(0, getScrollY() + mStickyViewTopOffset)
			canvas.translate(0, getScrollY() + mStickyViewTopOffset);
			
			if(mShadowDrawable != null){
				int left = 0;
				int top = mCurrentStickyView.getHeight() + mStickyViewTopOffset;
				int right = mCurrentStickyView.getWidth();
				int bottom = top + (int)(density * defaultShadowHeight + 0.5f);
				mShadowDrawable.setBounds(left, top, right, bottom);
				mShadowDrawable.draw(canvas);
			}
			
			canvas.clipRect(0, mStickyViewTopOffset, mCurrentStickyView.getWidth(), mCurrentStickyView.getHeight());
			
			mCurrentStickyView.draw(canvas);
			
			//重置坐标原点参数
			canvas.restore();
		}
	}
	
	
	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		if(ev.getAction() == MotionEvent.ACTION_DOWN){
			redirectTouchToStickyView = true;
		}
		
		if(redirectTouchToStickyView){
			redirectTouchToStickyView = mCurrentStickyView != null;
			
			if(redirectTouchToStickyView){
				redirectTouchToStickyView = ev.getY() <= (mCurrentStickyView
						.getHeight() + mStickyViewTopOffset)
						&& ev.getX() >= mCurrentStickyView.getLeft()
						&& ev.getX() <= mCurrentStickyView.getRight();
			}
		}
		
		if (redirectTouchToStickyView) {
			ev.offsetLocation(0, -1 * ((getScrollY() + mStickyViewTopOffset) - mCurrentStickyView.getTop()));
		}
		return super.dispatchTouchEvent(ev);
	}
	
	private boolean hasNotDoneActionDown = true;

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		if(redirectTouchToStickyView){
			ev.offsetLocation(0, ((getScrollY() + mStickyViewTopOffset) - mCurrentStickyView.getTop()));
		} 
		
		if(ev.getAction()==MotionEvent.ACTION_DOWN){
			hasNotDoneActionDown = false;
		}
		
		if(hasNotDoneActionDown){
			MotionEvent down = MotionEvent.obtain(ev);
			down.setAction(MotionEvent.ACTION_DOWN);
			super.onTouchEvent(down);
			hasNotDoneActionDown = false;
		}
		
		if(ev.getAction()==MotionEvent.ACTION_UP || ev.getAction()==MotionEvent.ACTION_CANCEL){
			hasNotDoneActionDown = true;
		}
		return super.onTouchEvent(ev);
	}
}

自定义ScrollView用到的xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >

    <gradient
        android:angle="90"
        android:centerColor="#22222222"
        android:endColor="#AA222222"
        android:startColor="#00222222" >
    </gradient>

</shape>



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值