创建自定义ScrollView
/** * ProjectName: yuanxinclan_new * Author: lgq * Date: 2017/12/20 0020 10:07 */ public class StickyScrollView extends ScrollView { /** * Tag for views that should stick and have constant drawing. e.g. * TextViews, ImageViews etc */ public static final String STICKY_TAG = "sticky"; /** * Flag for views that should stick and have non-constant drawing. e.g. * Buttons, ProgressBars etc */ public static final String FLAG_NONCONSTANT = "-nonconstant"; /** * Flag for views that have aren't fully opaque */ public static final String FLAG_HASTRANSPARANCY = "-hastransparancy"; private ArrayList<View> stickyViews; private View currentlyStickingView; private float stickyViewTopOffset; private boolean redirectTouchesToStickyView; private boolean clippingToPadding; private boolean clipToPaddingHasBeenSet; private final Runnable invalidateRunnable = new Runnable() { @Override public void run() { if (currentlyStickingView != null) { int l = getLeftForViewRelativeOnlyChild(currentlyStickingView); int t = getBottomForViewRelativeOnlyChild(currentlyStickingView); int r = getRightForViewRelativeOnlyChild(currentlyStickingView); int b = (int) (getScrollY() + (currentlyStickingView .getHeight() + stickyViewTopOffset)); invalidate(l, t, r, b); } postDelayed(this, 16); } }; public StickyScrollView(Context context) { this(context, null); } public StickyScrollView(Context context, AttributeSet attrs) { this(context, attrs, android.R.attr.scrollViewStyle); } public StickyScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setup(); } public void setup() { stickyViews = new ArrayList<View>(); } private int getLeftForViewRelativeOnlyChild(View v) { int left = v.getLeft(); while (v.getParent() != getChildAt(0)) { v = (View) v.getParent(); left += v.getLeft(); } return left; } private int getTopForViewRelativeOnlyChild(View v) { int top = v.getTop(); while (v.getParent() != getChildAt(0)) { v = (View) v.getParent(); top += v.getTop(); } return top; } private int getRightForViewRelativeOnlyChild(View v) { int right = v.getRight(); while (v.getParent() != getChildAt(0)) { v = (View) v.getParent(); right += v.getRight(); } return right; } private int getBottomForViewRelativeOnlyChild(View v) { int bottom = v.getBottom(); while (v.getParent() != getChildAt(0)) { v = (View) v.getParent(); bottom += v.getBottom(); } return bottom; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (!clipToPaddingHasBeenSet) { clippingToPadding = true; } notifyHierarchyChanged(); } @Override public void setClipToPadding(boolean clipToPadding) { super.setClipToPadding(clipToPadding); clippingToPadding = clipToPadding; clipToPaddingHasBeenSet = true; } @Override public void addView(View child) { super.addView(child); findStickyViews(child); } @Override public void addView(View child, int index) { super.addView(child, index); findStickyViews(child); } @Override public void addView(View child, int index, ViewGroup.LayoutParams params) { super.addView(child, index, params); findStickyViews(child); } @Override public void addView(View child, int width, int height) { super.addView(child, width, height); findStickyViews(child); } @Override public void addView(View child, ViewGroup.LayoutParams params) { super.addView(child, params); findStickyViews(child); } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (currentlyStickingView != null) { canvas.save(); canvas.translate(getPaddingLeft(), getScrollY() + stickyViewTopOffset + (clippingToPadding ? getPaddingTop() : 0)); canvas.clipRect(0, (clippingToPadding ? -stickyViewTopOffset : 0), getWidth(), currentlyStickingView.getHeight()); if (getStringTagForView(currentlyStickingView).contains( FLAG_HASTRANSPARANCY)) { showView(currentlyStickingView); currentlyStickingView.draw(canvas); hideView(currentlyStickingView); } else { currentlyStickingView.draw(canvas); } canvas.restore(); } } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { redirectTouchesToStickyView = true; } if (redirectTouchesToStickyView) { redirectTouchesToStickyView = currentlyStickingView != null; if (redirectTouchesToStickyView) { redirectTouchesToStickyView = ev.getY() <= (currentlyStickingView .getHeight() + stickyViewTopOffset) && ev.getX() >= getLeftForViewRelativeOnlyChild(currentlyStickingView) && ev.getX() <= getRightForViewRelativeOnlyChild(currentlyStickingView); } } else if (currentlyStickingView == null) { redirectTouchesToStickyView = false; } if (redirectTouchesToStickyView) { ev.offsetLocation( 0, -1 * ((getScrollY() + stickyViewTopOffset) - getTopForViewRelativeOnlyChild(currentlyStickingView))); } return super.dispatchTouchEvent(ev); } private boolean hasNotDoneActionDown = true; @Override public boolean onTouchEvent(MotionEvent ev) { if (redirectTouchesToStickyView) { ev.offsetLocation( 0, ((getScrollY() + stickyViewTopOffset) - getTopForViewRelativeOnlyChild(currentlyStickingView))); } if (ev.getAction() == MotionEvent.ACTION_DOWN) { hasNotDoneActionDown = false; } if (hasNotDoneActionDown) { MotionEvent down = MotionEvent.obtain(ev); down.setAction(MotionEvent.ACTION_DOWN); super.onTouchEvent(down); hasNotDoneActionDown = false; } if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) { hasNotDoneActionDown = true; } return super.onTouchEvent(ev); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); doTheStickyThing(); } private void doTheStickyThing() { View viewThatShouldStick = null; View approachingView = null; for (View v : stickyViews) { int viewTop = getTopForViewRelativeOnlyChild(v) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop()); if (viewTop <= 0) { if (viewThatShouldStick == null || viewTop > (getTopForViewRelativeOnlyChild(viewThatShouldStick) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop()))) { viewThatShouldStick = v; } } else { if (approachingView == null || viewTop < (getTopForViewRelativeOnlyChild(approachingView) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop()))) { approachingView = v; } } } if (viewThatShouldStick != null) { stickyViewTopOffset = approachingView == null ? 0 : Math.min(0, getTopForViewRelativeOnlyChild(approachingView) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop()) - viewThatShouldStick.getHeight()); if (viewThatShouldStick != currentlyStickingView) { if (currentlyStickingView != null) { stopStickingCurrentlyStickingView(); } startStickingView(viewThatShouldStick); } } else if (currentlyStickingView != null) { stopStickingCurrentlyStickingView(); } } private void startStickingView(View viewThatShouldStick) { currentlyStickingView = viewThatShouldStick; if (getStringTagForView(currentlyStickingView).contains( FLAG_HASTRANSPARANCY)) { hideView(currentlyStickingView); } if (((String) currentlyStickingView.getTag()) .contains(FLAG_NONCONSTANT)) { post(invalidateRunnable); } } private void stopStickingCurrentlyStickingView() { if (getStringTagForView(currentlyStickingView).contains( FLAG_HASTRANSPARANCY)) { showView(currentlyStickingView); } currentlyStickingView = null; removeCallbacks(invalidateRunnable); } /** * Notify that the sticky attribute has been added or removed from one or * more views in the View hierarchy */ public void notifyStickyAttributeChanged() { notifyHierarchyChanged(); } private void notifyHierarchyChanged() { if (currentlyStickingView != null) { stopStickingCurrentlyStickingView(); } stickyViews.clear(); findStickyViews(getChildAt(0)); doTheStickyThing(); invalidate(); } private void findStickyViews(View v) { if (v instanceof ViewGroup) { ViewGroup vg = (ViewGroup) v; for (int i = 0; i < vg.getChildCount(); i++) { String tag = getStringTagForView(vg.getChildAt(i)); if (tag != null && tag.contains(STICKY_TAG)) { stickyViews.add(vg.getChildAt(i)); } else if (vg.getChildAt(i) instanceof ViewGroup) { findStickyViews(vg.getChildAt(i)); } } } else { String tag = (String) v.getTag(); if (tag != null && tag.contains(STICKY_TAG)) { stickyViews.add(v); } } } private String getStringTagForView(View v) { Object tagObject = v.getTag(); return String.valueOf(tagObject); } private void hideView(View v) { // if (Build.VERSION.SDK_INT >= 11) { // v.setAlpha(0); // } else { AlphaAnimation anim = new AlphaAnimation(1, 0); anim.setDuration(0); anim.setFillAfter(true); v.startAnimation(anim); // } } private void showView(View v) { // if (Build.VERSION.SDK_INT >= 11) { // v.setAlpha(1); // } else { AlphaAnimation anim = new AlphaAnimation(0, 1); anim.setDuration(0); anim.setFillAfter(true); v.startAnimation(anim); // } } }
使用自定义scrollView 在scrollView中给任一View添加属性 android:tag="sticky"即可。该View滑动到顶部时即固定
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/gray_b7"> <RelativeLayout android:layout_width="match_parent" android:layout_height="44dp" android:background="@color/businesstop" android:orientation="horizontal"> <LinearLayout android:id="@+id/activity_business_district_library_left_layout" android:layout_width="70dp" android:layout_height="match_parent" android:layout_marginLeft="9dp" android:gravity="center_vertical"> <ImageView android:layout_width="25dp" android:layout_height="25dp" android:src="@drawable/ease_mm_title_back" /> </LinearLayout> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="商圈" android:textColor="@color/white" android:textSize="18sp"/> <LinearLayout android:id="@+id/activity_business_district_library_right_layout" android:layout_width="50dp" android:layout_height="match_parent" android:layout_alignParentRight="true" android:gravity="center_vertical|right" android:visibility="gone" android:paddingRight="12dp" > <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/enterprise_list_nav_icon_search" /> </LinearLayout> </RelativeLayout> <com.yuanxin.clan.mvp.view.PullToRefreshView android:layout_below="@+id/myre" android:id="@+id/p2rv" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.yuanxin.clan.core.util.StickyScrollView android:id="@+id/slv" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/gray_b7" android:fillViewport="false"> <LinearLayout android:id="@+id/layout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.bigkoo.convenientbanner.ConvenientBanner android:id="@+id/bannerTop" android:layout_width="match_parent" android:layout_height="175dp" android:background="@drawable/banner01" app:image_scale_type="center_crop" app:indicator_drawable_selected="@drawable/banner_iocn_pre" app:indicator_drawable_unselected="@drawable/banner_iocn_nomal"/> <LinearLayout android:layout_width="match_parent" android:layout_height="48dp" android:gravity="center_vertical" android:background="@color/white" android:orientation="horizontal"> <LinearLayout android:layout_width="48dp" android:layout_marginLeft="12dp" android:layout_height="match_parent" android:gravity="center_vertical"> <ImageView android:id="@+id/imageView6" android:layout_width="30dp" android:layout_height="30dp" android:src="@drawable/business_list_voice"/> </LinearLayout> <com.yuanxin.clan.core.adapter.verticalRollingTextView.VerticalRollingTextView android:id="@+id/noticeBoard" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="10dp" android:layout_marginRight="12dp" android:textColor="@color/login_black" android:textSize="12sp"/> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="10dp" android:background="@color/gray_b7"></LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="100dp" android:orientation="horizontal" android:background="@color/white" android:weightSum="4"> <LinearLayout android:id="@+id/shanghuili" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:orientation="vertical"> <ImageView android:layout_width="49dp" android:layout_height="49dp" android:src="@drawable/business_icon"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/login_black" android:textSize="14dp" android:text="商会" android:layout_marginTop="8dp"/> </LinearLayout> <LinearLayout android:id="@+id/xiehuili" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:orientation="vertical"> <ImageView android:layout_width="49dp" android:layout_height="49dp" android:src="@drawable/business_siehui"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/login_black" android:textSize="14dp" android:text="协会" android:layout_marginTop="8dp"/> </LinearLayout> <LinearLayout android:id="@+id/quanzili" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:orientation="vertical"> <ImageView android:layout_width="49dp" android:layout_height="49dp" android:src="@drawable/business_list_quanzi"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/login_black" android:textSize="14dp" android:text="圈子" android:layout_marginTop="8dp"/> </LinearLayout> <LinearLayout android:id="@+id/yuanquli" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:orientation="vertical"> <ImageView android:layout_width="49dp" android:layout_height="49dp" android:src="@drawable/business_list_yuanqu"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/login_black" android:textSize="14dp" android:text="园区" android:layout_marginTop="8dp"/> </LinearLayout> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="10dp" android:background="@color/gray_b7"></LinearLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="49dp" android:background="@color/white" android:layout_alignParentTop="true" android:tag="sticky" > <TextView android:id="@+id/activity_business_district_library_area" android:layout_width="wrap_content" android:layout_height="49dp" android:layout_centerVertical="true" android:drawablePadding="4dp" android:drawableRight="@drawable/arrow_down" android:gravity="center_vertical" android:paddingLeft="20dp" android:text="区域" android:textColor="@color/common_register_black" android:textSize="14sp"/> <TextView android:id="@+id/activity_business_district_library_industry" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:drawablePadding="4dp" android:drawableRight="@drawable/arrow_down" android:text="行业" android:textColor="@color/common_register_black" android:textSize="14sp"/> <TextView android:id="@+id/activity_business_district_library_shangxi" android:layout_width="wrap_content" android:layout_height="wrap_content" android:drawablePadding="4dp" android:drawableRight="@drawable/arrow_down" android:text="商系" android:textColor="@color/common_register_black" android:textSize="14sp" android:layout_alignBaseline="@+id/activity_business_district_library_industry" android:layout_alignBottom="@+id/activity_business_district_library_industry" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" android:layout_marginRight="19dp" android:layout_marginEnd="19dp"/> </RelativeLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/gray_b7"></LinearLayout> <android.support.v7.widget.RecyclerView android:id="@+id/activity_business_district_library_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white"> </android.support.v7.widget.RecyclerView> <!--<TextView--> <!--android:layout_width="match_parent"--> <!--android:layout_height="0.5dp"--> <!--android:layout_marginTop="5dp"--> <!--android:layout_marginBottom="5dp"--> <!--android:background="@color/gray_white"/>--> <!--<TextView--> <!--android:layout_width="match_parent"--> <!--android:layout_height="1dp"--> <!--android:background="@drawable/line"/>--> </LinearLayout> </com.yuanxin.clan.core.util.StickyScrollView> </com.yuanxin.clan.mvp.view.PullToRefreshView> </LinearLayout>
附加PullToRefreshView
public class PullToRefreshView extends LinearLayout { private static final int REFRESHING_TYPE_HEADER = 1; private static final int REFRESHING_TYPE_FOOTER = 2; private int nRefreshingType = 0; // refresh states private static final int PULL_TO_REFRESH = 2; private static final int RELEASE_TO_REFRESH = 3; private static final int REFRESHING = 4; // pull state private static final int PULL_UP_STATE = 0; private static final int PULL_DOWN_STATE = 1; private boolean enablePullTorefresh = true; private boolean enablePullLoadMoreDataStatus = true; /** * last y */ private int mLastMotionY; /** * last x */ private int mLastMotionX; /** * lock */ private boolean mLock; /** * header view */ private View mHeaderView; /** * footer view */ private View mFooterView; /** * list or grid */ private AdapterView<?> mAdapterView; /** * scrollview */ private ScrollView mScrollView; /** * header view height */ private int mHeaderViewHeight; /** * footer view height */ private int mFooterViewHeight; /** * header view image */ private ImageView mHeaderImageView; /** * footer view image */ private ImageView mFooterImageView; /** * header tip text */ private TextView mHeaderTextView; /** * footer tip text */ private TextView mFooterTextView; /** * header refresh time */ private TextView mHeaderUpdateTextView; /** * footer refresh time */ // private TextView mFooterUpdateTextView; /** * header progress bar */ private ProgressBar mHeaderProgressBar; /** * footer progress bar */ private ProgressBar mFooterProgressBar; /** * layout inflater */ private LayoutInflater mInflater; /** * header view current state */ private int mHeaderState; /** * footer view current state */ private int mFooterState; /** * pull state,pull up or pull down;PULL_UP_STATE or PULL_DOWN_STATE */ private int mPullState; /** * 变为向下的箭头,改变箭头方向 */ private RotateAnimation mFlipAnimation; /** * 变为逆向的箭头,旋转 */ private RotateAnimation mReverseFlipAnimation; /** * footer refresh listener */ private OnFooterRefreshListener mOnFooterRefreshListener; /** * footer refresh listener */ private OnHeaderRefreshListener mOnHeaderRefreshListener; /** * last update time */ private Context context; public PullToRefreshView(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; init(); } public PullToRefreshView(Context context) { super(context); this.context = context; init(); } /** * init * * @description */ private void init() { // Load all of the animations we need in code rather than through XML mFlipAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); mFlipAnimation.setInterpolator(new LinearInterpolator()); mFlipAnimation.setDuration(100); mFlipAnimation.setFillAfter(true); mReverseFlipAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); mReverseFlipAnimation.setInterpolator(new LinearInterpolator()); mReverseFlipAnimation.setDuration(100); mReverseFlipAnimation.setFillAfter(true); mInflater = LayoutInflater.from(getContext()); // header view 在此添加,保证是第一个添加到linearlayout的最上端 addHeaderView(); } private void addHeaderView() { // header view mHeaderView = mInflater.inflate(R.layout.refresh_header, this, false); mHeaderImageView = (ImageView) mHeaderView.findViewById(R.id.pull_to_refresh_image); mHeaderTextView = (TextView) mHeaderView.findViewById(R.id.pull_to_refresh_text); mHeaderUpdateTextView = (TextView) mHeaderView.findViewById(R.id.pull_to_refresh_updated_at); mHeaderProgressBar = (ProgressBar) mHeaderView.findViewById(R.id.pull_to_refresh_progress); // header layout measureView(mHeaderView); mHeaderViewHeight = mHeaderView.getMeasuredHeight(); LinearLayout.LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); // 设置topMargin的值为负的header View高度,即将其隐藏在最上方 params.topMargin = -(mHeaderViewHeight); mHeaderView.setLayoutParams(params); addView(mHeaderView, params); } public void setBarColor(int nColor) { mHeaderView.setBackgroundColor(nColor); mFooterView.setBackgroundColor(nColor); } private void addFooterView() { // footer view mFooterView = mInflater.inflate(R.layout.refresh_footer, this, false); mFooterImageView = (ImageView) mFooterView.findViewById(R.id.pull_to_load_image); mFooterTextView = (TextView) mFooterView.findViewById(R.id.pull_to_load_text); mFooterProgressBar = (ProgressBar) mFooterView.findViewById(R.id.pull_to_load_progress); // footer layout measureView(mFooterView); mFooterViewHeight = mFooterView.getMeasuredHeight(); LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mFooterViewHeight); // int top = getHeight(); // params.topMargin // =getHeight();//在这里getHeight()==0,但在onInterceptTouchEvent()方法里getHeight()已经有值了,不再是0; // getHeight()什么时候会赋值,稍候再研究一下 // 由于是线性布局可以直接添加,只要AdapterView的高度是MATCH_PARENT,那么footer view就会被添加到最后,并隐藏 addView(mFooterView, params); } @Override protected void onFinishInflate() { super.onFinishInflate(); // footer view 在此添加保证添加到linearlayout中的最后 addFooterView(); initContentAdapterView(); } /** * init AdapterView like ListView,GridView and so on;or init ScrollView */ private void initContentAdapterView() { int count = getChildCount(); if (count < 3) { throw new IllegalArgumentException( "this layout must contain 3 child views,and AdapterView or ScrollView must in the second position!"); } View view = null; for (int i = 0; i < count - 1; ++i) { view = getChildAt(i); if (view instanceof AdapterView<?>) { mAdapterView = (AdapterView<?>) view; } if (view instanceof ScrollView) { // finish later mScrollView = (ScrollView) view; } } if (mAdapterView == null && mScrollView == null) { throw new IllegalArgumentException( "must contain a AdapterView or ScrollView in this layout!"); } } private void measureView(View child) { ViewGroup.LayoutParams p = child.getLayoutParams(); if (p == null) { p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width); int lpHeight = p.height; int childHeightSpec; if (lpHeight > 0) { childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); } else { childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } child.measure(childWidthSpec, childHeightSpec); } @Override public boolean onInterceptTouchEvent(MotionEvent e) { int y = (int) e.getRawY(); int x = (int) e.getRawX(); switch (e.getAction()) { case MotionEvent.ACTION_DOWN: // 首先拦截down事件,记录y坐标 mLastMotionY = y; mLastMotionX = x; break; case MotionEvent.ACTION_MOVE: // deltaY > 0 是向下运动,< 0是向上运动 int deltaY = y - mLastMotionY; int deltaX = x - mLastMotionX; if (Math.abs(deltaY) > Math.abs(deltaX) * 1.5 && isRefreshViewScroll(deltaY)) { return true; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: break; } return false; } /* * 如果在onInterceptTouchEvent()方法中没有拦截(即onInterceptTouchEvent()方法中 return * false)则由PullToRefreshView 的子View来处理;否则由下面的方法来处理(即由PullToRefreshView自己来处理) */ @Override public boolean onTouchEvent(MotionEvent event) { if (mLock) { return true; } int y = (int) event.getRawY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // onInterceptTouchEvent已经记录 // mLastMotionY = y; break; case MotionEvent.ACTION_MOVE: int deltaY = y - mLastMotionY; if (mPullState == PULL_DOWN_STATE) { // PullToRefreshView执行下拉 headerPrepareToRefresh(deltaY); // setHeaderPadding(-mHeaderViewHeight); } else if (mPullState == PULL_UP_STATE) { // PullToRefreshView执行上拉 footerPrepareToRefresh(deltaY); } mLastMotionY = y; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: int topMargin = getHeaderTopMargin(); if (mPullState == PULL_DOWN_STATE) { if (topMargin >= 0) { // 开始刷新 headerRefreshing(); } else { // 还没有执行刷新,重新隐藏 setHeaderTopMargin(-mHeaderViewHeight); } } else if (mPullState == PULL_UP_STATE) { if (Math.abs(topMargin) >= mHeaderViewHeight + mFooterViewHeight) { // 开始执行footer 刷新 footerRefreshing(); } else { // 还没有执行刷新,重新隐藏 setHeaderTopMargin(-mHeaderViewHeight); } } break; } return super.onTouchEvent(event); } /** * 是否应该到了父View,即PullToRefreshView滑动 * * @param deltaY , deltaY > 0 是向下运动,< 0是向上运动 * @return */ private boolean isRefreshViewScroll(int deltaY) { if (mHeaderState == REFRESHING || mFooterState == REFRESHING) { return false; } // 对于ListView和GridView if (mAdapterView != null) { // 子view(ListView or GridView)滑动到最顶端 if (deltaY > 0) { // 判断是否禁用下拉刷新操作 if (!enablePullTorefresh) { return false; } View child = mAdapterView.getChildAt(0); if (child == null) { // 如果mAdapterView中没有数据,不拦截 mPullState = PULL_DOWN_STATE; return true; } if (mAdapterView.getFirstVisiblePosition() == 0 && child.getTop() == 0) { mPullState = PULL_DOWN_STATE; return true; } int top = child.getTop(); int padding = mAdapterView.getPaddingTop(); if (mAdapterView.getFirstVisiblePosition() == 0 && Math.abs(top - padding) <= 11) {// 这里之前用3可以判断,但现在不行,还没找到原因 mPullState = PULL_DOWN_STATE; return true; } } else if (deltaY < 0) { // 判断是否禁用上拉加载更多操作 if (!enablePullLoadMoreDataStatus) { return false; } View lastChild = mAdapterView.getChildAt(mAdapterView .getChildCount() - 1); if (lastChild == null) { // 如果mAdapterView中没有数据,不拦截 return false; } // 最后一个子view的Bottom小于父View的高度说明mAdapterView的数据没有填满父view, // 等于父View的高度说明mAdapterView已经滑动到最后 if (lastChild.getBottom() <= getHeight() && mAdapterView.getLastVisiblePosition() == mAdapterView .getCount() - 1) { mPullState = PULL_UP_STATE; return true; } } } // 对于ScrollView if (mScrollView != null) { // 子scroll view滑动到最顶端 View child = mScrollView.getChildAt(0); if (deltaY > 0 && mScrollView.getScrollY() == 0) { if (!enablePullTorefresh) { return false; } mPullState = PULL_DOWN_STATE; return true; } else if (deltaY < 0 && child.getMeasuredHeight() <= getHeight() + mScrollView.getScrollY()) { if (!enablePullLoadMoreDataStatus) { return false; } mPullState = PULL_UP_STATE; return true; } } return false; } /** * header 准备刷新,手指移动过程,还没有释放 * * @param deltaY ,手指滑动的距离 */ private void headerPrepareToRefresh(int deltaY) { int newTopMargin = changingHeaderViewTopMargin(deltaY, 0.3f); // 当header view的topMargin>=0时,说明已经完全显示出来了,修改header view 的提示状态 if (newTopMargin >= 0 && mHeaderState != RELEASE_TO_REFRESH) { mHeaderTextView.setText(R.string.pull_to_refresh_release_label); mHeaderUpdateTextView.setVisibility(View.GONE); mHeaderImageView.clearAnimation(); mHeaderImageView.startAnimation(mFlipAnimation); mHeaderState = RELEASE_TO_REFRESH; } else if (newTopMargin < 0 && newTopMargin > -mHeaderViewHeight) {// 拖动时没有释放 mHeaderImageView.clearAnimation(); mHeaderImageView.startAnimation(mFlipAnimation); mHeaderUpdateTextView.setVisibility(View.GONE); // mHeaderImageView. mHeaderTextView.setText(R.string.pull_to_refresh_pull_label); mHeaderState = PULL_TO_REFRESH; } } /** * footer 准备刷新,手指移动过程,还没有释放 移动footer view高度同样和移动header view * 高度是一样,都是通过修改header view的topmargin的值来达到 * * @param deltaY ,手指滑动的距离 */ private void footerPrepareToRefresh(int deltaY) { int newTopMargin = changingHeaderViewTopMargin(deltaY, 1.0f); // 如果header view topMargin 的绝对值大于或等于header + footer 的高度 // 说明footer view 完全显示出来了,修改footer view 的提示状态 if (Math.abs(newTopMargin) >= (mHeaderViewHeight + mFooterViewHeight) && mFooterState != RELEASE_TO_REFRESH) { mFooterTextView .setText(R.string.pull_to_refresh_footer_release_label); mFooterImageView.clearAnimation(); mFooterImageView.startAnimation(mFlipAnimation); mFooterState = RELEASE_TO_REFRESH; } else if (Math.abs(newTopMargin) < (mHeaderViewHeight + mFooterViewHeight)) { mFooterImageView.clearAnimation(); mFooterImageView.startAnimation(mFlipAnimation); mFooterTextView.setText(R.string.pull_to_refresh_footer_pull_label); mFooterState = PULL_TO_REFRESH; } } /** * 修改Header view top margin的值 * * @param deltaY * @description */ private int changingHeaderViewTopMargin(int deltaY, float percentage) { LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams(); float newTopMargin = params.topMargin + deltaY * percentage; // 这里对上拉做一下限制,因为当前上拉后然后不释放手指直接下拉,会把下拉刷新给触发了,感谢网友yufengzungzhe的指出 // 表示如果是在上拉后一段距离,然后直接下拉 if (deltaY > 0 && mPullState == PULL_UP_STATE && Math.abs(params.topMargin) <= mHeaderViewHeight) { return params.topMargin; } // 同样地,对下拉做一下限制,避免出现跟上拉操作时一样的bug if (deltaY < 0 && mPullState == PULL_DOWN_STATE && Math.abs(params.topMargin) >= mHeaderViewHeight) { return params.topMargin; } params.topMargin = (int) newTopMargin; mHeaderView.setLayoutParams(params); invalidate(); return params.topMargin; } /** * header refreshing */ public void headerRefreshing() { mHeaderState = REFRESHING; setHeaderTopMargin(0); mHeaderImageView.setVisibility(View.GONE); mHeaderImageView.clearAnimation(); mHeaderImageView.setImageDrawable(null); mHeaderProgressBar.setVisibility(View.VISIBLE); mHeaderTextView.setText(R.string.pull_to_refresh_refreshing_label); if (!TextUtils.isEmpty(mHeaderUpdateTextView.getText().toString().trim())) mHeaderUpdateTextView.setVisibility(View.VISIBLE); if (mOnHeaderRefreshListener != null) { mOnHeaderRefreshListener.onHeaderRefresh(this); } nRefreshingType = REFRESHING_TYPE_HEADER; } /** * footer refreshing */ private void footerRefreshing() { mFooterState = REFRESHING; int top = mHeaderViewHeight + mFooterViewHeight; setHeaderTopMargin(-top); mFooterImageView.setVisibility(View.GONE); mFooterImageView.clearAnimation(); mFooterImageView.setImageDrawable(null); mFooterProgressBar.setVisibility(View.VISIBLE); mFooterTextView .setText(R.string.pull_to_refresh_footer_refreshing_label); if (mOnFooterRefreshListener != null) { mOnFooterRefreshListener.onFooterRefresh(this); } nRefreshingType = REFRESHING_TYPE_FOOTER; } /** * 设置header view 的topMargin的值 * * @param topMargin ,为0时,说明header view 刚好完全显示出来; 为-mHeaderViewHeight时,说明完全隐藏了 * @description */ private void setHeaderTopMargin(int topMargin) { LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams(); params.topMargin = topMargin; mHeaderView.setLayoutParams(params); invalidate(); } /** * header view 完成更新后恢复初始状态 */ private void onHeaderRefreshComplete() { setHeaderTopMargin(-mHeaderViewHeight); mHeaderImageView.setVisibility(View.VISIBLE); mHeaderImageView.setImageResource(R.drawable.ic_pulltorefresh_arrow); mHeaderTextView.setText(R.string.pull_to_refresh_pull_label); mHeaderProgressBar.setVisibility(View.GONE); mHeaderState = PULL_TO_REFRESH; long between = Calendar.getInstance().getTimeInMillis() - System.currentTimeMillis(); setLastUpdated(String.format(context .getString(R.string.pull_to_refresh_refresh_time_label), new SimpleDateFormat((between >= 86400000 ? "MM-dd" : "HH:mm")).format(System.currentTimeMillis()))); } /** * Resets the list to a normal state after a refresh. * * @param lastUpdated Last updated at. */ public void onHeaderRefreshComplete(CharSequence lastUpdated) { setLastUpdated(lastUpdated); onHeaderRefreshComplete(); } /** * footer view 完成更新后恢复初始状态 */ private void onFooterRefreshComplete() { setHeaderTopMargin(-mHeaderViewHeight); mFooterImageView.setVisibility(View.VISIBLE); mFooterImageView.setImageResource(R.drawable.ic_pulltorefresh_arrow_up); mFooterTextView.setText(R.string.pull_to_refresh_footer_pull_label); mFooterProgressBar.setVisibility(View.GONE); // mHeaderUpdateTextView.setText(""); mFooterState = PULL_TO_REFRESH; } /** * footer view 完成更新后恢复初始状态 */ public void onFooterRefreshComplete(int size) { if (size > 0) { mFooterView.setVisibility(View.VISIBLE); } else { mFooterView.setVisibility(View.GONE); } setHeaderTopMargin(-mHeaderViewHeight); mFooterImageView.setVisibility(View.VISIBLE); mFooterImageView.setImageResource(R.drawable.ic_pulltorefresh_arrow_up); mFooterTextView.setText(R.string.pull_to_refresh_footer_pull_label); mFooterProgressBar.setVisibility(View.GONE); // mHeaderUpdateTextView.setText(""); mFooterState = PULL_TO_REFRESH; } /** * 完成更新后恢复初始状态 */ public void setRefreshComplete() { switch (nRefreshingType) { case REFRESHING_TYPE_HEADER: onHeaderRefreshComplete(); break; case REFRESHING_TYPE_FOOTER: onFooterRefreshComplete(); break; default: break; } } /** * Set a text to represent when the list was last updated. * * @param lastUpdated Last updated at. */ public void setLastUpdated(CharSequence lastUpdated) { if (lastUpdated != null) { mHeaderUpdateTextView.setVisibility(View.VISIBLE); mHeaderUpdateTextView.setText(lastUpdated); } else { mHeaderUpdateTextView.setVisibility(View.GONE); } } /** * 获取当前header view 的topMargin * * @description */ private int getHeaderTopMargin() { LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams(); return params.topMargin; } // /** // * lock // * // */ // private void lock() { // mLock = true; // } // // /** // * unlock // * // */ // private void unlock() { // mLock = false; // } /** * set headerRefreshListener * * @param headerRefreshListener * @description */ public void setOnHeaderRefreshListener( OnHeaderRefreshListener headerRefreshListener) { mOnHeaderRefreshListener = headerRefreshListener; } public void setOnFooterRefreshListener( OnFooterRefreshListener footerRefreshListener) { mOnFooterRefreshListener = footerRefreshListener; } /** * Interface definition for a callback to be invoked when list/grid footer * view should be refreshed. */ public interface OnFooterRefreshListener { public void onFooterRefresh(PullToRefreshView view); } /** * Interface definition for a callback to be invoked when list/grid header * view should be refreshed. */ public interface OnHeaderRefreshListener { public void onHeaderRefresh(PullToRefreshView view); } public boolean isEnablePullTorefresh() { return enablePullTorefresh; } public void setEnablePullTorefresh(boolean enablePullTorefresh) { this.enablePullTorefresh = enablePullTorefresh; } public boolean isEnablePullLoadMoreDataStatus() { return enablePullLoadMoreDataStatus; } public void setEnablePullLoadMoreDataStatus( boolean enablePullLoadMoreDataStatus) { this.enablePullLoadMoreDataStatus = enablePullLoadMoreDataStatus; } }
在fragment使用完整Class
public class GongYinFragment extends BaseFragment implements PullToRefreshView.OnFooterRefreshListener,PullToRefreshView.OnHeaderRefreshListener{ @BindView(R.id.fragment_my_all_crowd_funding_recycler_view) RecyclerView fragmentMyAllCrowdFundingRecyclerView; @BindView(R.id.p2rv) PullToRefreshView p2rv; @BindView(R.id.sosli) LinearLayout sosli; @BindView(R.id.noticeBoard) MarqueeView noticeBoard; @BindView(R.id.kongli) LinearLayout kongli; private List<GongXuEntity> mGongXuEntities = new ArrayList<>(); private AllGongXuAdapter adapter; private SubscriberOnNextListener getBusinessSearchListOnNextListener; private IntentFilter intentFilter; private LocalBroadcastManager localBroadcastManager; private int ab = 1,pageCount;// 当前页面,从0开始计数 private String detail; public Unbinder unbinder; private More_LoadDialog mMore_loadDialog; private List<HomePageAnnouncementEntity> mStrings=new ArrayList<>(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public int getViewLayout() { return R.layout.allgongxufragmentla; } @Override protected void initView(Bundle savedInstanceState) { initRecyclerView(); mMore_loadDialog = new More_LoadDialog(getContext()); // sosli.setVisibility(View.GONE); getMyBusinessDistrict(1); initNoticeBoard(); p2rv.setOnFooterRefreshListener(this); p2rv.setOnHeaderRefreshListener(this); } private void initNoticeBoard() { String url = Url.homePageAnnouncement; RequestParams params = new RequestParams(); /*1:首页广告位 2:供需成功案例*/ params.put("announcementType", 2); doHttpGet(url, params, new RequestCallback(){ @Override public void onFailure(int i, Header[] headers, String s, Throwable throwable) { // Toasty.error(getActivity(), "网络连接异常", Toast.LENGTH_SHORT, true).show(); } @Override public void onSuccess(int i, Header[] headers, String s) { try { // Log.v("Lgq","w d z HomeFragment===="+s); JSONObject object = new JSONObject(s); if (object.getString("success").equals("true")) { mStrings.addAll(FastJsonUtils.getObjectsList(object.getString("data"), HomePageAnnouncementEntity.class)); ArrayList<String> notices = new ArrayList<String>(); for (HomePageAnnouncementEntity str: mStrings) { notices.add(str.getAnnouncementTitle()); } noticeBoard.startWithList(notices, R.anim.anim_bottom_in, R.anim.anim_top_out); noticeBoard.setOnItemClickListener(new MarqueeView.OnItemClickListener() { @Override public void onItemClick(int position, TextView textView) { String url = mStrings.get(position).getAnnouncementContent(); if (url.startsWith("http")) { startActivity(new Intent(getActivity(), HomeADactivity.class).putExtra("url", url)); } } }); } } catch (JSONException e) { // Toast.makeText(getActivity(), "数据解析出错", Toast.LENGTH_SHORT).show(); Logger.d("json 解析出错"); } } }); } private void getMyBusinessDistrict(int pageNumber) { String url = Url.getgongxu; RequestParams params = new RequestParams(); mMore_loadDialog.show(); params.put("pageNumber", pageNumber); params.put("supplyDemand", 0); params.put("status", 1); doHttpGet(url, params, new RequestCallback() { @Override public void onFailure(int i, Header[] headers, String s, Throwable throwable) { Toast.makeText(getContext(), "网络连接异常", Toast.LENGTH_SHORT).show(); mMore_loadDialog.dismiss(); p2rv.setRefreshComplete(); } @Override public void onSuccess(int i, Header[] headers, String s) { mMore_loadDialog.dismiss(); p2rv.setRefreshComplete(); try { JSONObject object = new JSONObject(s); pageCount = object.getInt("pageCount"); if (object.getString("success").equals("true")) { JSONArray jsonArray = object.getJSONArray("data"); if (jsonArray.length()==0){ kongli.setVisibility(View.VISIBLE); } for (int a = 0; a < jsonArray.length(); a++) { JSONObject businessObject = jsonArray.getJSONObject(a); String supplyDemandId = businessObject.getString("supplyDemandId");//商圈id String image1 = businessObject.getString("image1");//图片 String image11 = Url.img_domain + image1+Url.imageStyle640x640; String image2 = businessObject.getString("image2");//图片 String image22 = Url.img_domain + image2+Url.imageStyle640x640; String image3 = businessObject.getString("image3");//图片 String image33 = Url.img_domain + image3+Url.imageStyle640x640; String content = businessObject.getString("content");//商圈名称 String createDt = businessObject.getString("createDt"); String epId = businessObject.getString("epId"); String supplyDemand = businessObject.getString("supplyDemand"); String userId = businessObject.getString("userId"); String title = businessObject.getString("title"); String address = businessObject.getString("address"); JSONObject twoob=new JSONObject(address); String city = twoob.getString("city"); // Log.v("Lgq","....... " +image11+" ... "+image22+" 。。。 "+image33); GongXuEntity entity = new GongXuEntity(); entity.setImage1(TextUtil.isEmpty(image1)?image1:image11); entity.setImage2(TextUtil.isEmpty(image2)?image2:image22); entity.setImage3(TextUtil.isEmpty(image3)?image3:image33); entity.setContent(content); entity.setCreateDt(createDt); entity.setEpId(epId); entity.setSupplyDemandId(supplyDemandId); entity.setSupplyDemand(supplyDemand); entity.setUserId(userId); entity.setTitle(title); entity.setCity(city); mGongXuEntities.add(entity); } adapter.notifyDataSetChanged(); } else { Toast.makeText(getContext(), object.getString("msg"), Toast.LENGTH_SHORT).show(); } } catch (JSONException e) { // Toast.makeText(getContext(), "数据解析出错", Toast.LENGTH_SHORT).show(); Logger.e("数据解析出错"); } } }); sosli.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (! UserNative.readIsLogin()){ toLogin(); return; } startActivity(new Intent(getActivity(), GongxuSOS_Activity.class).putExtra("supplyDemand", 0)); } }); } private void initRecyclerView() { adapter = new AllGongXuAdapter(getContext(), mGongXuEntities); adapter.setOnItemClickListener(new AllGongXuAdapter.OnItemClickListener() { @Override public void onItemClick(View view, int position) { // 未登陆要求登陆 if (! UserNative.readIsLogin()){ toLogin(); return; } String link = Url.urlWeb+"/market-supply_demand-info¶m="+mGongXuEntities.get(position).getSupplyDemandId()+"&appFlg=0"; Intent intent = new Intent(getContext(), GongXuDetailActivity.class);//商圈详情 intent.putExtra("url", link); intent.putExtra("title", mGongXuEntities.get(position).getTitle()); startActivity(intent); } }); fragmentMyAllCrowdFundingRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); fragmentMyAllCrowdFundingRecyclerView.setAdapter(adapter); fragmentMyAllCrowdFundingRecyclerView.setFocusable(false);//导航栏切换不再focuse fragmentMyAllCrowdFundingRecyclerView.setNestedScrollingEnabled(false);//禁止滑动 } @Override public void onFooterRefresh(PullToRefreshView view) { ab++; if (ab> pageCount) { p2rv.onFooterRefreshComplete(1); Toast.makeText(getContext(), "没有更多数据", Toast.LENGTH_SHORT).show(); return; } getMyBusinessDistrict(ab); } @Override public void onHeaderRefresh(PullToRefreshView view) { mGongXuEntities.clear(); ab = 1; getMyBusinessDistrict(ab); } @Override public void onDestroyView() { super.onDestroyView(); // unbinder.unbind(); } }