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