import java.lang.reflect.Field;import com.loopj.android.http.BuildConfig;import android.content.Context;import android.graphics.Rect;import android.support.v4.view.ViewCompat;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.util.Log;import android.view.View;/** * 重写 LinearLayoutManager 为了ScrollView可以显示RecyclerView 垂直布局 * * @author M.Z */public class FullyLinearLayoutManager2 extends LinearLayoutManager {     private static boolean canMakeInsetsDirty = true;    private static Field insetsDirtyField = null;     private static final int CHILD_WIDTH = 0;    private static final int CHILD_HEIGHT = 1;    private static final int DEFAULT_CHILD_SIZE = 100;     private final int[] childDimensions = new int[2];    private final RecyclerView view;     private int childSize = DEFAULT_CHILD_SIZE;    private boolean hasChildSize;    private int overScrollMode = ViewCompat.OVER_SCROLL_ALWAYS;    private final Rect tmpRect = new Rect();     public FullyLinearLayoutManager2(Context context) {        super(context);        this.view = null;    }     public FullyLinearLayoutManager2(Context context, int orientation, boolean reverseLayout) {        super(context, orientation, reverseLayout);        this.view = null;    }     public FullyLinearLayoutManager2(RecyclerView view) {        super(view.getContext());        this.view = view;        this.overScrollMode = ViewCompat.getOverScrollMode(view);    }     public FullyLinearLayoutManager2(RecyclerView view, int orientation, boolean reverseLayout) {        super(view.getContext(), orientation, reverseLayout);        this.view = view;        this.overScrollMode = ViewCompat.getOverScrollMode(view);    }     public void setOverScrollMode(int overScrollMode) {        if (overScrollMode < ViewCompat.OVER_SCROLL_ALWAYS || overScrollMode > ViewCompat.OVER_SCROLL_NEVER)            throw new IllegalArgumentException("Unknown overscroll mode: " + overScrollMode);        if (this.view == null) throw new IllegalStateException("view == null");        this.overScrollMode = overScrollMode;        ViewCompat.setOverScrollMode(view, overScrollMode);    }     public static int makeUnspecifiedSpec() {        return View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);    }     @Override    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {        final int widthMode = View.MeasureSpec.getMode(widthSpec);        final int heightMode = View.MeasureSpec.getMode(heightSpec);         final int widthSize = View.MeasureSpec.getSize(widthSpec);        final int heightSize = View.MeasureSpec.getSize(heightSpec);         final boolean hasWidthSize = widthMode != View.MeasureSpec.UNSPECIFIED;        final boolean hasHeightSize = heightMode != View.MeasureSpec.UNSPECIFIED;         final boolean exactWidth = widthMode == View.MeasureSpec.EXACTLY;        final boolean exactHeight = heightMode == View.MeasureSpec.EXACTLY;         final int unspecified = makeUnspecifiedSpec();         if (exactWidth && exactHeight) {            // in case of exact calculations for both dimensions let's use default "onMeasure" implementation            super.onMeasure(recycler, state, widthSpec, heightSpec);            return;        }         final boolean vertical = getOrientation() == VERTICAL;         initChildDimensions(widthSize, heightSize, vertical);         int width = 0;        int height = 0;         // it's possible to get scrap views in recycler which are bound to old (invalid) adapter entities. This        // happens because their invalidation happens after "onMeasure" method. As a workaround let's clear the        // recycler now (it should not cause any performance issues while scrolling as "onMeasure" is never        // called whiles scrolling)        recycler.clear();         final int stateItemCount = state.getItemCount();        final int adapterItemCount = getItemCount();        // adapter always contains actual data while state might contain old data (f.e. data before the animation is        // done). As we want to measure the view with actual data we must use data from the adapter and not from  the        // state        for (int i = 0; i < adapterItemCount; i++) {            if (vertical) {                if (!hasChildSize) {                    if (i < stateItemCount) {                        // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items                        // we will use previously calculated dimensions                        measureChild(recycler, i, widthSize, unspecified, childDimensions);                    } else {                        logMeasureWarning(i);                    }                }                height += childDimensions[CHILD_HEIGHT];                if (i == 0) {                    width = childDimensions[CHILD_WIDTH];                }                if (hasHeightSize && height >= heightSize) {                    break;                }            } else {                if (!hasChildSize) {                    if (i < stateItemCount) {                        // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items                        // we will use previously calculated dimensions                        measureChild(recycler, i, unspecified, heightSize, childDimensions);                    } else {                        logMeasureWarning(i);                    }                }                width += childDimensions[CHILD_WIDTH];                if (i == 0) {                    height = childDimensions[CHILD_HEIGHT];                }                if (hasWidthSize && width >= widthSize) {                    break;                }            }        }         if (exactWidth) {            width = widthSize;        } else {            width += getPaddingLeft() + getPaddingRight();            if (hasWidthSize) {                width = Math.min(width, widthSize);            }        }         if (exactHeight) {            height = heightSize;        } else {            height += getPaddingTop() + getPaddingBottom();            if (hasHeightSize) {                height = Math.min(height, heightSize);            }        }         setMeasuredDimension(width, height);         if (view != null && overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS) {            final boolean fit = (vertical && (!hasHeightSize || height < heightSize))                    || (!vertical && (!hasWidthSize || width < widthSize));             ViewCompat.setOverScrollMode(view, fit ? ViewCompat.OVER_SCROLL_NEVER : ViewCompat.OVER_SCROLL_ALWAYS);        }    }     private void logMeasureWarning(int child) {        if (BuildConfig.DEBUG) {            Log.w("LinearLayoutManager", "Can't measure child #" + child + ", previously used dimensions will be reused." +                    "To remove this message either use #setChildSize() method or don't run RecyclerView animations");        }    }     private void initChildDimensions(int width, int height, boolean vertical) {        if (childDimensions[CHILD_WIDTH] != 0 || childDimensions[CHILD_HEIGHT] != 0) {            // already initialized, skipping            return;        }        if (vertical) {            childDimensions[CHILD_WIDTH] = width;            childDimensions[CHILD_HEIGHT] = childSize;        } else {            childDimensions[CHILD_WIDTH] = childSize;            childDimensions[CHILD_HEIGHT] = height;        }    }     @Override    public void setOrientation(int orientation) {        // might be called before the constructor of this class is called        //noinspection ConstantConditions        if (childDimensions != null) {            if (getOrientation() != orientation) {                childDimensions[CHILD_WIDTH] = 0;                childDimensions[CHILD_HEIGHT] = 0;            }        }        super.setOrientation(orientation);    }     public void clearChildSize() {        hasChildSize = false;        setChildSize(DEFAULT_CHILD_SIZE);    }     public void setChildSize(int childSize) {        hasChildSize = true;        if (this.childSize != childSize) {            this.childSize = childSize;            requestLayout();        }    }     private void measureChild(RecyclerView.Recycler recycler, int position, int widthSize, int heightSize, int[] dimensions) {        final View child;        try {            child = recycler.getViewForPosition(position);        } catch (IndexOutOfBoundsException e) {            if (BuildConfig.DEBUG) {                Log.w("LinearLayoutManager", "LinearLayoutManager doesn't work well with animations. Consider switching them off", e);            }            return;        }         final RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) child.getLayoutParams();         final int hPadding = getPaddingLeft() + getPaddingRight();        final int vPadding = getPaddingTop() + getPaddingBottom();         final int hMargin = p.leftMargin + p.rightMargin;        final int vMargin = p.topMargin + p.bottomMargin;         // we must make insets dirty in order calculateItemDecorationsForChild to work        makeInsetsDirty(p);        // this method should be called before any getXxxDecorationXxx() methods        calculateItemDecorationsForChild(child, tmpRect);         final int hDecoration = getRightDecorationWidth(child) + getLeftDecorationWidth(child);        final int vDecoration = getTopDecorationHeight(child) + getBottomDecorationHeight(child);         final int childWidthSpec = getChildMeasureSpec(widthSize, hPadding + hMargin + hDecoration, p.width, canScrollHorizontally());        final int childHeightSpec = getChildMeasureSpec(heightSize, vPadding + vMargin + vDecoration, p.height, canScrollVertically());         child.measure(childWidthSpec, childHeightSpec);         dimensions[CHILD_WIDTH] = getDecoratedMeasuredWidth(child) + p.leftMargin + p.rightMargin;        dimensions[CHILD_HEIGHT] = getDecoratedMeasuredHeight(child) + p.bottomMargin + p.topMargin;         // as view is recycled let's not keep old measured values        makeInsetsDirty(p);        recycler.recycleView(child);    }     private static void makeInsetsDirty(RecyclerView.LayoutParams p) {        if (!canMakeInsetsDirty) {            return;        }        try {            if (insetsDirtyField == null) {                insetsDirtyField = RecyclerView.LayoutParams.class.getDeclaredField("mInsetsDirty");                insetsDirtyField.setAccessible(true);            }            insetsDirtyField.set(p, true);        } catch (NoSuchFieldException e) {            onMakeInsertDirtyFailed();        } catch (IllegalAccessException e) {            onMakeInsertDirtyFailed();        }    }     private static void onMakeInsertDirtyFailed() {        canMakeInsetsDirty = false;        if (BuildConfig.DEBUG) {            Log.w("LinearLayoutManager", "Can't make LayoutParams insets dirty, decorations measurements might be incorrect");        }    }}

使用:

recyclerView.setLayoutManager(new FullyLinearLayoutManager(getActivity(),LinearLayoutManager.VERTICAL, false));

最后RecyclerView外层嵌套一层

            

重写ScrollView

import android.content.Context;import android.os.Handler;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.ViewConfiguration;import android.widget.ScrollView;/** * @author 作者 zxb: * @date 创建时间:2016年7月27日 * @Description 带监听滚动的scrollview */public class MyScrollView extends ScrollView {private int downX;private int downY;private int mTouchSlop;private OnScrollListener onScrollListener;/** * 主要是用在用户手指离开MyScrollView,MyScrollView还在继续滑动,我们用来保存Y的距离,然后做比较 */private int lastScrollY;public MyScrollView(Context context) {this(context, null);mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();}public MyScrollView(Context context, AttributeSet attrs) {this(context, attrs, 0);mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();}public MyScrollView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();}/** * 设置滚动接口 * * @param onScrollListener */public void setOnScrollListener(OnScrollListener onScrollListener) {this.onScrollListener = onScrollListener;}/** * 用于用户手指离开MyScrollView的时候获取MyScrollView滚动的Y距离,然后回调给onScroll方法中 */private Handler handler = new Handler() {public void handleMessage(android.os.Message msg) {int scrollY = MyScrollView.this.getScrollY();// 此时的距离和记录下的距离不相等,在隔5毫秒给handler发送消息if (lastScrollY != scrollY) {lastScrollY = scrollY;handler.sendMessageDelayed(handler.obtainMessage(), 5);}if (onScrollListener != null) {onScrollListener.onScroll(scrollY);}};};/** * 重写onTouchEvent, 当用户的手在MyScrollView上面的时候, * 直接将MyScrollView滑动的Y方向距离回调给onScroll方法中,当用户抬起手的时候, * MyScrollView可能还在滑动,所以当用户抬起手我们隔5毫秒给handler发送消息,在handler处理 * MyScrollView滑动的距离 */@Overridepublic boolean onTouchEvent(MotionEvent ev) {if (onScrollListener != null) {onScrollListener.onScroll(lastScrollY = this.getScrollY());}switch (ev.getAction()) {case MotionEvent.ACTION_UP:handler.sendMessageDelayed(handler.obtainMessage(), 5);break;}return super.onTouchEvent(ev);}/** * * 滚动的回调接口 * * @author xiaanming * */public interface OnScrollListener {/** * 回调方法, 返回MyScrollView滑动的Y方向距离 * * @param scrollY *            、 */public void onScroll(int scrollY);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent e) {int action = e.getAction();switch (action) {case MotionEvent.ACTION_DOWN:downX = (int) e.getRawX();downY = (int) e.getRawY();break;case MotionEvent.ACTION_MOVE:int moveY = (int) e.getRawY();if (Math.abs(moveY - downY) > mTouchSlop) {return true;}}return super.onInterceptTouchEvent(e);}}


 

 

更多相关文章

  1. android 3d页面跳转
  2. Android(安卓)drawableleft如何设置图片大小
  3. Android(安卓)ontouch 手速
  4. Android仿iOS左右滑动开关控件(Android4.0以上适用)
  5. android用户界面之AlarmManager教程实例汇
  6. Android端的极光配置
  7. android用户界面之TabHost教程实例汇总
  8. android用户界面之AlarmManager教程实例汇
  9. android GridView 去掉自带点击边框效果和禁止上下滑动

随机推荐

  1. Android中动态设置布局高度一致
  2. Android(安卓)Service完全解析
  3. Android实现下拉框(Spinner)
  4. android listview check 事件
  5. android 遍历安装过的包名
  6. 横向 纵向结合的ScrollView
  7. android 内核添加tourch screen
  8. Android 一些常用的但是记不住的设置
  9. android 版 双色球号码生成
  10. android实现左右滑动菜单