177bb6f179a5495c90470f910d46667d8306ae010Alan Viverette/* 277bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * Copyright (C) 2015 The Android Open Source Project 377bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * 477bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * Licensed under the Apache License, Version 2.0 (the "License"); 577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * you may not use this file except in compliance with the License. 677bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * You may obtain a copy of the License at 777bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * 877bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * http://www.apache.org/licenses/LICENSE-2.0 977bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * 1077bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * Unless required by applicable law or agreed to in writing, software 1177bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * distributed under the License is distributed on an "AS IS" BASIS, 1277bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1377bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * See the License for the specific language governing permissions and 1477bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * limitations under the License. 1577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette */ 1677bb6f179a5495c90470f910d46667d8306ae010Alan Viverette 1777bb6f179a5495c90470f910d46667d8306ae010Alan Viverettepackage com.android.internal.widget; 1877bb6f179a5495c90470f910d46667d8306ae010Alan Viverette 1977bb6f179a5495c90470f910d46667d8306ae010Alan Viveretteimport android.content.Context; 20922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viveretteimport android.content.res.TypedArray; 2177bb6f179a5495c90470f910d46667d8306ae010Alan Viveretteimport android.util.AttributeSet; 2277bb6f179a5495c90470f910d46667d8306ae010Alan Viveretteimport android.view.Gravity; 2377bb6f179a5495c90470f910d46667d8306ae010Alan Viveretteimport android.view.View; 2477bb6f179a5495c90470f910d46667d8306ae010Alan Viveretteimport android.widget.LinearLayout; 2577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette 2677bb6f179a5495c90470f910d46667d8306ae010Alan Viveretteimport com.android.internal.R; 2777bb6f179a5495c90470f910d46667d8306ae010Alan Viverette 2877bb6f179a5495c90470f910d46667d8306ae010Alan Viverette/** 2977bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * An extension of LinearLayout that automatically switches to vertical 3077bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * orientation when it can't fit its child views horizontally. 3177bb6f179a5495c90470f910d46667d8306ae010Alan Viverette */ 3277bb6f179a5495c90470f910d46667d8306ae010Alan Viverettepublic class ButtonBarLayout extends LinearLayout { 3377bb6f179a5495c90470f910d46667d8306ae010Alan Viverette /** Whether the current configuration allows stacking. */ 345658328d8d4684640b65c24980885060f56af14dJason Monk private boolean mAllowStacking; 3577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette 36922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette private int mLastWidthSize = -1; 37922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette 3877bb6f179a5495c90470f910d46667d8306ae010Alan Viverette public ButtonBarLayout(Context context, AttributeSet attrs) { 3977bb6f179a5495c90470f910d46667d8306ae010Alan Viverette super(context, attrs); 4077bb6f179a5495c90470f910d46667d8306ae010Alan Viverette 41922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ButtonBarLayout); 42922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette mAllowStacking = ta.getBoolean(R.styleable.ButtonBarLayout_allowStacking, false); 43922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette ta.recycle(); 4477bb6f179a5495c90470f910d46667d8306ae010Alan Viverette } 4577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette 465658328d8d4684640b65c24980885060f56af14dJason Monk public void setAllowStacking(boolean allowStacking) { 475658328d8d4684640b65c24980885060f56af14dJason Monk if (mAllowStacking != allowStacking) { 485658328d8d4684640b65c24980885060f56af14dJason Monk mAllowStacking = allowStacking; 495658328d8d4684640b65c24980885060f56af14dJason Monk if (!mAllowStacking && getOrientation() == LinearLayout.VERTICAL) { 505658328d8d4684640b65c24980885060f56af14dJason Monk setStacked(false); 515658328d8d4684640b65c24980885060f56af14dJason Monk } 525658328d8d4684640b65c24980885060f56af14dJason Monk requestLayout(); 535658328d8d4684640b65c24980885060f56af14dJason Monk } 545658328d8d4684640b65c24980885060f56af14dJason Monk } 555658328d8d4684640b65c24980885060f56af14dJason Monk 5677bb6f179a5495c90470f910d46667d8306ae010Alan Viverette @Override 57922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 58cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 59cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette 60922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette if (mAllowStacking) { 61cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette if (widthSize > mLastWidthSize && isStacked()) { 62922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette // We're being measured wider this time, try un-stacking. 63922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette setStacked(false); 64922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette } 6577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette 66922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette mLastWidthSize = widthSize; 6777bb6f179a5495c90470f910d46667d8306ae010Alan Viverette } 6877bb6f179a5495c90470f910d46667d8306ae010Alan Viverette 69cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette boolean needsRemeasure = false; 70cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette 71cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette // If we're not stacked, make sure the measure spec is AT_MOST rather 72cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette // than EXACTLY. This ensures that we'll still get TOO_SMALL so that we 73cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette // know to stack the buttons. 74cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette final int initialWidthMeasureSpec; 75cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette if (!isStacked() && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) { 76cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette initialWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST); 77cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette 78cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette // We'll need to remeasure again to fill excess space. 79cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette needsRemeasure = true; 80cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette } else { 81cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette initialWidthMeasureSpec = widthMeasureSpec; 82cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette } 83cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette 84cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette super.onMeasure(initialWidthMeasureSpec, heightMeasureSpec); 8577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette 86cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette if (mAllowStacking && !isStacked()) { 8777bb6f179a5495c90470f910d46667d8306ae010Alan Viverette final int measuredWidth = getMeasuredWidthAndState(); 8877bb6f179a5495c90470f910d46667d8306ae010Alan Viverette final int measuredWidthState = measuredWidth & MEASURED_STATE_MASK; 8977bb6f179a5495c90470f910d46667d8306ae010Alan Viverette if (measuredWidthState == MEASURED_STATE_TOO_SMALL) { 9077bb6f179a5495c90470f910d46667d8306ae010Alan Viverette setStacked(true); 9177bb6f179a5495c90470f910d46667d8306ae010Alan Viverette 9277bb6f179a5495c90470f910d46667d8306ae010Alan Viverette // Measure again in the new orientation. 93cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette needsRemeasure = true; 9477bb6f179a5495c90470f910d46667d8306ae010Alan Viverette } 9577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette } 96cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette 97cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette if (needsRemeasure) { 98cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette super.onMeasure(widthMeasureSpec, heightMeasureSpec); 99cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette } 10077bb6f179a5495c90470f910d46667d8306ae010Alan Viverette } 10177bb6f179a5495c90470f910d46667d8306ae010Alan Viverette 10277bb6f179a5495c90470f910d46667d8306ae010Alan Viverette private void setStacked(boolean stacked) { 10377bb6f179a5495c90470f910d46667d8306ae010Alan Viverette setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL); 10477bb6f179a5495c90470f910d46667d8306ae010Alan Viverette setGravity(stacked ? Gravity.RIGHT : Gravity.BOTTOM); 10577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette 106922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette final View spacer = findViewById(R.id.spacer); 107922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette if (spacer != null) { 108922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette spacer.setVisibility(stacked ? View.GONE : View.INVISIBLE); 10977bb6f179a5495c90470f910d46667d8306ae010Alan Viverette } 11077bb6f179a5495c90470f910d46667d8306ae010Alan Viverette 11177bb6f179a5495c90470f910d46667d8306ae010Alan Viverette // Reverse the child order. This is specific to the Material button 11277bb6f179a5495c90470f910d46667d8306ae010Alan Viverette // bar's layout XML and will probably not generalize. 11377bb6f179a5495c90470f910d46667d8306ae010Alan Viverette final int childCount = getChildCount(); 11477bb6f179a5495c90470f910d46667d8306ae010Alan Viverette for (int i = childCount - 2; i >= 0; i--) { 11577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette bringChildToFront(getChildAt(i)); 11677bb6f179a5495c90470f910d46667d8306ae010Alan Viverette } 117cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette } 11877bb6f179a5495c90470f910d46667d8306ae010Alan Viverette 119cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette private boolean isStacked() { 120cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette return getOrientation() == LinearLayout.VERTICAL; 12177bb6f179a5495c90470f910d46667d8306ae010Alan Viverette } 12277bb6f179a5495c90470f910d46667d8306ae010Alan Viverette} 123