首页 > 代码库 > 安卓自定义Scrollview,实现卷帘效果

安卓自定义Scrollview,实现卷帘效果

这个效果和网易彩票安卓客户端的双色球主页下拉效果差不多。公司也是做彩票的,想要一个这样的效果。开始有思路,但是实现起来还是挺麻烦的,逻辑不好理,经过一番研究,果然不负有心人。终于完美实现。分享给大家......

默认效果:

技术分享

向下滑动后:

技术分享


这里其实有很多细节,比如下滑高度小于卷帘的1/2,就弹回原处,大于1/2就自动弹到底,有一个弹性的效果。

当然上滑的时候也是,滑动距离小于1/2,还是留在底部,大于1/2,就弹回最顶部。

当你一进来,不下拉,就不会弹出卷帘,往上滑的时候就会滑动下面的内容,再次往下滑动,也不会弹出卷帘,而是要释放,再滑动一次,这样可以明显知道卷帘和下面的不是连在一起的。


布局文件代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#f6f6f6"
    android:orientation="vertical" >

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="52dip"
        android:layout_gravity="center"
        android:background="#d80702" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:gravity="center"
            android:paddingLeft="15dip"
            android:text="双色球-普通"
            android:textColor="#ffffff"
            android:textSize="18sp" />
    </RelativeLayout>

    <TextView
        android:id="@+id/lisi_textiview"
        android:layout_width="fill_parent"
        android:layout_height="1dip"
        android:background="#517688"
        android:gravity="center"
        android:text="历史记录1\n历史记录2\n历史记录3\n历史记录4\n历史记录5\n历史记录6\n历史记录7\n历史记录8\n历史记录9\n历史记录10" />

    <com.myrollerscrollview.view.MyScrollView
        android:id="@+id/main_scrollview"
        android:layout_width="fill_parent"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:scrollbars="vertical" >

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:background="#7d7d7d"
            android:focusable="true"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/main_textiview"
                android:layout_width="fill_parent"
                android:layout_height="40dip"
                android:background="#aabbcc"
                android:gravity="center"
                android:text="这里是彩期显示区域"
                android:textColor="#222222"
                android:visibility="visible" />

            <TextView
                android:id="@+id/main_textiview1"
                android:layout_width="fill_parent"
                android:layout_height="550dip"
                android:background="#cbacba"
                android:gravity="center"
                android:text="这里是选球界面,红球"
                android:textColor="#d80702"
                android:visibility="visible" />

            <TextView
                android:id="@+id/main_textiview2"
                android:layout_width="fill_parent"
                android:layout_height="550dip"
                android:background="#abcabc"
                android:gravity="center"
                android:text="这里是选球界面,篮球"
                android:textColor="#0000ff"
                android:visibility="visible" />
        </LinearLayout>
    </com.myrollerscrollview.view.MyScrollView>

    <!-- 金额,确认选号 -->

    <View
        android:layout_width="fill_parent"
        android:layout_height="1px"
        android:background="#dedede" />

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="52dip"
        android:background="#f8f8f8"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/ssq_main_txt_mainConfirm"
            android:layout_width="80dp"
            android:layout_height="36dp"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="10dp"
            android:gravity="center"
            android:text="投注"
            android:textColor="#222222"
            android:textSize="16sp"
            android:textStyle="bold"
            android:typeface="monospace" />
    </RelativeLayout>

</LinearLayout>

主类Activity代码很简单:

package com.myrollerscrollview.activity;

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.widget.TextView;

import com.myrollerscrollview.R;
import com.myrollerscrollview.listener.MyScrollViewListener;
import com.myrollerscrollview.view.MyScrollView;

//主界面
public class MainActivity extends Activity implements MyScrollViewListener {
	private MyScrollView scrllon_view;
	private TextView lisi_textview;
	private TextView main_textview;
	private TextView main_textview1;
	private TextView main_textview2;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);//去掉标题栏
		setContentView(R.layout.activity_main);
		

		scrllon_view = (MyScrollView) findViewById(R.id.main_scrollview);
		lisi_textview = (TextView) findViewById(R.id.lisi_textiview);
		main_textview = (TextView) findViewById(R.id.main_textiview);
		main_textview1 = (TextView) findViewById(R.id.main_textiview1);
		main_textview2 = (TextView) findViewById(R.id.main_textiview2);

		scrllon_view.setMaxHeight(350);
		scrllon_view.topView = this.lisi_textview;
	}

	@Override
	public void onScrollChanged(MyScrollView scrollView, int x, int y, int oldx, int oldy) {
		 //System.out.println("x=" + x + "  y=" + y + "  oldx=" + oldx + " oldy=" + oldy);
	}

}

重点是这里  自定义的Scrollview

package com.myrollerscrollview.view;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ScrollView;

import com.myrollerscrollview.listener.MyScrollViewListener;

/**
 * 自定卷帘Scrollview
 * 
 * @author lengguoxing
 *
 * 引用地址:com.myrollerscrollview.view.MyScrollView
 */
 
public class MyScrollView  extends ScrollView {  
	private MyScrollViewListener scrollViewListener = null;  
	private MyScrollView myScrollView = this;
	
	public View topView;//顶部卷帘
	public boolean isTopViewAllGone = true;//卷帘完全隐藏
	
	private int downMoveDistance = 0;// 向下滑动的距离
	private int upMoveDistance = 0;// 滑动到底时,向上滑动的距离
	
	public int maxHeight = 100;//这个是可以用户通过方法setMaxHeight自定义的
	
    public MyScrollView(Context context) {  
        super(context);  
    }  
  
    public MyScrollView(Context context, AttributeSet attrs,  
            int defStyle) {  
        super(context, attrs, defStyle);  
    }  
  
    public MyScrollView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
    }  
  
    public void setScrollViewListener(MyScrollViewListener scrollViewListener) {  
        this.scrollViewListener = scrollViewListener;  
    }  
  
    public int getMaxHeight() {
		return maxHeight;
	}

	public void setMaxHeight(int maxHeight) {
		this.maxHeight = maxHeight;
	}
	
    @Override  
    protected void onScrollChanged(int x, int y, int oldx, int oldy) {  
        super.onScrollChanged(x, y, oldx, oldy);  
        if (scrollViewListener != null ) {  
            scrollViewListener.onScrollChanged(this, x, y, oldx, oldy);  
        }  
    }  
    
    int downY = 0;// 按下时候焦点的y坐标
	int moveY = 0;// 滑动时候存储焦点的y坐标
	int upY = 0;// 释放时候焦点的y坐标
	
	int downScrollY = 0;//滑动开始时滚动条的Y坐标
	android.widget.LinearLayout.LayoutParams lp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, 0); 
  
	/**
	 *
	 * topView.getBottom() - topView.getTop() == maxHeight  卷帘完全显示的判断
	 * topView.getBottom() == topView.getTop()   卷帘完全隐藏的判断
	 * myScrollView.getScrollY()==0可以判断是否滚动了。
	 */
	
	@Override
    public boolean onTouchEvent(MotionEvent event) {
    	//System.out.println("onTouchEvent----------------------getHeight=" + this.getHeight() + "    ");
    	switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN://按下的时候
			//System.out.println("----------------action down=(" + event.getX() +", " + event.getY() + ")");
			
			downY = (int) event.getY();
			
			downScrollY = myScrollView.getScrollY();
			
			//upMoveDistance = downMoveDistance;
			upMoveDistance = 0;
			
			return super.onTouchEvent(event);
		case MotionEvent.ACTION_MOVE://滑动的时候
			//System.out.println("----------------action move=(" + event.getX() +", " + event.getY() + ")");
			//System.out.println("----------------myScrollView " + myScrollView.getTop() + "   " + myScrollView.getBottom());
			//System.out.println("---------------------topView " + topView.getTop() + "   " + topView.getBottom());
			//System.out.println("-----myScrollView.getScrollY " + myScrollView.getScrollY() + "       maxheight=" + maxHeight); 
			//System.out.println("-----------------downMoveDistance= " + downMoveDistance + "     upMoveDistance=" + upMoveDistance);
			if(topView.getBottom() - topView.getTop() == maxHeight){//卷帘完全显示
				downMoveDistance = maxHeight;
				upMoveDistance = 0;
			} 
			
			if(topView.getBottom() == topView.getTop()){//卷帘完全隐藏
				isTopViewAllGone = true;
				downMoveDistance = 0;
				upMoveDistance = maxHeight;
			}else{
				isTopViewAllGone = false;
			}
			
			moveY = (int) event.getY();

			int YY = moveY - downY;

			if (YY > 0) {// 向下滑动
				int tempMove = (moveY - downY) % maxHeight;
				if (tempMove > downMoveDistance) {
					downMoveDistance = tempMove;
				}
				if (downMoveDistance > 0) {
					//向下滑的条件是:1、未滚动到底;2、滚动条还在顶部;3、滚动条本来就在顶部
					if (downMoveDistance <= maxHeight && myScrollView.getScrollY() == 0 && downScrollY == 0) {
						lp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.FILL_PARENT, downMoveDistance);
						lp.setMargins(0, 0, 0, 0);
						topView.setPadding(0, 0, 0, 0);
						topView.setLayoutParams(lp);
					} 
				}
			} else if (YY < 0 && downMoveDistance > 0) {// 向上滑动 //这里的逻辑有点问题
				int tempMove = (downY - moveY) % maxHeight;
				if (tempMove > upMoveDistance) { // 这里有问题
					upMoveDistance = tempMove;
				}

				if (upMoveDistance > 0) {
					if (upMoveDistance < maxHeight && myScrollView.getScrollY() == 0) {
						lp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.FILL_PARENT, downMoveDistance - upMoveDistance);
						lp.setMargins(0, 0, 0, 0);
						topView.setPadding(0, 0, 0, 0);
						topView.setLayoutParams(lp);
 
					}  
					 
				}
			}
			 
			if(isTopViewAllGone){
				return super.onTouchEvent(event);
			}else{
				return false;
			}
		case MotionEvent.ACTION_UP:
			//System.out.println("----------------action up=(" + event.getX() +", " + event.getY() + ")");
			
			upY = (int) event.getY();
			int YY2 = (upY - downY);
			if (YY2 > 0) {// 向下滑动
				if (YY2 < maxHeight / 2 && myScrollView.getScrollY() == 0 && downScrollY == 0) {// 滑动距离小于一半,自动弹回原处
					lp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.FILL_PARENT, 0);
					topView.setLayoutParams(lp);
					downMoveDistance = 0;
				} else if (YY2 >= maxHeight / 2 && myScrollView.getScrollY() == 0 && downScrollY == 0) {// 滑动距离大于一半,加速最底部
					lp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.FILL_PARENT, maxHeight);
					topView.setLayoutParams(lp);
					downMoveDistance = maxHeight;
				}
			} else if (YY2 < 0) {// 向上滑动
				YY2 = -YY2;//
				if (YY2 < maxHeight / 2 && myScrollView.getScrollY() == 0) {// 向上滑动距离小于一半,自动弹回最底部
					lp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.FILL_PARENT, maxHeight);
					topView.setLayoutParams(lp);
					downMoveDistance = maxHeight;
					upMoveDistance = 0;
				} else if (YY2 >= maxHeight / 2 && myScrollView.getScrollY() == 0) {// 向上滑动距离大于一半,加速原处
					lp = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.FILL_PARENT, 0);
					topView.setLayoutParams(lp);
					downMoveDistance = 0;
					upMoveDistance = maxHeight;
				}
			}
			
			return super.onTouchEvent(event);
		} 
    	return super.onTouchEvent(event);
    }
  
} 

核心代码就在那个onTouchEvent方法里面

MotionEvent.ACTION_MOVE 控制滑动效果

MotionEvent.ACTION_UP 控制弹性效果


这里的几个判断很重要:

topView.getBottom() - topView.getTop() == maxHeight  卷帘完全显示的判断
topView.getBottom() == topView.getTop()   卷帘完全隐藏的判断
myScrollView.getScrollY()==0可以判断是否滚动了。


里面的注释很详细,就不一一介绍了。这里附上下载的地址  点击打开链接











安卓自定义Scrollview,实现卷帘效果