activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/background_img" android:layout_width="match_parent" android:layout_height="400dp" android:layout_marginTop="-100dp" android:scaleType="fitXY" android:src="@drawable/pic3" /> <com.harvic.pullscrollviewdemo.PullScrollView android:id="@+id/pullscrollview" android:layout_width="match_parent" android:layout_height="match_parent"> <TableLayout android:id="@+id/table_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="150dp"/> </com.harvic.pullscrollviewdemo.PullScrollView> </FrameLayout>
package com.harvic.pullscrollviewdemo; import android.app.Activity; import android.graphics.Color; import android.os.Bundle; import android.view.Gravity; import android.view.View; import android.widget.ImageView; import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity { private TableLayout mMainLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //加载布局 setContentView(R.layout.activity_main); //背景图片 ImageView headerView = (ImageView)findViewById(R.id.background_img); //自定义的PullScrollView PullScrollView pullScrollView = (PullScrollView)findViewById(R.id.pullscrollview); //??? pullScrollView.setmHeaderView(headerView); //自定义的PullScrollView里面的元素 mMainLayout = (TableLayout) findViewById(R.id.table_layout); //显示数据 showTable(); } public void showTable() { TableRow.LayoutParams layoutParams = new TableRow.LayoutParams( TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT); layoutParams.gravity = Gravity.CENTER; layoutParams.leftMargin = 30; layoutParams.bottomMargin = 10; layoutParams.topMargin = 10; for (int i = 0; i < 30; i++) { TableRow tableRow = new TableRow(this); TextView textView = new TextView(this); textView.setText("Test pull down scroll view " + i); textView.setTextSize(20); textView.setPadding(15, 15, 15, 15); tableRow.addView(textView, layoutParams); if (i % 2 != 0) { tableRow.setBackgroundColor(Color.LTGRAY); } else { tableRow.setBackgroundColor(Color.WHITE); } final int n = i; tableRow.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getApplicationContext(), "Click item " + n, Toast.LENGTH_SHORT).show(); } }); mMainLayout.addView(tableRow); } } }
package com.harvic.pullscrollviewdemo; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Point; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.animation.TranslateAnimation; import android.widget.ScrollView; public class PullScrollView extends ScrollView { // 底部View private View mContentView; // 是否禁止控件本身的的移动 boolean mEnableMoving = false; /** * 阻尼系数,越小阻力就越大. */ private static final float SCROLL_RATIO = 0.5f; private int mContentTop, mContentBottom; private int mHeaderCurTop, mHeaderCurBottom; // 底部图片View private View mHeaderView; // 获取手指按下时候的Y轴坐标 public static int dip2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } /** * 构造方法 */ public PullScrollView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public PullScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } // 用户定义的headview高度 private int mHeaderHeight = 0; private void init(Context context, AttributeSet attrs) { mHeaderHeight = dip2px(context, 400); } /** * 函数解析完毕后,调用的函数 */ @Override protected void onFinishInflate() { if (getChildCount() > 0) { mContentView = getChildAt(0); } super.onFinishInflate(); } public void setmHeaderView(View view) { mHeaderView = view; } // 初始点击位置 private Point mTouchPoint = new Point(); // 头部图片的初始化位置 private Rect mHeadInitRect = new Rect(); // ScrollView的contentView的初始化位置 private Rect mContentInitRect = new Rect(); // 标识当前view是否移动 boolean mIsMoving = false; // 是否使用layout函数移动布局 boolean mIsLayout = false; @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { // 保存原始位置 mTouchPoint.set((int) event.getX(), (int) event.getY()); // 初始化头部图片矩形区域 mHeadInitRect.set(mHeaderView.getLeft(), mHeaderView.getTop(), mHeaderView.getRight(), mHeaderView.getBottom()); // 初始化SrollView的孩子View的矩形区域 mContentInitRect.set(mContentView.getLeft(), mContentView.getTop(), mContentView.getRight(), mContentView.getBottom()); mIsMoving = false; // 如果当前不是从初始化位置开始滚动的话,就不让用户拖拽 if (getScrollY() == 0) { mIsLayout = true; } } else if (event.getAction() == MotionEvent.ACTION_MOVE) { // 如果当前的事件是我们要处理的事件时,比如现在的下拉,这时候,我们就不能让子控件来处理这个事件 // 这里就需要把它截获,不传给子控件,更不能让子控件消费这个事件 // 不然子控件的行为就可能与我们的相冲突 // 移动的距离 int deltaY = (int) event.getY() - mTouchPoint.y; deltaY = deltaY < 0 ? 0 : (deltaY > mHeaderHeight ? mHeaderHeight : deltaY); if (deltaY > 0 && deltaY >= getScrollY() && getScrollY() == 0) { onTouchEvent(event); return true; } } return super.onInterceptTouchEvent(event); } /** * 处理手势事件 */ @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_MOVE: { // int deltaY = (int) event.getY() - mTouchPoint.y; deltaY = deltaY < 0 ? 0 : (deltaY > mHeaderHeight ? mHeaderHeight : deltaY); if (deltaY > 0 && deltaY >= getScrollY() && mIsLayout) { float headerMoveHeight = deltaY * 0.5f * SCROLL_RATIO; mHeaderCurTop = (int) (mHeadInitRect.top + headerMoveHeight); mHeaderCurBottom = (int) (mHeadInitRect.bottom + headerMoveHeight); float contentMoveHeight = deltaY * SCROLL_RATIO; mContentTop = (int) (mContentInitRect.top + contentMoveHeight); mContentBottom = (int) (mContentInitRect.bottom + contentMoveHeight); if (mContentTop <= mHeaderCurBottom) { mHeaderView.layout(mHeadInitRect.left, mHeaderCurTop, mHeadInitRect.right, mHeaderCurBottom); mContentView.layout(mContentInitRect.left, mContentTop, mContentInitRect.right, mContentBottom); mIsMoving = true; mEnableMoving = true; } } } break; case MotionEvent.ACTION_UP: { // 反弹 if (mIsMoving) { mHeaderView.layout(mHeadInitRect.left, mHeadInitRect.top, mHeadInitRect.right, mHeadInitRect.bottom); TranslateAnimation headAnim = new TranslateAnimation(0, 0, mHeaderCurTop - mHeadInitRect.top, 0); headAnim.setDuration(200); mHeaderView.startAnimation(headAnim); mContentView.layout(mContentInitRect.left, mContentInitRect.top, mContentInitRect.right, mContentInitRect.bottom); TranslateAnimation contentAnim = new TranslateAnimation(0, 0, mContentTop - mContentInitRect.top, 0); contentAnim.setDuration(200); mContentView.startAnimation(contentAnim); mIsMoving = false; } mEnableMoving = false; mIsLayout = false; } break; } // 禁止控件本身的滑动. // 这句厉害,如果mEnableMoving返回TRUE,那么就不会执行super.onTouchEvent(event) // 只有返回FALSE的时候,才会执行super.onTouchEvent(event) // 禁止控件本身的滑动,就会让它,本来应有的滑动就不会滑动了,比如向上滚动 return mEnableMoving || super.onTouchEvent(event); } }