android中上拉下滑布局,Android ScrollView的顶部下拉和底部上拉回弹效果

要实现ScrollView的回弹效果,需要对其进行触摸事件处理。先来看一下简单的效果:

d21a64c19035ff01d38991ab57ac7d90.gif

根据Android的View事件分发处理机制,下面对dispatchTouchEvent进行详细分析:

在加载布局完成之后,获取ScrollView的第一个子元素,保存它的参数,left top right bottom参数,根据顶部下拉操作和底部上拉操作进行子View的布局参数根据滑动距离改变,ACTION_UP的时候判断是否存在回弹,如果需要则进行动画回弹到原来的位置,可以添加一个回弹结束监听,比如监听回弹处理跳转到其他的页面的操作等。

具体的实现如下,添加了是否禁用顶部和底部回弹的参数设置,以及回弹效果结束监听。

/**

* A Simple Rebound ScrollView

* @author Denluoyia

*/

public class ReboundScrollView extends ScrollView{

private boolean mEnableTopRebound = true;

private boolean mEnableBottomRebound = true;

private OnReboundEndListener mOnReboundEndListener;

private View mContentView;

private Rect mRect = new Rect();

public ReboundScrollView(Context context) {

super(context);

}

public ReboundScrollView(Context context, AttributeSet attrs) {

super(context, attrs);

}

public ReboundScrollView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

}

/** after inflating view, we can get the width and height of view */

@Override

protected void onFinishInflate() {

super.onFinishInflate();

mContentView = getChildAt(0);

}

@Override

protected void onLayout(boolean changed, int l, int t, int r, int b) {

super.onLayout(changed, l, t, r, b);

if (mContentView == null) return;

// to remember the location of mContentView

mRect.set(mContentView.getLeft(), mContentView.getTop(), mContentView.getRight(), mContentView.getBottom());

}

public ReboundScrollView setOnReboundEndListener(OnReboundEndListener onReboundEndListener){

this.mOnReboundEndListener = onReboundEndListener;

return this;

}

public ReboundScrollView setEnableTopRebound(boolean enableTopRebound){

this.mEnableTopRebound = enableTopRebound;

return this;

}

public ReboundScrollView setEnableBottomRebound(boolean mEnableBottomRebound){

this.mEnableBottomRebound = mEnableBottomRebound;

return this;

}

private int lastY;

private boolean rebound = false;

private int reboundDirection = 0; //<0 表示下部回弹 >0 表示上部回弹 0表示不回弹

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

if (mContentView == null){

return super.dispatchTouchEvent(ev);

}

switch (ev.getAction()){

case MotionEvent.ACTION_DOWN:

lastY = (int) ev.getY();

break;

case MotionEvent.ACTION_MOVE:

if (!isScrollToTop() && !isScrollToBottom()){

lastY = (int) ev.getY();

break;

}

//处于顶部或者底部

int deltaY = (int) (ev.getY() - lastY);

//deltaY > 0 下拉 deltaY < 0 上拉

//disable top or bottom rebound

if ((!mEnableTopRebound && deltaY > 0) || (!mEnableBottomRebound && deltaY < 0)){

break;

}

int offset = (int) (deltaY * 0.48);

mContentView.layout(mRect.left, mRect.top + offset, mRect.right, mRect.bottom + offset);

rebound = true;

break;

case MotionEvent.ACTION_UP:

if (!rebound) break;

reboundDirection = mContentView.getTop() - mRect.top;

TranslateAnimation animation = new TranslateAnimation(0, 0, mContentView.getTop(), mRect.top);

animation.setDuration(300);

animation.setAnimationListener(new Animation.AnimationListener() {

@Override

public void onAnimationStart(Animation animation) {

}

@Override

public void onAnimationEnd(Animation animation) {

if (mOnReboundEndListener != null){

if (reboundDirection > 0){

mOnReboundEndListener.onReboundTopComplete();

}

if (reboundDirection < 0){

mOnReboundEndListener.onReboundBottomComplete();

}

reboundDirection = 0;

}

}

@Override

public void onAnimationRepeat(Animation animation) {

}

});

mContentView.startAnimation(animation);

mContentView.layout(mRect.left, mRect.top, mRect.right, mRect.bottom);

rebound = false;

break;

}

return super.dispatchTouchEvent(ev);

}

@Override

public void setFillViewport(boolean fillViewport) {

super.setFillViewport(true); //默认是填充ScrollView 或者再XML布局文件中设置fillViewport属性

}

/**

* 判断当前ScrollView是否处于顶部

*/

private boolean isScrollToTop(){

return getScrollY() == 0;

}

/**

* 判断当前ScrollView是否已滑到底部

*/

private boolean isScrollToBottom(){

return mContentView.getHeight() <= getHeight() + getScrollY();

}

/**

* listener for top and bottom rebound

* do your implement in the following methods

*/

public interface OnReboundEndListener{

void onReboundTopComplete();

void onReboundBottomComplete();

}

}

使用:

直接在XML布局文件中把ScrollView替换成ReboundScrollView就可以了。还可以拓展把回弹顶部和底部添加其他的动画效果(之后再拓展试下)。

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"

tools:context=".TestActivity">

android:id="@+id/reboundScrollView"

android:layout_width="match_parent"

android:layout_height="match_parent">

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="#eefade"

android:padding="16dp">

android:layout_width="match_parent"

android:layout_height="match_parent"

android:textSize="15sp"

android:lineSpacingExtra="5dp"

android:text="@string/content"/>

如果需要禁用回弹,可以直接设置enableTopRebound和enableBottomRebound参数,同样设置回弹结束(或开始)监听。

public class TestActivity extends AppCompatActivity {

private ReboundScrollView reboundScrollView;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

reboundScrollView = findViewById(R.id.reboundScrollView);

//reboundScrollView.setEnableTopRebound(false);

//reboundScrollView.setEnableBottomRebound(false);

reboundScrollView.setOnReboundEndListener(new ReboundScrollView.OnReboundEndListener() {

@Override

public void onReboundTopComplete() {

Toast.makeText(TestActivity.this, "顶部回弹", Toast.LENGTH_SHORT).show();

}

@Override

public void onReboundBottomComplete() {

Toast.makeText(TestActivity.this, "底部回弹", Toast.LENGTH_SHORT).show();

}

});

}

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值