ActionBarContextView.java revision 85446e95afa480cee2247bb96795fccc8cf812af
1/* 2 * Copyright (C) 2010 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 */ 16package com.android.internal.widget; 17 18import com.android.internal.R; 19import com.android.internal.view.menu.ActionMenuView; 20import com.android.internal.view.menu.MenuBuilder; 21 22import android.content.Context; 23import android.content.res.TypedArray; 24import android.util.AttributeSet; 25import android.view.ActionMode; 26import android.view.LayoutInflater; 27import android.view.View; 28import android.view.ViewGroup; 29import android.widget.Button; 30import android.widget.ButtonGroup; 31import android.widget.ImageButton; 32import android.widget.LinearLayout; 33import android.widget.TextView; 34 35/** 36 * @hide 37 */ 38public class ActionBarContextView extends ViewGroup { 39 private int mContentHeight; 40 41 private CharSequence mTitle; 42 private CharSequence mSubtitle; 43 44 private View mClose; 45 private View mCustomView; 46 private LinearLayout mTitleLayout; 47 private TextView mTitleView; 48 private TextView mSubtitleView; 49 private int mTitleStyleRes; 50 private int mSubtitleStyleRes; 51 private ActionMenuView mMenuView; 52 53 public ActionBarContextView(Context context) { 54 this(context, null); 55 } 56 57 public ActionBarContextView(Context context, AttributeSet attrs) { 58 this(context, attrs, com.android.internal.R.attr.actionModeStyle); 59 } 60 61 public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) { 62 super(context, attrs, defStyle); 63 64 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionMode, defStyle, 0); 65 setBackgroundDrawable(a.getDrawable( 66 com.android.internal.R.styleable.ActionMode_background)); 67 mTitleStyleRes = a.getResourceId( 68 com.android.internal.R.styleable.ActionMode_titleTextStyle, 0); 69 mSubtitleStyleRes = a.getResourceId( 70 com.android.internal.R.styleable.ActionMode_subtitleTextStyle, 0); 71 72 mContentHeight = a.getLayoutDimension( 73 com.android.internal.R.styleable.ActionMode_height, 0); 74 a.recycle(); 75 } 76 77 @Override 78 public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) { 79 // No starting an action mode for an existing action mode UI child! (Where would it go?) 80 return null; 81 } 82 83 public void setHeight(int height) { 84 mContentHeight = height; 85 } 86 87 public void setCustomView(View view) { 88 if (mCustomView != null) { 89 removeView(mCustomView); 90 } 91 mCustomView = view; 92 if (mTitleLayout != null) { 93 removeView(mTitleLayout); 94 mTitleLayout = null; 95 } 96 if (view != null) { 97 addView(view); 98 } 99 requestLayout(); 100 } 101 102 public void setTitle(CharSequence title) { 103 mTitle = title; 104 initTitle(); 105 } 106 107 public void setSubtitle(CharSequence subtitle) { 108 mSubtitle = subtitle; 109 initTitle(); 110 } 111 112 public CharSequence getTitle() { 113 return mTitle; 114 } 115 116 public CharSequence getSubtitle() { 117 return mSubtitle; 118 } 119 120 private void initTitle() { 121 if (mTitleLayout == null) { 122 LayoutInflater inflater = LayoutInflater.from(getContext()); 123 inflater.inflate(R.layout.action_bar_title_item, this); 124 mTitleLayout = (LinearLayout) getChildAt(getChildCount() - 1); 125 mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title); 126 mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle); 127 if (mTitle != null) { 128 mTitleView.setText(mTitle); 129 if (mTitleStyleRes != 0) { 130 mTitleView.setTextAppearance(mContext, mTitleStyleRes); 131 } 132 } 133 if (mSubtitle != null) { 134 mSubtitleView.setText(mSubtitle); 135 if (mSubtitleStyleRes != 0) { 136 mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes); 137 } 138 mSubtitleView.setVisibility(VISIBLE); 139 } 140 } else { 141 mTitleView.setText(mTitle); 142 mSubtitleView.setText(mSubtitle); 143 mSubtitleView.setVisibility(mSubtitle != null ? VISIBLE : GONE); 144 if (mTitleLayout.getParent() == null) { 145 addView(mTitleLayout); 146 } 147 } 148 } 149 150 public void initForMode(final ActionMode mode) { 151 if (mClose == null) { 152 LayoutInflater inflater = LayoutInflater.from(mContext); 153 inflater.inflate(R.layout.action_mode_close_item, this); 154 mClose = getChildAt(getChildCount() - 1); 155 } else { 156 addView(mClose); 157 } 158 159 View closeButton = mClose.findViewById(R.id.action_mode_close_button); 160 closeButton.setOnClickListener(new OnClickListener() { 161 public void onClick(View v) { 162 mode.finish(); 163 } 164 }); 165 166 final MenuBuilder menu = (MenuBuilder) mode.getMenu(); 167 mMenuView = (ActionMenuView) menu.getMenuView(MenuBuilder.TYPE_ACTION_BUTTON, this); 168 mMenuView.setOverflowReserved(true); 169 mMenuView.updateChildren(false); 170 addView(mMenuView); 171 } 172 173 public void closeMode() { 174 removeAllViews(); 175 mCustomView = null; 176 mMenuView = null; 177 } 178 179 public boolean showOverflowMenu() { 180 if (mMenuView != null) { 181 return mMenuView.showOverflowMenu(); 182 } 183 return false; 184 } 185 186 public boolean hideOverflowMenu() { 187 if (mMenuView != null) { 188 return mMenuView.hideOverflowMenu(); 189 } 190 return false; 191 } 192 193 public boolean isOverflowMenuShowing() { 194 if (mMenuView != null) { 195 return mMenuView.isOverflowMenuShowing(); 196 } 197 return false; 198 } 199 200 @Override 201 protected LayoutParams generateDefaultLayoutParams() { 202 // Used by custom views if they don't supply layout params. Everything else 203 // added to an ActionBarContextView should have them already. 204 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 205 } 206 207 @Override 208 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 209 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 210 if (widthMode != MeasureSpec.EXACTLY) { 211 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 212 "with android:layout_width=\"match_parent\" (or fill_parent)"); 213 } 214 215 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 216 if (heightMode == MeasureSpec.UNSPECIFIED) { 217 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 218 "with android:layout_height=\"wrap_content\""); 219 } 220 221 final int contentWidth = MeasureSpec.getSize(widthMeasureSpec); 222 223 int maxHeight = mContentHeight > 0 ? 224 mContentHeight : MeasureSpec.getSize(heightMeasureSpec); 225 226 final int verticalPadding = getPaddingTop() + getPaddingBottom(); 227 int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight(); 228 final int height = maxHeight - verticalPadding; 229 final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); 230 231 if (mClose != null) { 232 availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0); 233 } 234 235 if (mTitleLayout != null && mCustomView == null) { 236 availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0); 237 } 238 239 final int childCount = getChildCount(); 240 for (int i = 0; i < childCount; i++) { 241 final View child = getChildAt(i); 242 if (child == mClose || child == mTitleLayout || child == mCustomView) { 243 continue; 244 } 245 246 availableWidth = measureChildView(child, availableWidth, childSpecHeight, 0); 247 } 248 249 if (mCustomView != null) { 250 LayoutParams lp = mCustomView.getLayoutParams(); 251 final int customWidthMode = lp.width != LayoutParams.WRAP_CONTENT ? 252 MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; 253 final int customWidth = lp.width >= 0 ? 254 Math.min(lp.width, availableWidth) : availableWidth; 255 final int customHeightMode = lp.height != LayoutParams.WRAP_CONTENT ? 256 MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; 257 final int customHeight = lp.height >= 0 ? 258 Math.min(lp.height, height) : height; 259 mCustomView.measure(MeasureSpec.makeMeasureSpec(customWidth, customWidthMode), 260 MeasureSpec.makeMeasureSpec(customHeight, customHeightMode)); 261 } 262 263 if (mContentHeight <= 0) { 264 int measuredHeight = 0; 265 final int count = getChildCount(); 266 for (int i = 0; i < count; i++) { 267 View v = getChildAt(i); 268 int paddedViewHeight = v.getMeasuredHeight() + verticalPadding; 269 if (paddedViewHeight > measuredHeight) { 270 measuredHeight = paddedViewHeight; 271 } 272 } 273 setMeasuredDimension(contentWidth, measuredHeight); 274 } else { 275 setMeasuredDimension(contentWidth, maxHeight); 276 } 277 } 278 279 @Override 280 protected void onLayout(boolean changed, int l, int t, int r, int b) { 281 int x = getPaddingLeft(); 282 final int y = getPaddingTop(); 283 final int contentHeight = b - t - getPaddingTop() - getPaddingBottom(); 284 285 if (mClose != null && mClose.getVisibility() != GONE) { 286 x += positionChild(mClose, x, y, contentHeight); 287 } 288 289 if (mTitleLayout != null && mCustomView == null) { 290 x += positionChild(mTitleLayout, x, y, contentHeight); 291 } 292 293 if (mCustomView != null) { 294 x += positionChild(mCustomView, x, y, contentHeight); 295 } 296 297 x = r - l - getPaddingRight(); 298 299 if (mMenuView != null) { 300 x -= positionChildInverse(mMenuView, x, y, contentHeight); 301 } 302 } 303 304 private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) { 305 child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 306 childSpecHeight); 307 308 availableWidth -= child.getMeasuredWidth(); 309 availableWidth -= spacing; 310 311 return availableWidth; 312 } 313 314 private int positionChild(View child, int x, int y, int contentHeight) { 315 int childWidth = child.getMeasuredWidth(); 316 int childHeight = child.getMeasuredHeight(); 317 int childTop = y + (contentHeight - childHeight) / 2; 318 319 child.layout(x, childTop, x + childWidth, childTop + childHeight); 320 321 return childWidth; 322 } 323 324 private int positionChildInverse(View child, int x, int y, int contentHeight) { 325 int childWidth = child.getMeasuredWidth(); 326 int childHeight = child.getMeasuredHeight(); 327 int childTop = y + (contentHeight - childHeight) / 2; 328 329 child.layout(x - childWidth, childTop, x, childTop + childHeight); 330 331 return childWidth; 332 } 333} 334