android 自定义Scrollview实现淘宝二层楼效果新版微信小程序下拉效果
由于最近一段时间真的是太忙了,没有顾上即使更新博客,还请粉丝们见谅,最近要实现这样一个效果,这个效果跟淘宝二层楼和新版微信7.0的下拉小程序效果差不多,百般查找真心没有找到自己合适的,没办法只能自撸了,来看效果。
来,二话不说了,直接上代码吧。
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ScrollView;
import android.widget.Scroller;
/**
* Created by zhaoshaohe on 2019.4.1
*/
public class StickyScrollView extends ScrollView {
public static final String TAG = StickyScrollView.class.getSimpleName();
public static final int PAGE_TOP = 0;
public static final int PAGE_BOTTOM = 1;
public static final double PERCENT = 0.4;
public static final int ANIMATION_DURATION = 180;
public static final int TOUCH_DURATION = 150;
private ViewGroup mChildLayout;
private View mTopChildView;
private Context mContext;
private OnPageChangeListener onPageChangeListener;
private boolean isScrollAuto; //判断是否自由滚动
private Scroller mScroller; //滑动类
private int screenHeight; //屏幕高度
private int offsetDistance; //topview的高度与屏幕的高度差
private int topChildHeight; //topview的高度
private boolean isTouch; //用户是否在触控屏幕
private int currentPage; //值为0时屏幕显示topview,值为1时屏幕显示bottomview
private long downTime; //用户按下屏幕的时间戳
private long upTime; //用户抬起时的时间戳
private int downY; //用户按下屏幕的y坐标
private int upY; //用户抬起的y坐标
private boolean isPageChange; //页面是否切换
public StickyScrollView(Context context) {
super(context);
this.mContext = context;
}
public StickyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
}
public StickyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
}
public void setOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
this.onPageChangeListener = onPageChangeListener;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mChildLayout = (ViewGroup) getChildAt(0);
mTopChildView = mChildLayout.getChildAt(0);
topChildHeight = mTopChildView.getMeasuredHeight();
screenHeight = getMeasuredHeight();
offsetDistance = topChildHeight - screenHeight;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
isTouch = true;
downY = (int) ev.getY();
downTime = System.currentTimeMillis();
if (mScroller != null) {
mScroller.forceFinished(true);
mScroller = null;
}
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
isTouch = false;
upY = (int) ev.getY();
upTime = System.currentTimeMillis();
//用户手指在屏幕上的时间
long duration = upTime - downTime;
//这里要确保点击事件不失效
if (Math.abs(upY - downY) > 50) {
Log.e(TAG, ">>>ISN_T CLICK:" + Math.abs(upY - downY));
if (currentPage == PAGE_TOP) {
//下面的判断已经能确定用户是否往上滑
if (getScrollY() > offsetDistance) {
mScroller = new Scroller(mContext);
if (getScrollY() < (screenHeight * PERCENT + offsetDistance) && duration > TOUCH_DURATION) {
isPageChange = false;
scrollToTarget(PAGE_TOP);
} else {
//切换到下界面
isPageChange = true;
isScrollAuto = duration < TOUCH_DURATION ? true : false;
scrollToTarget(PAGE_BOTTOM);
currentPage = PAGE_BOTTOM;
}
return false;
}
} else {
if (getScrollY() < topChildHeight) {
mScroller = new Scroller(mContext);
if (getScrollY() < (topChildHeight - screenHeight * PERCENT) || duration < TOUCH_DURATION) {
//切换到上界面
isPageChange = true;
isScrollAuto = duration < TOUCH_DURATION ? true : false;
scrollToTarget(PAGE_TOP);
currentPage = PAGE_TOP;
} else {
isPageChange = false;
scrollToTarget(PAGE_BOTTOM);
}
return false;
}
}
}
break;
}
return super.dispatchTouchEvent(ev);
}
/**
* 滚动到指定位置
*/
private void scrollToTarget(int currentPage) {
int delta;
if (currentPage == PAGE_TOP) {
delta = getScrollY() - offsetDistance;
mScroller.startScroll(0, getScrollY(), 0, -delta, isScrollAuto == true ? ANIMATION_DURATION : (int) (Math.abs(delta) * 0.4));
} else if (currentPage == PAGE_BOTTOM) {
delta = getScrollY() - topChildHeight;
mScroller.startScroll(0, getScrollY(), 0, -delta, isScrollAuto == true ? ANIMATION_DURATION : (int) (Math.abs(delta) * 0.4));
}
postInvalidate();
}
@Override
public void computeScroll() {
// 调用startScroll的时候scroller.computeScrollOffset()返回true
super.computeScroll();
if (mScroller == null) {
return;
}
if (mScroller.computeScrollOffset()) {
this.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
if (mScroller.isFinished()) {
mScroller = null;
if (onPageChangeListener != null && isPageChange) onPageChangeListener.OnPageChange(currentPage);
}
}
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
//滚动时的监听,当用户触屏滑动时不监听,t == getScrollY
if (currentPage == PAGE_TOP) {
if (getScrollY() > offsetDistance && !isTouch) {
if (mScroller == null) {
//用于控制当滑动到分界线时停止滚动
scrollTo(0, offsetDistance);
} else {
scrollToTarget(PAGE_TOP);
}
}
} else if (currentPage == PAGE_BOTTOM) {
if (getScrollY() < topChildHeight && !isTouch) {
if (mScroller == null) {
scrollTo(0, topChildHeight);
} else {
scrollToTarget(PAGE_BOTTOM);
}
}
}
}
/**
* 切换页面完成后的回调
*/
public interface OnPageChangeListener {
void OnPageChange(int currentPage);
}
}
这是自定义的粘性StickyScrollview,主要逻辑都在这儿了。
来看如何使用:
<com.example.zsh.scrollview_demo.StickyScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/sticky_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- child_0 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/item_1"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="2dp"
android:scaleType="centerCrop"
android:src="@mipmap/ic_launcher" />
<ImageView
android:id="@+id/item_2"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="2dp"
android:scaleType="centerCrop"
android:background="@mipmap/ic_launcher" />
<ImageView
android:id="@+id/item_3"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="2dp"
android:scaleType="centerCrop"
android:background="@mipmap/ic_launcher" />
<ImageView
android:id="@+id/item_4"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="2dp"
android:scaleType="centerCrop"
android:background="@mipmap/ic_launcher" />
<ImageView
android:id="@+id/item_5"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
android:scaleType="centerCrop"
android:background="@mipmap/ic_launcher" />
</LinearLayout>
<!-- child_1 -->
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<!-- divider -->
<TextView
android:layout_width="match_parent"
android:layout_height="30dp"
android:gravity="center"
android:textColor="@android:color/white"
android:textSize="16sp"
android:text="Divider"
android:background="#00AA50"/>
<ImageView
android:id="@+id/item_6"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="2dp"
android:scaleType="centerCrop"
android:background="@color/colorAccent" />
<ImageView
android:id="@+id/item_7"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="2dp"
android:scaleType="centerCrop"
android:background="@color/colorAccent"/>
<ImageView
android:id="@+id/item_8"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="2dp"
android:scaleType="centerCrop"
android:background="@color/colorAccent" />
<ImageView
android:id="@+id/item_9"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="2dp"
android:scaleType="centerCrop"
android:background="@color/colorAccent" />
<ImageView
android:id="@+id/item_10"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="2dp"
android:scaleType="centerCrop"
android:background="@color/colorAccent" />
</LinearLayout>
</ScrollView>
</LinearLayout>
</com.example.zsh.scrollview_demo.StickyScrollView>
再来看MainActivity,
package com.example.zsh.scrollview_demo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements View.OnClickListener, StickyScrollView.OnPageChangeListener {
public static final String TAG = MainActivity.class.getSimpleName();
private ImageView ImageItem1;
private ImageView ImageItem2;
private ImageView ImageItem3;
private ImageView ImageItem4;
private ImageView ImageItem5;
private ImageView ImageItem6;
private ImageView ImageItem7;
private ImageView ImageItem8;
private ImageView ImageItem9;
private ImageView ImageItem10;
private StickyScrollView stickyScrollView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
stickyScrollView = (StickyScrollView) findViewById(R.id.sticky_scroll);
stickyScrollView.setOnPageChangeListener(this);
ImageItem1 = (ImageView) findViewById(R.id.item_1);
ImageItem2 = (ImageView) findViewById(R.id.item_2);
ImageItem3 = (ImageView) findViewById(R.id.item_3);
ImageItem4 = (ImageView) findViewById(R.id.item_4);
ImageItem5 = (ImageView) findViewById(R.id.item_5);
ImageItem6 = (ImageView) findViewById(R.id.item_6);
ImageItem7 = (ImageView) findViewById(R.id.item_7);
ImageItem8 = (ImageView) findViewById(R.id.item_8);
ImageItem9 = (ImageView) findViewById(R.id.item_9);
ImageItem10 = (ImageView) findViewById(R.id.item_10);
ImageItem1.setOnClickListener(this);
ImageItem2.setOnClickListener(this);
ImageItem3.setOnClickListener(this);
ImageItem4.setOnClickListener(this);
ImageItem5.setOnClickListener(this);
ImageItem6.setOnClickListener(this);
ImageItem7.setOnClickListener(this);
ImageItem8.setOnClickListener(this);
ImageItem9.setOnClickListener(this);
ImageItem10.setOnClickListener(this);
}
@Override
public void onClick(View v) {
Log.e(TAG, ">>>onClick");
switch (v.getId()) {
case R.id.item_1:
Log.e(TAG, ">>>item_1");
Toast.makeText(this, "IMAGE_1", Toast.LENGTH_SHORT).show();
break;
case R.id.item_2:
Log.e(TAG, ">>>item_2");
Toast.makeText(this, "IMAGE_2", Toast.LENGTH_SHORT).show();
break;
case R.id.item_3:
Log.e(TAG, ">>>item_3");
Toast.makeText(this, "IMAGE_3", Toast.LENGTH_SHORT).show();
break;
case R.id.item_4:
Log.e(TAG, ">>>item_4");
Toast.makeText(this, "IMAGE_4", Toast.LENGTH_SHORT).show();
break;
case R.id.item_5:
Log.e(TAG, ">>>item_5");
Toast.makeText(this, "IMAGE_5", Toast.LENGTH_SHORT).show();
break;
case R.id.item_6:
Log.e(TAG, ">>>item_6");
Toast.makeText(this, "IMAGE_6", Toast.LENGTH_SHORT).show();
break;
case R.id.item_7:
Log.e(TAG, ">>>item_7");
Toast.makeText(this, "IMAGE_7", Toast.LENGTH_SHORT).show();
break;
case R.id.item_8:
Log.e(TAG, ">>>item_8");
Toast.makeText(this, "IMAGE_8", Toast.LENGTH_SHORT).show();
break;
case R.id.item_9:
Log.e(TAG, ">>>item_9");
Toast.makeText(this, "IMAGE_9", Toast.LENGTH_SHORT).show();
break;
case R.id.item_10:
Log.e(TAG, ">>>item_10");
Toast.makeText(this, "IMAGE_10", Toast.LENGTH_SHORT).show();
break;
}
}
@Override
public void OnPageChange(int currentPage) {
Log.e(TAG, ">>>OnPageChange");
switch (currentPage) {
case StickyScrollView.PAGE_TOP:
Toast.makeText(this, "CHANGE_TO_TOP", Toast.LENGTH_SHORT).show();
break;
case StickyScrollView.PAGE_BOTTOM:
Toast.makeText(this, "CHANGE_TO_BOTTOM", Toast.LENGTH_SHORT).show();
break;
}
}
}
好了,这样一个效果就做出来了。可能还会有小瑕疵,后续再慢慢调。欢迎大家使用,哈哈,我要去忙了。。