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 android.support.v7.internal.widget; 17 18import android.content.Context; 19import android.content.res.TypedArray; 20import android.graphics.drawable.Drawable; 21import android.support.v7.appcompat.R; 22import android.support.v7.view.ActionMode; 23import android.support.v7.internal.view.menu.ActionMenuPresenter; 24import android.support.v7.internal.view.menu.ActionMenuView; 25import android.support.v7.internal.view.menu.MenuBuilder; 26import android.text.TextUtils; 27import android.util.AttributeSet; 28import android.view.LayoutInflater; 29import android.view.View; 30import android.view.ViewGroup; 31import android.widget.LinearLayout; 32import android.widget.TextView; 33 34/** 35 * @hide 36 */ 37public class ActionBarContextView extends AbsActionBarView { 38 39 private static final String TAG = "ActionBarContextView"; 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 Drawable mSplitBackground; 52 private boolean mTitleOptional; 53 54 public ActionBarContextView(Context context) { 55 this(context, null); 56 } 57 58 public ActionBarContextView(Context context, AttributeSet attrs) { 59 this(context, attrs, R.attr.actionModeStyle); 60 } 61 62 public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) { 63 super(context, attrs, defStyle); 64 65 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionMode, defStyle, 0); 66 setBackgroundDrawable(a.getDrawable( 67 R.styleable.ActionMode_background)); 68 mTitleStyleRes = a.getResourceId( 69 R.styleable.ActionMode_titleTextStyle, 0); 70 mSubtitleStyleRes = a.getResourceId( 71 R.styleable.ActionMode_subtitleTextStyle, 0); 72 73 mContentHeight = a.getLayoutDimension( 74 R.styleable.ActionMode_height, 0); 75 76 mSplitBackground = a.getDrawable( 77 R.styleable.ActionMode_backgroundSplit); 78 79 a.recycle(); 80 } 81 82 @Override 83 public void onDetachedFromWindow() { 84 super.onDetachedFromWindow(); 85 if (mActionMenuPresenter != null) { 86 mActionMenuPresenter.hideOverflowMenu(); 87 mActionMenuPresenter.hideSubMenus(); 88 } 89 } 90 91 @Override 92 public void setSplitActionBar(boolean split) { 93 if (mSplitActionBar != split) { 94 if (mActionMenuPresenter != null) { 95 // Mode is already active; move everything over and adjust the menu itself. 96 final ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams( 97 ViewGroup.LayoutParams.WRAP_CONTENT, 98 ViewGroup.LayoutParams.FILL_PARENT); 99 if (!split) { 100 mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); 101 mMenuView.setBackgroundDrawable(null); 102 final ViewGroup oldParent = (ViewGroup) mMenuView.getParent(); 103 if (oldParent != null) { 104 oldParent.removeView(mMenuView); 105 } 106 addView(mMenuView, layoutParams); 107 } else { 108 // Allow full screen width in split mode. 109 mActionMenuPresenter.setWidthLimit( 110 getContext().getResources().getDisplayMetrics().widthPixels, true); 111 // No limit to the item count; use whatever will fit. 112 mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE); 113 // Span the whole width 114 layoutParams.width = ViewGroup.LayoutParams.FILL_PARENT; 115 layoutParams.height = mContentHeight; 116 mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); 117 mMenuView.setBackgroundDrawable(mSplitBackground); 118 final ViewGroup oldParent = (ViewGroup) mMenuView.getParent(); 119 if (oldParent != null) { 120 oldParent.removeView(mMenuView); 121 } 122 mSplitView.addView(mMenuView, layoutParams); 123 } 124 } 125 super.setSplitActionBar(split); 126 } 127 } 128 129 public void setContentHeight(int height) { 130 mContentHeight = height; 131 } 132 133 public void setCustomView(View view) { 134 if (mCustomView != null) { 135 removeView(mCustomView); 136 } 137 mCustomView = view; 138 if (mTitleLayout != null) { 139 removeView(mTitleLayout); 140 mTitleLayout = null; 141 } 142 if (view != null) { 143 addView(view); 144 } 145 requestLayout(); 146 } 147 148 public void setTitle(CharSequence title) { 149 mTitle = title; 150 initTitle(); 151 } 152 153 public void setSubtitle(CharSequence subtitle) { 154 mSubtitle = subtitle; 155 initTitle(); 156 } 157 158 public CharSequence getTitle() { 159 return mTitle; 160 } 161 162 public CharSequence getSubtitle() { 163 return mSubtitle; 164 } 165 166 private void initTitle() { 167 if (mTitleLayout == null) { 168 LayoutInflater inflater = LayoutInflater.from(getContext()); 169 inflater.inflate(R.layout.abc_action_bar_title_item, this); 170 mTitleLayout = (LinearLayout) getChildAt(getChildCount() - 1); 171 mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title); 172 mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle); 173 if (mTitleStyleRes != 0) { 174 mTitleView.setTextAppearance(getContext(), mTitleStyleRes); 175 } 176 if (mSubtitleStyleRes != 0) { 177 mSubtitleView.setTextAppearance(getContext(), mSubtitleStyleRes); 178 } 179 } 180 181 mTitleView.setText(mTitle); 182 mSubtitleView.setText(mSubtitle); 183 184 final boolean hasTitle = !TextUtils.isEmpty(mTitle); 185 final boolean hasSubtitle = !TextUtils.isEmpty(mSubtitle); 186 mSubtitleView.setVisibility(hasSubtitle ? VISIBLE : GONE); 187 mTitleLayout.setVisibility(hasTitle || hasSubtitle ? VISIBLE : GONE); 188 if (mTitleLayout.getParent() == null) { 189 addView(mTitleLayout); 190 } 191 } 192 193 public void initForMode(final ActionMode mode) { 194 if (mClose == null) { 195 LayoutInflater inflater = LayoutInflater.from(getContext()); 196 mClose = inflater.inflate(R.layout.abc_action_mode_close_item, this, false); 197 addView(mClose); 198 } else if (mClose.getParent() == null) { 199 addView(mClose); 200 } 201 202 View closeButton = mClose.findViewById(R.id.action_mode_close_button); 203 closeButton.setOnClickListener(new View.OnClickListener() { 204 public void onClick(View v) { 205 mode.finish(); 206 } 207 }); 208 209 final MenuBuilder menu = (MenuBuilder) mode.getMenu(); 210 if (mActionMenuPresenter != null) { 211 mActionMenuPresenter.dismissPopupMenus(); 212 } 213 mActionMenuPresenter = new ActionMenuPresenter(getContext()); 214 mActionMenuPresenter.setReserveOverflow(true); 215 216 final ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams( 217 ViewGroup.LayoutParams.WRAP_CONTENT, 218 ViewGroup.LayoutParams.FILL_PARENT); 219 if (!mSplitActionBar) { 220 menu.addMenuPresenter(mActionMenuPresenter); 221 mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); 222 mMenuView.setBackgroundDrawable(null); 223 addView(mMenuView, layoutParams); 224 } else { 225 // Allow full screen width in split mode. 226 mActionMenuPresenter.setWidthLimit( 227 getContext().getResources().getDisplayMetrics().widthPixels, true); 228 // No limit to the item count; use whatever will fit. 229 mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE); 230 // Span the whole width 231 layoutParams.width = ViewGroup.LayoutParams.FILL_PARENT; 232 layoutParams.height = mContentHeight; 233 menu.addMenuPresenter(mActionMenuPresenter); 234 mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); 235 mMenuView.setBackgroundDrawable(mSplitBackground); 236 mSplitView.addView(mMenuView, layoutParams); 237 } 238 } 239 240 public void closeMode() { 241 if (mClose == null) { 242 killMode(); 243 return; 244 } 245 } 246 247 public void killMode() { 248 removeAllViews(); 249 if (mSplitView != null) { 250 mSplitView.removeView(mMenuView); 251 } 252 mCustomView = null; 253 mMenuView = null; 254 } 255 256 @Override 257 public boolean showOverflowMenu() { 258 if (mActionMenuPresenter != null) { 259 return mActionMenuPresenter.showOverflowMenu(); 260 } 261 return false; 262 } 263 264 @Override 265 public boolean hideOverflowMenu() { 266 if (mActionMenuPresenter != null) { 267 return mActionMenuPresenter.hideOverflowMenu(); 268 } 269 return false; 270 } 271 272 @Override 273 public boolean isOverflowMenuShowing() { 274 if (mActionMenuPresenter != null) { 275 return mActionMenuPresenter.isOverflowMenuShowing(); 276 } 277 return false; 278 } 279 280 @Override 281 protected ViewGroup.LayoutParams generateDefaultLayoutParams() { 282 // Used by custom views if they don't supply layout params. Everything else 283 // added to an ActionBarContextView should have them already. 284 return new ViewGroup.MarginLayoutParams( 285 ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); 286 } 287 288 @Override 289 public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { 290 return new ViewGroup.MarginLayoutParams(getContext(), attrs); 291 } 292 293 @Override 294 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 295 final int widthMode = View.MeasureSpec.getMode(widthMeasureSpec); 296 if (widthMode != View.MeasureSpec.EXACTLY) { 297 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 298 "with android:layout_width=\"FILL_PARENT\" (or fill_parent)"); 299 } 300 301 final int heightMode = View.MeasureSpec.getMode(heightMeasureSpec); 302 if (heightMode == View.MeasureSpec.UNSPECIFIED) { 303 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 304 "with android:layout_height=\"wrap_content\""); 305 } 306 307 final int contentWidth = View.MeasureSpec.getSize(widthMeasureSpec); 308 309 int maxHeight = mContentHeight > 0 ? 310 mContentHeight : View.MeasureSpec.getSize(heightMeasureSpec); 311 312 final int verticalPadding = getPaddingTop() + getPaddingBottom(); 313 int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight(); 314 final int height = maxHeight - verticalPadding; 315 final int childSpecHeight = View.MeasureSpec 316 .makeMeasureSpec(height, View.MeasureSpec.AT_MOST); 317 318 if (mClose != null) { 319 availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0); 320 ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mClose 321 .getLayoutParams(); 322 availableWidth -= lp.leftMargin + lp.rightMargin; 323 } 324 325 if (mMenuView != null && mMenuView.getParent() == this) { 326 availableWidth = measureChildView(mMenuView, availableWidth, 327 childSpecHeight, 0); 328 } 329 330 if (mTitleLayout != null && mCustomView == null) { 331 if (mTitleOptional) { 332 final int titleWidthSpec = View.MeasureSpec 333 .makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); 334 mTitleLayout.measure(titleWidthSpec, childSpecHeight); 335 final int titleWidth = mTitleLayout.getMeasuredWidth(); 336 final boolean titleFits = titleWidth <= availableWidth; 337 if (titleFits) { 338 availableWidth -= titleWidth; 339 } 340 mTitleLayout.setVisibility(titleFits ? VISIBLE : GONE); 341 } else { 342 availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0); 343 } 344 } 345 346 if (mCustomView != null) { 347 ViewGroup.LayoutParams lp = mCustomView.getLayoutParams(); 348 final int customWidthMode = lp.width != ViewGroup.LayoutParams.WRAP_CONTENT ? 349 View.MeasureSpec.EXACTLY : View.MeasureSpec.AT_MOST; 350 final int customWidth = lp.width >= 0 ? 351 Math.min(lp.width, availableWidth) : availableWidth; 352 final int customHeightMode = lp.height != ViewGroup.LayoutParams.WRAP_CONTENT ? 353 View.MeasureSpec.EXACTLY : View.MeasureSpec.AT_MOST; 354 final int customHeight = lp.height >= 0 ? 355 Math.min(lp.height, height) : height; 356 mCustomView.measure(View.MeasureSpec.makeMeasureSpec(customWidth, customWidthMode), 357 View.MeasureSpec.makeMeasureSpec(customHeight, customHeightMode)); 358 } 359 360 if (mContentHeight <= 0) { 361 int measuredHeight = 0; 362 final int count = getChildCount(); 363 for (int i = 0; i < count; i++) { 364 View v = getChildAt(i); 365 int paddedViewHeight = v.getMeasuredHeight() + verticalPadding; 366 if (paddedViewHeight > measuredHeight) { 367 measuredHeight = paddedViewHeight; 368 } 369 } 370 setMeasuredDimension(contentWidth, measuredHeight); 371 } else { 372 setMeasuredDimension(contentWidth, maxHeight); 373 } 374 } 375 376 @Override 377 protected void onLayout(boolean changed, int l, int t, int r, int b) { 378 int x = getPaddingLeft(); 379 final int y = getPaddingTop(); 380 final int contentHeight = b - t - getPaddingTop() - getPaddingBottom(); 381 382 if (mClose != null && mClose.getVisibility() != GONE) { 383 ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mClose 384 .getLayoutParams(); 385 x += lp.leftMargin; 386 x += positionChild(mClose, x, y, contentHeight); 387 x += lp.rightMargin; 388 389 } 390 391 if (mTitleLayout != null && mCustomView == null && mTitleLayout.getVisibility() != GONE) { 392 x += positionChild(mTitleLayout, x, y, contentHeight); 393 } 394 395 if (mCustomView != null) { 396 x += positionChild(mCustomView, x, y, contentHeight); 397 } 398 399 x = r - l - getPaddingRight(); 400 401 if (mMenuView != null) { 402 x -= positionChildInverse(mMenuView, x, y, contentHeight); 403 } 404 } 405 406 public void setTitleOptional(boolean titleOptional) { 407 if (titleOptional != mTitleOptional) { 408 requestLayout(); 409 } 410 mTitleOptional = titleOptional; 411 } 412 413 public boolean isTitleOptional() { 414 return mTitleOptional; 415 } 416} 417