ActionBarContextView.java revision 29ed7575c2129974a57ad77b3531bc5995940c6c
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.app.ActionBarImpl; 20 21import android.app.ActionBar; 22import android.content.Context; 23import android.content.res.TypedArray; 24import android.graphics.drawable.Drawable; 25import android.util.AttributeSet; 26import android.view.LayoutInflater; 27import android.view.Menu; 28import android.view.MenuItem; 29import android.view.View; 30import android.view.ViewGroup; 31import android.view.View.MeasureSpec; 32import android.view.ViewGroup.LayoutParams; 33import android.widget.ImageButton; 34import android.widget.LinearLayout; 35import android.widget.TextView; 36 37/** 38 * @hide 39 */ 40public class ActionBarContextView extends ViewGroup { 41 // TODO: This must be defined in the default theme 42 private static final int CONTENT_HEIGHT_DIP = 50; 43 44 private int mItemPadding; 45 private int mItemMargin; 46 private int mContentHeight; 47 48 private CharSequence mTitle; 49 private CharSequence mSubtitle; 50 51 private ImageButton mCloseButton; 52 private View mCustomView; 53 private LinearLayout mTitleLayout; 54 private TextView mTitleView; 55 private TextView mSubtitleView; 56 private Drawable mCloseDrawable; 57 58 public ActionBarContextView(Context context) { 59 this(context, null, 0); 60 } 61 62 public ActionBarContextView(Context context, AttributeSet attrs) { 63 this(context, attrs, 0); 64 } 65 66 public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) { 67 super(context, attrs, defStyle); 68 69 TypedArray a = context.obtainStyledAttributes(attrs, 70 com.android.internal.R.styleable.Theme); 71 mItemPadding = a.getDimensionPixelOffset( 72 com.android.internal.R.styleable.Theme_actionButtonPadding, 0); 73 setBackgroundDrawable(a.getDrawable( 74 com.android.internal.R.styleable.Theme_actionBarContextBackground)); 75 mCloseDrawable = a.getDrawable( 76 com.android.internal.R.styleable.Theme_actionBarCloseContextDrawable); 77 mItemMargin = mItemPadding / 2; 78 79 mContentHeight = CONTENT_HEIGHT_DIP; 80 a.recycle(); 81 } 82 83 public void setCustomView(View view) { 84 if (mCustomView != null) { 85 removeView(mCustomView); 86 } 87 mCustomView = view; 88 if (mTitleLayout != null) { 89 removeView(mTitleLayout); 90 mTitleLayout = null; 91 } 92 if (view != null) { 93 addView(view); 94 } 95 requestLayout(); 96 } 97 98 public void setTitle(CharSequence title) { 99 mTitle = title; 100 initTitle(); 101 } 102 103 public void setSubtitle(CharSequence subtitle) { 104 mSubtitle = subtitle; 105 initTitle(); 106 } 107 108 public CharSequence getTitle() { 109 return mTitle; 110 } 111 112 public CharSequence getSubtitle() { 113 return mSubtitle; 114 } 115 116 private void initTitle() { 117 if (mTitleLayout == null) { 118 LayoutInflater inflater = LayoutInflater.from(getContext()); 119 mTitleLayout = (LinearLayout) inflater.inflate(R.layout.action_bar_title_item, null); 120 mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title); 121 mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle); 122 if (mTitle != null) { 123 mTitleView.setText(mTitle); 124 } 125 if (mSubtitle != null) { 126 mSubtitleView.setText(mSubtitle); 127 } 128 addView(mTitleLayout); 129 } else { 130 mTitleView.setText(mTitle); 131 mSubtitleView.setText(mSubtitle); 132 if (mTitleLayout.getParent() == null) { 133 addView(mTitleLayout); 134 } 135 } 136 } 137 138 public void initForMode(final ActionBar.ContextMode mode) { 139 final ActionBarImpl.ContextMode implMode = (ActionBarImpl.ContextMode) mode; 140 141 if (mCloseButton == null) { 142 mCloseButton = new ImageButton(getContext()); 143 mCloseButton.setImageDrawable(mCloseDrawable); 144 mCloseButton.setBackgroundDrawable(null); 145 mCloseButton.setOnClickListener(new OnClickListener() { 146 public void onClick(View v) { 147 mode.finish(); 148 } 149 }); 150 } 151 addView(mCloseButton); 152 153 final Context context = getContext(); 154 final Menu menu = mode.getMenu(); 155 final int itemCount = menu.size(); 156 for (int i = 0; i < itemCount; i++) { 157 final MenuItem item = menu.getItem(i); 158 final ImageButton button = new ImageButton(context, null, 159 com.android.internal.R.attr.actionButtonStyle); 160 button.setClickable(true); 161 button.setFocusable(true); 162 button.setImageDrawable(item.getIcon()); 163 button.setId(item.getItemId()); 164 button.setVisibility(item.isVisible() ? VISIBLE : GONE); 165 button.setEnabled(item.isEnabled()); 166 167 button.setOnClickListener(new OnClickListener() { 168 public void onClick(View v) { 169 implMode.dispatchOnContextItemClicked(item); 170 } 171 }); 172 173 addView(button); 174 } 175 requestLayout(); 176 } 177 178 public void closeMode() { 179 removeAllViews(); 180 mCustomView = null; 181 } 182 183 @Override 184 protected LayoutParams generateDefaultLayoutParams() { 185 // Used by custom views if they don't supply layout params. Everything else 186 // added to an ActionBarContextView should have them already. 187 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 188 } 189 190 @Override 191 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 192 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 193 if (widthMode != MeasureSpec.EXACTLY) { 194 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 195 "with android:layout_width=\"match_parent\" (or fill_parent)"); 196 } 197 198 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 199 if (heightMode != MeasureSpec.AT_MOST) { 200 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 201 "with android:layout_height=\"wrap_content\""); 202 } 203 204 final int contentWidth = MeasureSpec.getSize(widthMeasureSpec); 205 final int itemMargin = mItemPadding; 206 207 int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight(); 208 final int height = mContentHeight - getPaddingTop() - getPaddingBottom(); 209 final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); 210 211 if (mCloseButton != null) { 212 availableWidth = measureChildView(mCloseButton, availableWidth, 213 childSpecHeight, itemMargin); 214 } 215 216 if (mTitleLayout != null && mCustomView == null) { 217 availableWidth = measureChildView(mTitleLayout, availableWidth, 218 childSpecHeight, itemMargin); 219 } 220 221 final int childCount = getChildCount(); 222 for (int i = 0; i < childCount; i++) { 223 final View child = getChildAt(i); 224 if (child == mCloseButton || child == mTitleLayout || child == mCustomView) { 225 continue; 226 } 227 228 availableWidth = measureChildView(child, availableWidth, childSpecHeight, itemMargin); 229 } 230 231 if (mCustomView != null) { 232 LayoutParams lp = mCustomView.getLayoutParams(); 233 final int customWidthMode = lp.width != LayoutParams.WRAP_CONTENT ? 234 MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; 235 final int customWidth = lp.width >= 0 ? 236 Math.min(lp.width, availableWidth) : availableWidth; 237 final int customHeightMode = lp.height != LayoutParams.WRAP_CONTENT ? 238 MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; 239 final int customHeight = lp.height >= 0 ? 240 Math.min(lp.height, height) : height; 241 mCustomView.measure(MeasureSpec.makeMeasureSpec(customWidth, customWidthMode), 242 MeasureSpec.makeMeasureSpec(customHeight, customHeightMode)); 243 } 244 245 setMeasuredDimension(contentWidth, mContentHeight); 246 } 247 248 @Override 249 protected void onLayout(boolean changed, int l, int t, int r, int b) { 250 int x = getPaddingLeft(); 251 final int y = getPaddingTop(); 252 final int contentHeight = b - t - getPaddingTop() - getPaddingBottom(); 253 final int itemMargin = mItemPadding; 254 255 if (mCloseButton != null && mCloseButton.getVisibility() != GONE) { 256 x += positionChild(mCloseButton, x, y, contentHeight); 257 } 258 259 if (mTitleLayout != null && mCustomView == null) { 260 x += positionChild(mTitleLayout, x, y, contentHeight) + itemMargin; 261 } 262 263 if (mCustomView != null) { 264 x += positionChild(mCustomView, x, y, contentHeight) + itemMargin; 265 } 266 267 x = r - l - getPaddingRight(); 268 269 final int childCount = getChildCount(); 270 for (int i = 0; i < childCount; i++) { 271 final View child = getChildAt(i); 272 if (child == mCloseButton || child == mTitleLayout || child == mCustomView) { 273 continue; 274 } 275 276 x -= positionChildInverse(child, x, y, contentHeight) + itemMargin; 277 } 278 } 279 280 private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) { 281 child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 282 childSpecHeight); 283 284 availableWidth -= child.getMeasuredWidth(); 285 availableWidth -= spacing; 286 287 return availableWidth; 288 } 289 290 private int positionChild(View child, int x, int y, int contentHeight) { 291 int childWidth = child.getMeasuredWidth(); 292 int childHeight = child.getMeasuredHeight(); 293 int childTop = y + (contentHeight - childHeight) / 2; 294 295 child.layout(x, childTop, x + childWidth, childTop + childHeight); 296 297 return childWidth; 298 } 299 300 private int positionChildInverse(View child, int x, int y, int contentHeight) { 301 int childWidth = child.getMeasuredWidth(); 302 int childHeight = child.getMeasuredHeight(); 303 int childTop = y + (contentHeight - childHeight) / 2; 304 305 child.layout(x - childWidth, childTop, x, childTop + childHeight); 306 307 return childWidth; 308 } 309} 310