ActionBarContextView.java revision a7db03705f53c59e63e63c2e67e2db78f8226dcc
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 private void initTitle() { 109 if (mTitleLayout == null) { 110 LayoutInflater inflater = LayoutInflater.from(getContext()); 111 mTitleLayout = (LinearLayout) inflater.inflate(R.layout.action_bar_title_item, null); 112 mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title); 113 mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle); 114 if (mTitle != null) { 115 mTitleView.setText(mTitle); 116 } 117 if (mSubtitle != null) { 118 mSubtitleView.setText(mSubtitle); 119 } 120 addView(mTitleLayout); 121 } else { 122 mTitleView.setText(mTitle); 123 mSubtitleView.setText(mSubtitle); 124 if (mTitleLayout.getParent() == null) { 125 addView(mTitleLayout); 126 } 127 } 128 } 129 130 public void initForMode(final ActionBar.ContextMode mode) { 131 final ActionBarImpl.ContextMode implMode = (ActionBarImpl.ContextMode) mode; 132 133 if (mCloseButton == null) { 134 mCloseButton = new ImageButton(getContext()); 135 mCloseButton.setImageDrawable(mCloseDrawable); 136 mCloseButton.setBackgroundDrawable(null); 137 mCloseButton.setOnClickListener(new OnClickListener() { 138 public void onClick(View v) { 139 mode.finish(); 140 } 141 }); 142 } 143 addView(mCloseButton); 144 145 final Context context = getContext(); 146 final Menu menu = mode.getMenu(); 147 final int itemCount = menu.size(); 148 for (int i = 0; i < itemCount; i++) { 149 final MenuItem item = menu.getItem(i); 150 final ImageButton button = new ImageButton(context, null, 151 com.android.internal.R.attr.actionButtonStyle); 152 button.setClickable(true); 153 button.setFocusable(true); 154 button.setImageDrawable(item.getIcon()); 155 button.setId(item.getItemId()); 156 button.setVisibility(item.isVisible() ? VISIBLE : GONE); 157 button.setEnabled(item.isEnabled()); 158 159 button.setOnClickListener(new OnClickListener() { 160 public void onClick(View v) { 161 implMode.dispatchOnContextItemClicked(item); 162 } 163 }); 164 165 addView(button); 166 } 167 requestLayout(); 168 } 169 170 public void closeMode() { 171 removeAllViews(); 172 mCustomView = null; 173 } 174 175 @Override 176 protected LayoutParams generateDefaultLayoutParams() { 177 // Used by custom views if they don't supply layout params. Everything else 178 // added to an ActionBarContextView should have them already. 179 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 180 } 181 182 @Override 183 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 184 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 185 if (widthMode != MeasureSpec.EXACTLY) { 186 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 187 "with android:layout_width=\"match_parent\" (or fill_parent)"); 188 } 189 190 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 191 if (heightMode != MeasureSpec.AT_MOST) { 192 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 193 "with android:layout_height=\"wrap_content\""); 194 } 195 196 final int contentWidth = MeasureSpec.getSize(widthMeasureSpec); 197 final int itemMargin = mItemPadding; 198 199 int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight(); 200 final int height = mContentHeight - getPaddingTop() - getPaddingBottom(); 201 final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); 202 203 if (mCloseButton != null) { 204 availableWidth = measureChildView(mCloseButton, availableWidth, 205 childSpecHeight, itemMargin); 206 } 207 208 if (mTitleLayout != null && mCustomView == null) { 209 availableWidth = measureChildView(mTitleLayout, availableWidth, 210 childSpecHeight, itemMargin); 211 } 212 213 final int childCount = getChildCount(); 214 for (int i = 0; i < childCount; i++) { 215 final View child = getChildAt(i); 216 if (child == mCloseButton || child == mTitleLayout || child == mCustomView) { 217 continue; 218 } 219 220 availableWidth = measureChildView(child, availableWidth, childSpecHeight, itemMargin); 221 } 222 223 if (mCustomView != null) { 224 LayoutParams lp = mCustomView.getLayoutParams(); 225 final int customWidthMode = lp.width != LayoutParams.WRAP_CONTENT ? 226 MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; 227 final int customWidth = lp.width >= 0 ? 228 Math.min(lp.width, availableWidth) : availableWidth; 229 final int customHeightMode = lp.height != LayoutParams.WRAP_CONTENT ? 230 MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; 231 final int customHeight = lp.height >= 0 ? 232 Math.min(lp.height, height) : height; 233 mCustomView.measure(MeasureSpec.makeMeasureSpec(customWidth, customWidthMode), 234 MeasureSpec.makeMeasureSpec(customHeight, customHeightMode)); 235 } 236 237 setMeasuredDimension(contentWidth, mContentHeight); 238 } 239 240 @Override 241 protected void onLayout(boolean changed, int l, int t, int r, int b) { 242 int x = getPaddingLeft(); 243 final int y = getPaddingTop(); 244 final int contentHeight = b - t - getPaddingTop() - getPaddingBottom(); 245 final int itemMargin = mItemPadding; 246 247 if (mCloseButton != null && mCloseButton.getVisibility() != GONE) { 248 x += positionChild(mCloseButton, x, y, contentHeight); 249 } 250 251 if (mTitleLayout != null && mCustomView == null) { 252 x += positionChild(mTitleLayout, x, y, contentHeight) + itemMargin; 253 } 254 255 if (mCustomView != null) { 256 x += positionChild(mCustomView, x, y, contentHeight) + itemMargin; 257 } 258 259 x = r - l - getPaddingRight(); 260 261 final int childCount = getChildCount(); 262 for (int i = 0; i < childCount; i++) { 263 final View child = getChildAt(i); 264 if (child == mCloseButton || child == mTitleLayout || child == mCustomView) { 265 continue; 266 } 267 268 x -= positionChildInverse(child, x, y, contentHeight) + itemMargin; 269 } 270 } 271 272 private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) { 273 child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 274 childSpecHeight); 275 276 availableWidth -= child.getMeasuredWidth(); 277 availableWidth -= spacing; 278 279 return availableWidth; 280 } 281 282 private int positionChild(View child, int x, int y, int contentHeight) { 283 int childWidth = child.getMeasuredWidth(); 284 int childHeight = child.getMeasuredHeight(); 285 int childTop = y + (contentHeight - childHeight) / 2; 286 287 child.layout(x, childTop, x + childWidth, childTop + childHeight); 288 289 return childWidth; 290 } 291 292 private int positionChildInverse(View child, int x, int y, int contentHeight) { 293 int childWidth = child.getMeasuredWidth(); 294 int childHeight = child.getMeasuredHeight(); 295 int childTop = y + (contentHeight - childHeight) / 2; 296 297 child.layout(x - childWidth, childTop, x, childTop + childHeight); 298 299 return childWidth; 300 } 301} 302