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.annotation.NonNull; 21 import android.support.annotation.Nullable; 22 import android.support.v4.view.NestedScrollingParent2; 23 import android.support.v4.view.ViewCompat; 24 import android.util.AttributeSet; 25 import android.view.View; 26 import android.view.ViewGroup; 27 import android.widget.FrameLayout; 28 29 public class TestedFrameLayout extends FrameLayout implements NestedScrollingParent2 { 30 31 private NestedScrollingParent2 mNestedScrollingDelegate; 32 33 public TestedFrameLayout(Context context) { 34 super(context); 35 } 36 37 @Override 38 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 39 RecyclerView recyclerView = getRvChild(); 40 if (recyclerView == null) { 41 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 42 return; 43 } 44 FullControlLayoutParams lp = (FullControlLayoutParams) recyclerView.getLayoutParams(); 45 if (lp.wSpec == null && lp.hSpec == null) { 46 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 47 return; 48 } 49 final int childWidthMeasureSpec; 50 if (lp.wSpec != null) { 51 childWidthMeasureSpec = lp.wSpec; 52 } else if (lp.width == LayoutParams.MATCH_PARENT) { 53 final int width = Math.max(0, getMeasuredWidth() 54 - lp.leftMargin - lp.rightMargin); 55 childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); 56 } else { 57 childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 58 lp.leftMargin + lp.rightMargin, lp.width); 59 } 60 61 final int childHeightMeasureSpec; 62 if (lp.hSpec != null) { 63 childHeightMeasureSpec = lp.hSpec; 64 } else if (lp.height == LayoutParams.MATCH_PARENT) { 65 final int height = Math.max(0, getMeasuredHeight() 66 - lp.topMargin - lp.bottomMargin); 67 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); 68 } else { 69 childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 70 lp.topMargin + lp.bottomMargin, lp.height); 71 } 72 recyclerView.measure(childWidthMeasureSpec, childHeightMeasureSpec); 73 if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY && 74 MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) { 75 setMeasuredDimension( 76 MeasureSpec.getSize(widthMeasureSpec), 77 MeasureSpec.getSize(heightMeasureSpec) 78 ); 79 } else { 80 setMeasuredDimension( 81 chooseSize(widthMeasureSpec, 82 recyclerView.getWidth() + getPaddingLeft() + getPaddingRight(), 83 getMinimumWidth()), 84 chooseSize(heightMeasureSpec, 85 recyclerView.getHeight() + getPaddingTop() + getPaddingBottom(), 86 getMinimumHeight())); 87 } 88 } 89 90 public static int chooseSize(int spec, int desired, int min) { 91 final int mode = View.MeasureSpec.getMode(spec); 92 final int size = View.MeasureSpec.getSize(spec); 93 switch (mode) { 94 case View.MeasureSpec.EXACTLY: 95 return size; 96 case View.MeasureSpec.AT_MOST: 97 return Math.min(size, desired); 98 case View.MeasureSpec.UNSPECIFIED: 99 default: 100 return Math.max(desired, min); 101 } 102 } 103 104 private RecyclerView getRvChild() { 105 for (int i = 0; i < getChildCount(); i++) { 106 if (getChildAt(i) instanceof RecyclerView) { 107 return (RecyclerView) getChildAt(i); 108 } 109 } 110 return null; 111 } 112 113 @Override 114 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 115 return p instanceof FullControlLayoutParams; 116 } 117 118 @Override 119 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 120 return new FullControlLayoutParams(p); 121 } 122 123 @Override 124 public LayoutParams generateLayoutParams(AttributeSet attrs) { 125 return new FullControlLayoutParams(getContext(), attrs); 126 } 127 128 @Override 129 protected LayoutParams generateDefaultLayoutParams() { 130 return new FullControlLayoutParams(getWidth(), getHeight()); 131 } 132 133 @Override 134 public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { 135 return onStartNestedScroll(child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH); 136 } 137 138 @Override 139 public void onNestedScrollAccepted(View child, View target, int axes) { 140 onNestedScrollAccepted(child, target, axes, ViewCompat.TYPE_TOUCH); 141 } 142 143 @Override 144 public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { 145 onNestedPreScroll(target, dx, dy, consumed, ViewCompat.TYPE_TOUCH); 146 } 147 148 @Override 149 public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, 150 int dyUnconsumed) { 151 onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, 152 ViewCompat.TYPE_TOUCH); 153 } 154 155 @Override 156 public void onStopNestedScroll(View target) { 157 onStopNestedScroll(target, ViewCompat.TYPE_TOUCH); 158 } 159 160 @Override 161 public int getNestedScrollAxes() { 162 return mNestedScrollingDelegate != null 163 ? mNestedScrollingDelegate.getNestedScrollAxes() 164 : 0; 165 } 166 167 @Override 168 public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, 169 @ViewCompat.ScrollAxis int axes, @ViewCompat.NestedScrollType int type) { 170 return mNestedScrollingDelegate != null 171 && mNestedScrollingDelegate.onStartNestedScroll(child, target, axes, type); 172 } 173 174 @Override 175 public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, 176 @ViewCompat.ScrollAxis int axes, @ViewCompat.NestedScrollType int type) { 177 if (mNestedScrollingDelegate != null) { 178 mNestedScrollingDelegate.onNestedScrollAccepted(child, target, axes, type); 179 } 180 } 181 182 @Override 183 public boolean onNestedPreFling(View target, float velocityX, float velocityY) { 184 return mNestedScrollingDelegate != null 185 && mNestedScrollingDelegate.onNestedPreFling(target, velocityX, velocityY); 186 } 187 188 @Override 189 public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { 190 return mNestedScrollingDelegate != null 191 && mNestedScrollingDelegate.onNestedFling(target, velocityX, velocityY, consumed); 192 } 193 194 @Override 195 public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, 196 int dxUnconsumed, int dyUnconsumed, @ViewCompat.NestedScrollType int type) { 197 if (mNestedScrollingDelegate != null) { 198 mNestedScrollingDelegate.onNestedScroll(target, dxConsumed, dyConsumed, 199 dxUnconsumed, dyUnconsumed, type); 200 } 201 } 202 203 @Override 204 public void onNestedPreScroll(@NonNull View target, int dx, int dy, @Nullable int[] consumed, 205 @ViewCompat.NestedScrollType int type) { 206 if (mNestedScrollingDelegate != null) { 207 mNestedScrollingDelegate.onNestedPreScroll(target, dx, dy, consumed, type); 208 } 209 } 210 211 @Override 212 public void onStopNestedScroll(@NonNull View target, @ViewCompat.NestedScrollType int type) { 213 if (mNestedScrollingDelegate != null) { 214 mNestedScrollingDelegate.onStopNestedScroll(target, type); 215 } 216 } 217 218 public void setNestedScrollingDelegate(NestedScrollingParent2 delegate) { 219 mNestedScrollingDelegate = delegate; 220 } 221 222 public static class FullControlLayoutParams extends FrameLayout.LayoutParams { 223 224 Integer wSpec; 225 Integer hSpec; 226 227 public FullControlLayoutParams(Context c, AttributeSet attrs) { 228 super(c, attrs); 229 } 230 231 public FullControlLayoutParams(int width, int height) { 232 super(width, height); 233 } 234 235 public FullControlLayoutParams(ViewGroup.LayoutParams source) { 236 super(source); 237 } 238 239 public FullControlLayoutParams(FrameLayout.LayoutParams source) { 240 super(source); 241 } 242 243 public FullControlLayoutParams(MarginLayoutParams source) { 244 super(source); 245 } 246 } 247 } 248