Home | History | Annotate | Download | only in widget
      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