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