As shown in the figure, there is a rotation chart at the top, and TAB needs to be loaded to the top. Different tabs have different entries, and the entries under each TAB have different types, so it needs to support pull-down refreshing and pull-up loading. For the record, it took me a week to realize my stupid talent.
SmartRefreshLayout is used for the refresh load control in the project, and I’m going to use it again this time. I don’t know if it conflicts.
[TAB switch] There is no doubt that TabLayout+ViewPager implementation, difficult is [top], because they have not implemented.
Google keywords: [Android top + switch], Amoy to this brief article, is a little meimei, thank the author to share.
We ran the Demo and integrated SmartRefreshLayout, and thankfully there were no conflicts.
One of the dry goods in the Demo is the OuterRecyclerView and InnerRecyclerView classes. The main solution of RecyclerView nesting, longitudinal sliding conflict. The former is responsible for event interception, while the latter is responsible for consuming events and notifying the former of the results. The partial naming of these two classes is not ideographically strong, the annotation is not enough, and the click event after the introduction of TAB is not sensitive, I have made improvements, see the code at the end of the article.
Another dry goods in the Demo is to let me know the existence of Ali vLayout, admit that usually too lazy, as a programmer do not pay attention to technical current affairs and big factory dynamic I have a little failure. Fortunately, [top] using vLayout implementation. But there are a few twists and turns. I looked at the example and found that I had to set the color helper.setBGColor (0xffffFFFF) to match the product renderings after I instantiated StickyLayoutHelper.
The height of the ViewPager
So we calculate the ViewPager Height based on the number of items in different data sources (due to Tab differences) and the layout Height of the item, and change the ViewPager Height when appropriate (in the ViewPager pageChange listener). This is too much to maintain. Too low, and the ViewPager height is ultimately the maximum calculated.
ViewPager Fragment height can be found in a variety of methods that allow custom ViewPager to override onMeasure methods. Traversing the child to find the maximum height, using getChildView(0) to use the height, and using getCurrentItem() to use the height.
OuterRecyclerView Item layout_height=”wrap_content” ViewPager is not displayed at all (blank) Layout_height =”match_parent” ViewPager also displays only part of the InnerRecyclerView list
But eventually I found something written by foreigners that solved the custom ViewPager problem. At the same time, the customization of ViewPager has been extended: ViewPager supports minimum height, otherwise less than one screen is ugly, code below.
Eggs:
- Since the requestLayout method is called in ViewPager, TAB switching does not preserve the previous state (the top of the InnerRecyclerView is displayed each time).
- RequestLayout and requestDisallowInterceptTouchEvent two methods is very important
- ViewPager2 out
import android.content.Context; import android.support.annotation.Nullable; import android.support.v4.view.ViewPager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.MotionEvent; /** * This class was created by <b> Keystroke knife </ B > on Thursday, February 21, 2019 at 9:10:13; RecyclerView </b> </b> </b> <br> * For the implementation of the top + switch function * The RecyclerView provides a method to control whether to intercept events * <p> * See: https://github.com/FrizzleLiu/NestDemo */ public class OuterNestingRecyclerView extends RecyclerView { /** * Flag whether event interception is required. Default */ private Boolean isNeedIntercept =true;
private floatdownX; // The x-coordinate private when pressedfloatdownY; /** * private ViewPager vp; public OuterNestingRecyclerView(Context context) { super(context); } public OuterNestingRecyclerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public OuterNestingRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean onInterceptTouchEvent(MotionEvent e) {float x = e.getX();
float y = e.getY();
switch (e.getAction()) {
caseACTION_DOWN: // Store downX = x when pressed; downY = y;break;
caseMotionevent. ACTION_MOVE: // Get the distance differencefloat dx = x - downX;
floatdy = y - downY; Int orientation = getOrientation(dx, dy); Switch (orientation) {// Right swipe to ViewPager (only when right swipe is available)case 'r'If the caller does not set Viewpager, the finger move to the right event is not interceptedif (vp == null) {
setNeedIntercept(false);
} else{// If ViewPager is set, finger moves to the right will not be intercepted only if there is a ViewPager entry to the leftif (vp.getCurrentItem() > 0) {
setNeedIntercept(false); }}break; // Left swipe is handled by ViewPager (only if left swipe can be done)case 'l'If the caller does not set Viewpager, the finger move to the left event is not interceptedif (vp == null) {
setNeedIntercept(false);
} else{// If Viewpager is set, finger moves to the left will not be blocked later when there is a Viewpager entry to the rightif (vp.getCurrentItem() < vp.getAdapter().getCount() - 1) {
setNeedIntercept(false); }}break; // The tabLayout click event is not blocked. If this judgment is not made, the TabLayout click event is not sensitivecase 'c':
return false;
}
return isNeedIntercept;
}
return super.onInterceptTouchEvent(e);
}
public void setNeedIntercept(boolean needIntercept) {
isNeedIntercept = needIntercept;
}
private int getOrientation(float dx, float dy) {
if (Math.abs(dx) < 3 && Math.abs(dy) < 3) {
return 'c'; //clickifMath.abs(dx) > math.abs (dy)return dx > 0 ? 'r' : 'l'; // right, left}else{// move the Y axisreturn dy > 0 ? 'b' : 't'; // down // up}} public voidsetViewPager(ViewPager vp) { this.vp = vp; }}Copy the code
import android.content.Context; import android.support.annotation.Nullable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.MotionEvent; /** * This class was created by the <b> Keystroke knife </ B > on Thursday, February 21, 2019 at 9:16:09; <br> * function is: <b> when there is nesting between RecyclerView, inner RecyclerView </ B >; RecyclerView is responsible for when to consume events and when to disable the parent container from intercepting events. <p> https://github.com/FrizzleLiu/NestDemo */ public class InnerNestingRecyclerView extends RecyclerView { privatefloatdownX; // The x-coordinate private when pressedfloatdownY; Private int stickY; private int stickY; private int stickY; private int stickY; // Initialize the default value, Use need not sentenced to null when the private InnerConsumeEventListener InnerConsumeEventListener = innerConsumeEventOrNot - > {/ / no - op}; public InnerNestingRecyclerView(Context context) { super(context); } public InnerNestingRecyclerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public InnerNestingRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean onTouchEvent(MotionEvent e) {float x = e.getX();
float y = e.getY();
switch (e.getAction()) {
caseACTION_DOWN: // Store downX = x when pressed; downY = y; getParent().requestDisallowInterceptTouchEvent(true);
break;
caseMotionevent. ACTION_MOVE: // Get the distance differencefloat dx = x - downX;
floatdy = y - downY; Int orientation = getOrientation(dx, dy); int[] location = {0, 0}; getLocationOnScreen(location); switch (orientation) {case 'b': / / finger from slipping on the move, the direction of the data to the bottom sliding, canScrollVertically () for sentence/can/broken RecyclerView longitudinal sliding, check the scroll up negative, check the scroll is positive. // The inner RecyclerView can no longer handle events when it is pulled down to the topif(! canScrollVertically(-1)) { getParent().requestDisallowInterceptTouchEvent(false);
innerConsumeEventListener.notice(false);
} else{/ / inner RecyclerView can scroll up, the parent container banning intercept time, internal RecyclerView consumer events getParent () requestDisallowInterceptTouchEvent (true);
innerConsumeEventListener.notice(true);
}
break;
case 't': // When the finger slides from bottom to top, that is, the data slides to top direction, location[1] represents the distance between the left upper corner of inner RecyclerView and the Y-axis direction of the upper left point of the screen, // If the upper left corner of inner RecyclerView (i.e., the top) does not slide up to the specified position, That is, without sucking // top, the event is handled by the parent containerif (location[1] > stickY) {
getParent().requestDisallowInterceptTouchEvent(false);
innerConsumeEventListener.notice(false);
return true;
} else{/ / if you have the suction a top, when fingers sliding up, inner RecyclerView events on consumption, / / the parent container banning intercept events getParent () requestDisallowInterceptTouchEvent (true);
innerConsumeEventListener.notice(true);
}
break; // Slide left and right to the ViewPager to handle, does not prohibit the parent class to intercept, that is, allow the parent class to intercept eventscase 'r':
case 'l':
getParent().requestDisallowInterceptTouchEvent(false);
break;
}
break;
}
return super.onTouchEvent(e);
}
private int getOrientation(float dx, float dy) {
ifMath.abs(dx) > math.abs (dy)return dx > 0 ? 'r' : 'l'; // right, left}else{// move the Y axisreturn dy > 0 ? 'b' : 't'; // down // up}} public voidsetStickY(int stickY) { this.stickY = stickY; } / * * * whether you need the inner RecyclerView consumer events to monitor * / public interface InnerConsumeEventListener {/ * * * is used to notify the caller, Whether the inner RecyclerView consumes events * * @param innerConsumeEventOrNottrue: The inner consumes the eventfalseVoid notice(Boolean innerConsumeEventOrNot); } /** * set up a listener to monitor whether the internal RecyclerView consumption time */ public voidsetInnerConsumeEventListener(InnerConsumeEventListener innerConsumeEventListener) { this.innerConsumeEventListener = innerConsumeEventListener; }}Copy the code
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.View;
/**
* https://mobikul.com/viewpager/
* https://medium.com/winkl-insights/how-to-have-a-height-wrapping-viewpager-when-images-have-variable-heights-on-android-60b18e55e72e
* https://stackoverflow.com/questions/8394681/android-i-am-unable-to-have-viewpager-wrap-content
*/
public class WrapContentHeightViewPager extends ViewPager {
public WrapContentHeightViewPager(Context context) {
super(context);
initPageChangeListener();
}
public WrapContentHeightViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
initPageChangeListener();
}
private void initPageChangeListener() {
addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { requestLayout(); }}); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { View child = getChildAt(getCurrentItem());if(child ! = null) { child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); int h = child.getMeasuredHeight();if (minHeight > h) {
h = minHeight;
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private int minHeight;
public void setMinHeight(int minHeight) { this.minHeight = minHeight; }}Copy the code