Android ListView上下滑动弹性动画

一、通过上拉加载下拉刷新方式实现

public class RefreshListView extends ListView implements AbsListView.OnScrollListener  {

    private final String TAG ="RefreshListView";
    private View mRefreshHeaderView;
    private  int MAX_HEIGHT = 0;
    private int mPaddingTop;
    private AbsListView.OnScrollListener mOnScrollListener;
    private float mLastTouchY;
    private boolean isScrollAtTop;
    private volatile  boolean isRefreshing;
    private View mRefreshFooterView;
    private int mPaddingBottom;
    private boolean isScrollAtBottom;

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

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

    public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initHeaderView();
        setOnScrollListener(this);
    }

    private void initHeaderView() {
        mRefreshHeaderView = createFooterOrHeaderView("下拉刷新");
        mRefreshFooterView = createFooterOrHeaderView("加载更多");
        addHeaderView(mRefreshHeaderView);
        addFooterView(mRefreshFooterView);

        MAX_HEIGHT = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,150,getContext().getResources().getDisplayMetrics());

        final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); //宽度未指定,当然也可以设置为E(-1,EXACTLY)
        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(MAX_HEIGHT, MeasureSpec.AT_MOST); //高度最大值限制为150dip

        mRefreshHeaderView.measure(widthMeasureSpec,heightMeasureSpec);
        mRefreshFooterView.measure(widthMeasureSpec,heightMeasureSpec);

        final int headerHeight = mRefreshHeaderView.getMeasuredHeight();
        mPaddingTop  = - headerHeight;
        mRefreshHeaderView.setPadding(0,mPaddingTop,0,0);

        final int footerHeight = mRefreshHeaderView.getMeasuredHeight();
        mPaddingBottom  = - footerHeight;
        mRefreshFooterView.setPadding(0,0,0,mPaddingBottom);
    }

    private LinearLayout createFooterOrHeaderView(String msg){
        final Context context = getContext();
        final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        final int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP ,10,metrics );
        final int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP ,40, metrics);

        LinearLayout headerPanel = new LinearLayout(context);
        headerPanel.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT));
        headerPanel.setOrientation(LinearLayout.HORIZONTAL);
        headerPanel.setBackgroundColor(0x33ff0000);
        headerPanel.setGravity(Gravity.CENTER);
        headerPanel.setAccessibilityDelegate(new AccessibilityDelegate());

        ProgressBar progressBar = new ProgressBar(context);
        final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(size, size);
        params.topMargin = padding;
        params.bottomMargin = padding;
        progressBar.setLayoutParams(params);
        progressBar.setIndeterminate(true);
        progressBar.setFocusable(true);

        TextView tv = new TextView(context);
        tv.setTextSize(12);
        tv.setText(msg);
        tv.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT));
        tv.setId(android.R.id.text1);

        headerPanel.addView(progressBar);
        headerPanel.addView(tv);

        return headerPanel;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if( !isScrollAtTop &&!isScrollAtBottom || isRefreshing ) {
            mLastTouchY =  ev.getY();
            return super.onTouchEvent(ev);
        }
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                mLastTouchY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
               startHandlePadding(ev);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                 endHandlePadding(ev);
                 break;
        }

        return super.onTouchEvent(ev);
    }

    private boolean endHandlePadding(MotionEvent ev) {

        if(isScrollAtTop) {
            float deltaY  = (ev.getY() - mLastTouchY)/2;
            if (deltaY < 0) return false;
            if (deltaY < (MAX_HEIGHT *3) / 5) {
                mRefreshHeaderView.setPadding(0, mPaddingTop, 0, 0);
            } else {
                isRefreshing = true;
                mRefreshHeaderView.setPadding(0, mPaddingTop + MAX_HEIGHT, 0, 0);
                mRefreshHeaderView.postDelayed(finishTask, 1500);
            }
        }else if(isScrollAtBottom){
            float deltaY  = (-ev.getY() + mLastTouchY)/2;
            if (deltaY < 0) return false;
            if (deltaY < (MAX_HEIGHT*3) / 5) {
                mRefreshFooterView.setPadding(0, 0, 0, mPaddingBottom);
            } else {
                isRefreshing = true;
                mRefreshFooterView.setPadding(0, 0, 0, MAX_HEIGHT + mPaddingBottom);
                mRefreshFooterView.postDelayed(finishTask, 1500);
            }
        }
        return true;
    }

    private boolean startHandlePadding(MotionEvent ev) {
        if(isScrollAtTop) {
            float deltaY = (ev.getY() - mLastTouchY)/2;// 这里 乘以 0.5作为弹性系数
            if (deltaY < 0) return false;
            deltaY = Math.min(deltaY, MAX_HEIGHT);
            Log.d(TAG, " deltaY=" + deltaY);
            mRefreshHeaderView.setPadding(0, (int) deltaY + mPaddingTop, 0, 0);
            return true;
        }else if(isScrollAtBottom){

            float deltaY = (-ev.getY() + mLastTouchY)/2 ; // 这里 乘以 0.5作为弹性系数
            if (deltaY < 0) return false;

            deltaY = Math.min(deltaY, MAX_HEIGHT);
            Log.d(TAG, " deltaY=" + deltaY);
            mRefreshFooterView.setPadding(0, 0, 0, (int) deltaY + mPaddingBottom);
            return true;
        }

        return false;
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if(mOnScrollListener!=null){
            mOnScrollListener.onScrollStateChanged(view,scrollState);
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if(mOnScrollListener!=null){
            mOnScrollListener.onScroll(view,firstVisibleItem,visibleItemCount,totalItemCount);
        }
        isScrollAtTop = (firstVisibleItem==0);
        isScrollAtBottom = (totalItemCount==(firstVisibleItem+visibleItemCount));
        onDatasetChanged();

    }
    private final Runnable finishTask = new Runnable() {
        @Override
        public void run() {
            isRefreshing  = false;
            mRefreshHeaderView.setPadding(0, mPaddingTop, 0, 0);
            mRefreshFooterView.setPadding(0, 0, 0, mPaddingBottom);
        }
    };

    @Override
    public void setAdapter(ListAdapter adapter) {
        super.setAdapter(adapter);
        this.onDatasetChanged();
    }


    private void onDatasetChanged() {
        final int pos = this.getFirstVisiblePosition() + this.getHeaderViewsCount();
        final ListAdapter adapter = this.getAdapter();
        if(adapter==null) return;

        if(pos>adapter.getCount()) return;
        final String item = (String) adapter.getItem(pos);
        if(TextUtils.isEmpty(item)|| item.trim().length()>1) return;

    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        onDatasetChanged();

    }

}

二、通过重写overScrollBy实现

/**
 * Created by Noah on 2016/1/16.
 */
public class BounceListView extends ListView {

    private static final float MAX_Y_OVERSCROLL_DISTANCE = 200;

    private float mMaxYOverscrollDistance;

    public BounceListView(Context context) {
        this(context, null);
    }
    public BounceListView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public BounceListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initBounceListView();
    }

    private void initBounceListView(){
        final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
        final float density = metrics.density;
        mMaxYOverscrollDistance = (int) (density * MAX_Y_OVERSCROLL_DISTANCE);
    }
    @Override
    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, (int)mMaxYOverscrollDistance, isTouchEvent);
    }

    /**
     * 设置本App所有的ListView弹性粒度
     * @param ctx
     * @param size
     * @return
     */
    public  boolean configGlobalMaxOverScrollDistance(Context ctx,int size)
    {
        try {
            final DisplayMetrics metrics = ctx.getResources().getDisplayMetrics();
            final float density = metrics.density;
            int value = (int) (density * size);
            mMaxYOverscrollDistance = value;

            ViewConfiguration config = ViewConfiguration.get(ctx);
            Field mOverscrollDistance =  ViewConfiguration.class.getDeclaredField("mOverscrollDistance");
            if(!mOverscrollDistance.isAccessible() || !Modifier.isPublic(mOverscrollDistance.getModifiers()))
            {
                mOverscrollDistance.setAccessible(true);
            }
            mOverscrollDistance.setInt(config,value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
}

 

 

转载于:https://my.oschina.net/ososchina/blog/603854

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值