Home | History | Annotate | Download | only in view
      1 /*
      2  * Copyright (C) 2006 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.view;
     18 import android.graphics.Rect;
     19 
     20 /**
     21  * Standard constants and tools for placing an object within a potentially
     22  * larger container.
     23  */
     24 public class Gravity
     25 {
     26     /** Constant indicating that no gravity has been set **/
     27     public static final int NO_GRAVITY = 0x0000;
     28 
     29     /** Raw bit indicating the gravity for an axis has been specified. */
     30     public static final int AXIS_SPECIFIED = 0x0001;
     31 
     32     /** Raw bit controlling how the left/top edge is placed. */
     33     public static final int AXIS_PULL_BEFORE = 0x0002;
     34     /** Raw bit controlling how the right/bottom edge is placed. */
     35     public static final int AXIS_PULL_AFTER = 0x0004;
     36     /** Raw bit controlling whether the right/bottom edge is clipped to its
     37      * container, based on the gravity direction being applied. */
     38     public static final int AXIS_CLIP = 0x0008;
     39 
     40     /** Bits defining the horizontal axis. */
     41     public static final int AXIS_X_SHIFT = 0;
     42     /** Bits defining the vertical axis. */
     43     public static final int AXIS_Y_SHIFT = 4;
     44 
     45     /** Push object to the top of its container, not changing its size. */
     46     public static final int TOP = (AXIS_PULL_BEFORE|AXIS_SPECIFIED)<<AXIS_Y_SHIFT;
     47     /** Push object to the bottom of its container, not changing its size. */
     48     public static final int BOTTOM = (AXIS_PULL_AFTER|AXIS_SPECIFIED)<<AXIS_Y_SHIFT;
     49     /** Push object to the left of its container, not changing its size. */
     50     public static final int LEFT = (AXIS_PULL_BEFORE|AXIS_SPECIFIED)<<AXIS_X_SHIFT;
     51     /** Push object to the right of its container, not changing its size. */
     52     public static final int RIGHT = (AXIS_PULL_AFTER|AXIS_SPECIFIED)<<AXIS_X_SHIFT;
     53 
     54     /** Place object in the vertical center of its container, not changing its
     55      *  size. */
     56     public static final int CENTER_VERTICAL = AXIS_SPECIFIED<<AXIS_Y_SHIFT;
     57     /** Grow the vertical size of the object if needed so it completely fills
     58      *  its container. */
     59     public static final int FILL_VERTICAL = TOP|BOTTOM;
     60 
     61     /** Place object in the horizontal center of its container, not changing its
     62      *  size. */
     63     public static final int CENTER_HORIZONTAL = AXIS_SPECIFIED<<AXIS_X_SHIFT;
     64     /** Grow the horizontal size of the object if needed so it completely fills
     65      *  its container. */
     66     public static final int FILL_HORIZONTAL = LEFT|RIGHT;
     67 
     68     /** Place the object in the center of its container in both the vertical
     69      *  and horizontal axis, not changing its size. */
     70     public static final int CENTER = CENTER_VERTICAL|CENTER_HORIZONTAL;
     71 
     72     /** Grow the horizontal and vertical size of the object if needed so it
     73      *  completely fills its container. */
     74     public static final int FILL = FILL_VERTICAL|FILL_HORIZONTAL;
     75 
     76     /** Flag to clip the edges of the object to its container along the
     77      *  vertical axis. */
     78     public static final int CLIP_VERTICAL = AXIS_CLIP<<AXIS_Y_SHIFT;
     79 
     80     /** Flag to clip the edges of the object to its container along the
     81      *  horizontal axis. */
     82     public static final int CLIP_HORIZONTAL = AXIS_CLIP<<AXIS_X_SHIFT;
     83 
     84     /** Raw bit controlling whether the layout direction is relative or not (START/END instead of
     85      * absolute LEFT/RIGHT).
     86      */
     87     public static final int RELATIVE_LAYOUT_DIRECTION = 0x00800000;
     88 
     89     /**
     90      * Binary mask to get the absolute horizontal gravity of a gravity.
     91      */
     92     public static final int HORIZONTAL_GRAVITY_MASK = (AXIS_SPECIFIED |
     93             AXIS_PULL_BEFORE | AXIS_PULL_AFTER) << AXIS_X_SHIFT;
     94     /**
     95      * Binary mask to get the vertical gravity of a gravity.
     96      */
     97     public static final int VERTICAL_GRAVITY_MASK = (AXIS_SPECIFIED |
     98             AXIS_PULL_BEFORE | AXIS_PULL_AFTER) << AXIS_Y_SHIFT;
     99 
    100     /** Special constant to enable clipping to an overall display along the
    101      *  vertical dimension.  This is not applied by default by
    102      *  {@link #apply(int, int, int, Rect, int, int, Rect)}; you must do so
    103      *  yourself by calling {@link #applyDisplay}.
    104      */
    105     public static final int DISPLAY_CLIP_VERTICAL = 0x10000000;
    106 
    107     /** Special constant to enable clipping to an overall display along the
    108      *  horizontal dimension.  This is not applied by default by
    109      *  {@link #apply(int, int, int, Rect, int, int, Rect)}; you must do so
    110      *  yourself by calling {@link #applyDisplay}.
    111      */
    112     public static final int DISPLAY_CLIP_HORIZONTAL = 0x01000000;
    113 
    114     /** Push object to x-axis position at the start of its container, not changing its size. */
    115     public static final int START = RELATIVE_LAYOUT_DIRECTION | LEFT;
    116 
    117     /** Push object to x-axis position at the end of its container, not changing its size. */
    118     public static final int END = RELATIVE_LAYOUT_DIRECTION | RIGHT;
    119 
    120     /**
    121      * Binary mask for the horizontal gravity and script specific direction bit.
    122      */
    123     public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = START | END;
    124 
    125     /**
    126      * Apply a gravity constant to an object. This supposes that the layout direction is LTR.
    127      *
    128      * @param gravity The desired placement of the object, as defined by the
    129      *                constants in this class.
    130      * @param w The horizontal size of the object.
    131      * @param h The vertical size of the object.
    132      * @param container The frame of the containing space, in which the object
    133      *                  will be placed.  Should be large enough to contain the
    134      *                  width and height of the object.
    135      * @param outRect Receives the computed frame of the object in its
    136      *                container.
    137      */
    138     public static void apply(int gravity, int w, int h, Rect container, Rect outRect) {
    139         apply(gravity, w, h, container, 0, 0, outRect);
    140     }
    141 
    142     /**
    143      * Apply a gravity constant to an object and take care if layout direction is RTL or not.
    144      *
    145      * @param gravity The desired placement of the object, as defined by the
    146      *                constants in this class.
    147      * @param w The horizontal size of the object.
    148      * @param h The vertical size of the object.
    149      * @param container The frame of the containing space, in which the object
    150      *                  will be placed.  Should be large enough to contain the
    151      *                  width and height of the object.
    152      * @param outRect Receives the computed frame of the object in its
    153      *                container.
    154      * @param layoutDirection The layout direction.
    155      *
    156      * @see View#LAYOUT_DIRECTION_LTR
    157      * @see View#LAYOUT_DIRECTION_RTL
    158      */
    159     public static void apply(int gravity, int w, int h, Rect container,
    160             Rect outRect, int layoutDirection) {
    161         int absGravity = getAbsoluteGravity(gravity, layoutDirection);
    162         apply(absGravity, w, h, container, 0, 0, outRect);
    163     }
    164 
    165     /**
    166      * Apply a gravity constant to an object.
    167      *
    168      * @param gravity The desired placement of the object, as defined by the
    169      *                constants in this class.
    170      * @param w The horizontal size of the object.
    171      * @param h The vertical size of the object.
    172      * @param container The frame of the containing space, in which the object
    173      *                  will be placed.  Should be large enough to contain the
    174      *                  width and height of the object.
    175      * @param xAdj Offset to apply to the X axis.  If gravity is LEFT this
    176      *             pushes it to the right; if gravity is RIGHT it pushes it to
    177      *             the left; if gravity is CENTER_HORIZONTAL it pushes it to the
    178      *             right or left; otherwise it is ignored.
    179      * @param yAdj Offset to apply to the Y axis.  If gravity is TOP this pushes
    180      *             it down; if gravity is BOTTOM it pushes it up; if gravity is
    181      *             CENTER_VERTICAL it pushes it down or up; otherwise it is
    182      *             ignored.
    183      * @param outRect Receives the computed frame of the object in its
    184      *                container.
    185      */
    186     public static void apply(int gravity, int w, int h, Rect container,
    187             int xAdj, int yAdj, Rect outRect) {
    188         switch (gravity&((AXIS_PULL_BEFORE|AXIS_PULL_AFTER)<<AXIS_X_SHIFT)) {
    189             case 0:
    190                 outRect.left = container.left
    191                         + ((container.right - container.left - w)/2) + xAdj;
    192                 outRect.right = outRect.left + w;
    193                 if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT))
    194                         == (AXIS_CLIP<<AXIS_X_SHIFT)) {
    195                     if (outRect.left < container.left) {
    196                         outRect.left = container.left;
    197                     }
    198                     if (outRect.right > container.right) {
    199                         outRect.right = container.right;
    200                     }
    201                 }
    202                 break;
    203             case AXIS_PULL_BEFORE<<AXIS_X_SHIFT:
    204                 outRect.left = container.left + xAdj;
    205                 outRect.right = outRect.left + w;
    206                 if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT))
    207                         == (AXIS_CLIP<<AXIS_X_SHIFT)) {
    208                     if (outRect.right > container.right) {
    209                         outRect.right = container.right;
    210                     }
    211                 }
    212                 break;
    213             case AXIS_PULL_AFTER<<AXIS_X_SHIFT:
    214                 outRect.right = container.right - xAdj;
    215                 outRect.left = outRect.right - w;
    216                 if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT))
    217                         == (AXIS_CLIP<<AXIS_X_SHIFT)) {
    218                     if (outRect.left < container.left) {
    219                         outRect.left = container.left;
    220                     }
    221                 }
    222                 break;
    223             default:
    224                 outRect.left = container.left + xAdj;
    225                 outRect.right = container.right + xAdj;
    226                 break;
    227         }
    228 
    229         switch (gravity&((AXIS_PULL_BEFORE|AXIS_PULL_AFTER)<<AXIS_Y_SHIFT)) {
    230             case 0:
    231                 outRect.top = container.top
    232                         + ((container.bottom - container.top - h)/2) + yAdj;
    233                 outRect.bottom = outRect.top + h;
    234                 if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT))
    235                         == (AXIS_CLIP<<AXIS_Y_SHIFT)) {
    236                     if (outRect.top < container.top) {
    237                         outRect.top = container.top;
    238                     }
    239                     if (outRect.bottom > container.bottom) {
    240                         outRect.bottom = container.bottom;
    241                     }
    242                 }
    243                 break;
    244             case AXIS_PULL_BEFORE<<AXIS_Y_SHIFT:
    245                 outRect.top = container.top + yAdj;
    246                 outRect.bottom = outRect.top + h;
    247                 if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT))
    248                         == (AXIS_CLIP<<AXIS_Y_SHIFT)) {
    249                     if (outRect.bottom > container.bottom) {
    250                         outRect.bottom = container.bottom;
    251                     }
    252                 }
    253                 break;
    254             case AXIS_PULL_AFTER<<AXIS_Y_SHIFT:
    255                 outRect.bottom = container.bottom - yAdj;
    256                 outRect.top = outRect.bottom - h;
    257                 if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT))
    258                         == (AXIS_CLIP<<AXIS_Y_SHIFT)) {
    259                     if (outRect.top < container.top) {
    260                         outRect.top = container.top;
    261                     }
    262                 }
    263                 break;
    264             default:
    265                 outRect.top = container.top + yAdj;
    266                 outRect.bottom = container.bottom + yAdj;
    267                 break;
    268         }
    269     }
    270 
    271     /**
    272      * Apply a gravity constant to an object.
    273      *
    274      * @param gravity The desired placement of the object, as defined by the
    275      *                constants in this class.
    276      * @param w The horizontal size of the object.
    277      * @param h The vertical size of the object.
    278      * @param container The frame of the containing space, in which the object
    279      *                  will be placed.  Should be large enough to contain the
    280      *                  width and height of the object.
    281      * @param xAdj Offset to apply to the X axis.  If gravity is LEFT this
    282      *             pushes it to the right; if gravity is RIGHT it pushes it to
    283      *             the left; if gravity is CENTER_HORIZONTAL it pushes it to the
    284      *             right or left; otherwise it is ignored.
    285      * @param yAdj Offset to apply to the Y axis.  If gravity is TOP this pushes
    286      *             it down; if gravity is BOTTOM it pushes it up; if gravity is
    287      *             CENTER_VERTICAL it pushes it down or up; otherwise it is
    288      *             ignored.
    289      * @param outRect Receives the computed frame of the object in its
    290      *                container.
    291      * @param layoutDirection The layout direction.
    292      *
    293      * @see View#LAYOUT_DIRECTION_LTR
    294      * @see View#LAYOUT_DIRECTION_RTL
    295      */
    296     public static void apply(int gravity, int w, int h, Rect container,
    297                              int xAdj, int yAdj, Rect outRect, int layoutDirection) {
    298         int absGravity = getAbsoluteGravity(gravity, layoutDirection);
    299         apply(absGravity, w, h, container, xAdj, yAdj, outRect);
    300     }
    301 
    302     /**
    303      * Apply additional gravity behavior based on the overall "display" that an
    304      * object exists in.  This can be used after
    305      * {@link #apply(int, int, int, Rect, int, int, Rect)} to place the object
    306      * within a visible display.  By default this moves or clips the object
    307      * to be visible in the display; the gravity flags
    308      * {@link #DISPLAY_CLIP_HORIZONTAL} and {@link #DISPLAY_CLIP_VERTICAL}
    309      * can be used to change this behavior.
    310      *
    311      * @param gravity Gravity constants to modify the placement within the
    312      * display.
    313      * @param display The rectangle of the display in which the object is
    314      * being placed.
    315      * @param inoutObj Supplies the current object position; returns with it
    316      * modified if needed to fit in the display.
    317      */
    318     public static void applyDisplay(int gravity, Rect display, Rect inoutObj) {
    319         if ((gravity&DISPLAY_CLIP_VERTICAL) != 0) {
    320             if (inoutObj.top < display.top) inoutObj.top = display.top;
    321             if (inoutObj.bottom > display.bottom) inoutObj.bottom = display.bottom;
    322         } else {
    323             int off = 0;
    324             if (inoutObj.top < display.top) off = display.top-inoutObj.top;
    325             else if (inoutObj.bottom > display.bottom) off = display.bottom-inoutObj.bottom;
    326             if (off != 0) {
    327                 if (inoutObj.height() > (display.bottom-display.top)) {
    328                     inoutObj.top = display.top;
    329                     inoutObj.bottom = display.bottom;
    330                 } else {
    331                     inoutObj.top += off;
    332                     inoutObj.bottom += off;
    333                 }
    334             }
    335         }
    336 
    337         if ((gravity&DISPLAY_CLIP_HORIZONTAL) != 0) {
    338             if (inoutObj.left < display.left) inoutObj.left = display.left;
    339             if (inoutObj.right > display.right) inoutObj.right = display.right;
    340         } else {
    341             int off = 0;
    342             if (inoutObj.left < display.left) off = display.left-inoutObj.left;
    343             else if (inoutObj.right > display.right) off = display.right-inoutObj.right;
    344             if (off != 0) {
    345                 if (inoutObj.width() > (display.right-display.left)) {
    346                     inoutObj.left = display.left;
    347                     inoutObj.right = display.right;
    348                 } else {
    349                     inoutObj.left += off;
    350                     inoutObj.right += off;
    351                 }
    352             }
    353         }
    354     }
    355 
    356     /**
    357      * Apply additional gravity behavior based on the overall "display" that an
    358      * object exists in.  This can be used after
    359      * {@link #apply(int, int, int, Rect, int, int, Rect)} to place the object
    360      * within a visible display.  By default this moves or clips the object
    361      * to be visible in the display; the gravity flags
    362      * {@link #DISPLAY_CLIP_HORIZONTAL} and {@link #DISPLAY_CLIP_VERTICAL}
    363      * can be used to change this behavior.
    364      *
    365      * @param gravity Gravity constants to modify the placement within the
    366      * display.
    367      * @param display The rectangle of the display in which the object is
    368      * being placed.
    369      * @param inoutObj Supplies the current object position; returns with it
    370      * modified if needed to fit in the display.
    371      * @param layoutDirection The layout direction.
    372      *
    373      * @see View#LAYOUT_DIRECTION_LTR
    374      * @see View#LAYOUT_DIRECTION_RTL
    375      */
    376     public static void applyDisplay(int gravity, Rect display, Rect inoutObj, int layoutDirection) {
    377         int absGravity = getAbsoluteGravity(gravity, layoutDirection);
    378         applyDisplay(absGravity, display, inoutObj);
    379     }
    380 
    381     /**
    382      * <p>Indicate whether the supplied gravity has a vertical pull.</p>
    383      *
    384      * @param gravity the gravity to check for vertical pull
    385      * @return true if the supplied gravity has a vertical pull
    386      */
    387     public static boolean isVertical(int gravity) {
    388         return gravity > 0 && (gravity & VERTICAL_GRAVITY_MASK) != 0;
    389     }
    390 
    391     /**
    392      * <p>Indicate whether the supplied gravity has an horizontal pull.</p>
    393      *
    394      * @param gravity the gravity to check for horizontal pull
    395      * @return true if the supplied gravity has an horizontal pull
    396      */
    397     public static boolean isHorizontal(int gravity) {
    398         return gravity > 0 && (gravity & RELATIVE_HORIZONTAL_GRAVITY_MASK) != 0;
    399     }
    400 
    401     /**
    402      * <p>Convert script specific gravity to absolute horizontal value.</p>
    403      *
    404      * if horizontal direction is LTR, then START will set LEFT and END will set RIGHT.
    405      * if horizontal direction is RTL, then START will set RIGHT and END will set LEFT.
    406      *
    407      *
    408      * @param gravity The gravity to convert to absolute (horizontal) values.
    409      * @param layoutDirection The layout direction.
    410      * @return gravity converted to absolute (horizontal) values.
    411      */
    412     public static int getAbsoluteGravity(int gravity, int layoutDirection) {
    413         int result = gravity;
    414         // If layout is script specific and gravity is horizontal relative (START or END)
    415         if ((result & RELATIVE_LAYOUT_DIRECTION) > 0) {
    416             if ((result & Gravity.START) == Gravity.START) {
    417                 // Remove the START bit
    418                 result &= ~START;
    419                 if (layoutDirection == View.LAYOUT_DIRECTION_RTL) {
    420                     // Set the RIGHT bit
    421                     result |= RIGHT;
    422                 } else {
    423                     // Set the LEFT bit
    424                     result |= LEFT;
    425                 }
    426             } else if ((result & Gravity.END) == Gravity.END) {
    427                 // Remove the END bit
    428                 result &= ~END;
    429                 if (layoutDirection == View.LAYOUT_DIRECTION_RTL) {
    430                     // Set the LEFT bit
    431                     result |= LEFT;
    432                 } else {
    433                     // Set the RIGHT bit
    434                     result |= RIGHT;
    435                 }
    436             }
    437             // Don't need the script specific bit any more, so remove it as we are converting to
    438             // absolute values (LEFT or RIGHT)
    439             result &= ~RELATIVE_LAYOUT_DIRECTION;
    440         }
    441         return result;
    442     }
    443 }
    444