1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.support.v7.widget; 18 19 import android.content.Context; 20 import android.support.v4.view.NestedScrollingParent; 21 import android.support.v4.view.ViewCompat; 22 import android.util.AttributeSet; 23 import android.util.Log; 24 import android.view.View; 25 import android.view.ViewGroup; 26 import android.widget.FrameLayout; 27 28 public class TestedFrameLayout extends FrameLayout implements NestedScrollingParent { 29 30 static final int TEST_NESTED_SCROLL_MODE_IGNORE = 0; 31 static final int TEST_NESTED_SCROLL_MODE_CONSUME = 1; 32 33 private int mNestedScrollMode; 34 private int mNestedFlingMode; 35 private boolean mNestedStopNestedScrollCalled; 36 37 public TestedFrameLayout(Context context) { 38 super(context); 39 } 40 41 @Override 42 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 43 RecyclerView recyclerView = getRvChild(); 44 if (recyclerView == null) { 45 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 46 return; 47 } 48 FullControlLayoutParams lp = (FullControlLayoutParams) recyclerView.getLayoutParams(); 49 if (lp.wSpec == null && lp.hSpec == null) { 50 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 51 return; 52 } 53 final int childWidthMeasureSpec; 54 if (lp.wSpec != null) { 55 childWidthMeasureSpec = lp.wSpec; 56 } else if (lp.width == LayoutParams.MATCH_PARENT) { 57 final int width = Math.max(0, getMeasuredWidth() 58 - lp.leftMargin - lp.rightMargin); 59 childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); 60 } else { 61 childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 62 lp.leftMargin + lp.rightMargin, lp.width); 63 } 64 65 final int childHeightMeasureSpec; 66 if (lp.hSpec != null) { 67 childHeightMeasureSpec = lp.hSpec; 68 } else if (lp.height == LayoutParams.MATCH_PARENT) { 69 final int height = Math.max(0, getMeasuredHeight() 70 - lp.topMargin - lp.bottomMargin); 71 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); 72 } else { 73 childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 74 lp.topMargin + lp.bottomMargin, lp.height); 75 } 76 recyclerView.measure(childWidthMeasureSpec, childHeightMeasureSpec); 77 if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY && 78 MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) { 79 setMeasuredDimension( 80 MeasureSpec.getSize(widthMeasureSpec), 81 MeasureSpec.getSize(heightMeasureSpec) 82 ); 83 } else { 84 setMeasuredDimension( 85 chooseSize(widthMeasureSpec, 86 recyclerView.getWidth() + getPaddingLeft() + getPaddingRight(), 87 getMinimumWidth()), 88 chooseSize(heightMeasureSpec, 89 recyclerView.getHeight() + getPaddingTop() + getPaddingBottom(), 90 getMinimumHeight())); 91 } 92 } 93 94 public static int chooseSize(int spec, int desired, int min) { 95 final int mode = View.MeasureSpec.getMode(spec); 96 final int size = View.MeasureSpec.getSize(spec); 97 switch (mode) { 98 case View.MeasureSpec.EXACTLY: 99 return size; 100 case View.MeasureSpec.AT_MOST: 101 return Math.min(size, desired); 102 case View.MeasureSpec.UNSPECIFIED: 103 default: 104 return Math.max(desired, min); 105 } 106 } 107 108 109 private RecyclerView getRvChild() { 110 for (int i = 0; i < getChildCount(); i++) { 111 if (getChildAt(i) instanceof RecyclerView) { 112 return (RecyclerView) getChildAt(i); 113 } 114 } 115 return null; 116 } 117 118 @Override 119 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 120 return p instanceof FullControlLayoutParams; 121 } 122 123 @Override 124 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 125 return new FullControlLayoutParams(p); 126 } 127 128 @Override 129 public LayoutParams generateLayoutParams(AttributeSet attrs) { 130 return new FullControlLayoutParams(getContext(), attrs); 131 } 132 133 @Override 134 protected LayoutParams generateDefaultLayoutParams() { 135 return new FullControlLayoutParams(getWidth(), getHeight()); 136 } 137 138 @Override 139 public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { 140 // Always start nested scroll 141 return mNestedFlingMode == TEST_NESTED_SCROLL_MODE_CONSUME 142 || mNestedScrollMode == TEST_NESTED_SCROLL_MODE_CONSUME; 143 } 144 145 @Override 146 public boolean onNestedPreFling(View target, float velocityX, float velocityY) { 147 Log.d("TestedFrameLayout", "onNestedPreFling: " + mNestedFlingMode); 148 149 return mNestedFlingMode == TEST_NESTED_SCROLL_MODE_CONSUME; 150 } 151 152 @Override 153 public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { 154 if (mNestedScrollMode == TEST_NESTED_SCROLL_MODE_CONSUME) { 155 // We consume all scroll deltas 156 consumed[0] = dx; 157 consumed[1] = dy; 158 } 159 } 160 161 @Override 162 public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, 163 int dyUnconsumed) { 164 // ignore 165 } 166 167 @Override 168 public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { 169 // ignore 170 return false; 171 } 172 173 @Override 174 public void onNestedScrollAccepted(View child, View target, int axes) { 175 // ignore 176 } 177 178 @Override 179 public int getNestedScrollAxes() { 180 // We can scroll in both direction 181 return ViewCompat.SCROLL_AXIS_HORIZONTAL | ViewCompat.SCROLL_AXIS_VERTICAL; 182 } 183 184 @Override 185 public void onStopNestedScroll(View target) { 186 mNestedStopNestedScrollCalled = true; 187 } 188 189 public boolean stopNestedScrollCalled() { 190 return mNestedStopNestedScrollCalled; 191 } 192 193 public void setNestedScrollMode(int mode) { 194 mNestedScrollMode = mode; 195 } 196 197 public void setNestedFlingMode(int mode) { 198 mNestedFlingMode = mode; 199 } 200 201 public static class FullControlLayoutParams extends FrameLayout.LayoutParams { 202 203 Integer wSpec; 204 Integer hSpec; 205 206 public FullControlLayoutParams(Context c, AttributeSet attrs) { 207 super(c, attrs); 208 } 209 210 public FullControlLayoutParams(int width, int height) { 211 super(width, height); 212 } 213 214 public FullControlLayoutParams(ViewGroup.LayoutParams source) { 215 super(source); 216 } 217 218 public FullControlLayoutParams(FrameLayout.LayoutParams source) { 219 super(source); 220 } 221 222 public FullControlLayoutParams(MarginLayoutParams source) { 223 super(source); 224 } 225 } 226 } 227