/* * ****************************************************************************** * Copyright (c) 2013-2014 Gabriele Mariotti. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ***************************************************************************** */ package it.gmariotti.cardslib.library.view.component; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.PopupMenu; import it.gmariotti.cardslib.library.R; import it.gmariotti.cardslib.library.internal.CardHeader; import it.gmariotti.cardslib.library.view.base.CardViewInterface; import it.gmariotti.cardslib.library.view.helper.CardViewHelper; import it.gmariotti.cardslib.library.view.helper.CardViewHelperUtil; /** * Compound View for Header Component. * It is built with base_header_layout xml file. * </p> * The base layout has two elements: * <ul> * <li><i>card_header_inner_frame</i>: frame which contains custom content.</li> * <li><i>card_header_button_frame</i>: frame which contains button.</li> * </ul> * </p> * You can populate and customize this layout using your CardHeader class. * See {@link CardHeader} for more info. * </p> * <b>You can use a custom layout for Header Component in your xml layout.</b> * <pre> * <it.gmariotti.cardslib.library.view.component.CardHeaderView * android:id="@+id/card_header_layout" * android:layout_width="match_parent" * android:layout_height="wrap_content" * card:card_header_layout_resourceID="@layout/my_header_layout" /> * </pre> * </p> * * @author Gabriele Mariotti (gabri.mariotti@gmail.com) */ public class CardHeaderView extends FrameLayout implements CardViewInterface { //-------------------------------------------------------------------------- // Custom Layout //-------------------------------------------------------------------------- /** * Header Layout */ protected int card_header_layout_resourceID = R.layout.base_header_layout; //-------------------------------------------------------------------------- // View //-------------------------------------------------------------------------- /** * Compound View for this Component */ protected View mInternalOuterView; /** * Inner View. */ protected View mInternalInnerView; //-------------------------------------------------------------------------- // Frame //-------------------------------------------------------------------------- /** * InnerContent Frame */ protected ViewGroup mFrameInner; /** * Button Frame */ protected ViewGroup mFrameButton; //-------------------------------------------------------------------------- // Button //-------------------------------------------------------------------------- /** * Overflow */ protected ImageButton mImageButtonOverflow; /** * Expand/Collapse button */ protected ImageButton mImageButtonExpand; /** * Other Button */ protected ImageButton mImageButtonOther; /** * Card Model * */ protected CardHeader mCardHeader; /** * Listener invoked when expand/collapse button is clicked */ //protected OnClickListener mOnClickExpandCollapseActionListener; /** * Used to recycle ui elements. */ protected boolean mIsRecycle = false; /** * Used to replace inner layout elements. */ protected boolean mForceReplaceInnerLayout = false; /** * Popup Menu for overflow button */ protected PopupMenu mPopupMenu; protected CardViewHelper mHelperImpl; //-------------------------------------------------------------------------- // Constructors //-------------------------------------------------------------------------- public CardHeaderView(Context context) { this(context, null, 0); } public CardHeaderView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CardHeaderView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(attrs, defStyle); mHelperImpl = CardViewHelperUtil.getInstance(context); } //-------------------------------------------------------------------------- // Init //-------------------------------------------------------------------------- /** * Initializes component * * @param attrs * @param defStyle */ protected void init(AttributeSet attrs, int defStyle) { //Init attrs initAttrs(attrs, defStyle); //Init View if (!isInEditMode()) initView(); } /** * Init custom attrs. * * @param attrs * @param defStyle */ protected void initAttrs(AttributeSet attrs, int defStyle) { TypedArray a = getContext().getTheme().obtainStyledAttributes( attrs, R.styleable.card_options, defStyle, defStyle); try { card_header_layout_resourceID = a.getResourceId(R.styleable.card_options_card_header_layout_resourceID, card_header_layout_resourceID); } finally { a.recycle(); } } /** * Inits view */ protected void initView() { //Inflate the root view (outerView) LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); mInternalOuterView = inflater.inflate(card_header_layout_resourceID, this, true); //Get buttons from layout mImageButtonExpand = (ImageButton) findViewById(R.id.card_header_button_expand); mImageButtonOverflow = (ImageButton) findViewById(R.id.card_header_button_overflow); mImageButtonOther = (ImageButton) findViewById(R.id.card_header_button_other); //Get frames mFrameInner = (FrameLayout) findViewById(R.id.card_header_inner_frame); mFrameButton = (FrameLayout) findViewById(R.id.card_header_button_frame); } @Override public View getInternalOuterView() { return mInternalOuterView; } /** * Adds a {@link CardHeader}. * <b>It is important to set all header values before launch this method</b> * * @param cardHeader CardHeader model */ public void addCardHeader(CardHeader cardHeader) { //Set header mCardHeader = cardHeader; //build ui buildUI(); } /** * This method builds UI. * If you are using standard base layout it sets up buttons and innerView. * If you are using your custom layout it sets your elements. */ protected void buildUI() { if (mCardHeader == null) return; //Set button visibility setupButtons(); //Setup InnerView setupInnerView(); } /** * Sets Buttons visibility */ @SuppressWarnings("deprecation") @SuppressLint("NewApi") protected void setupButtons() { if (mCardHeader.isButtonOverflowVisible()) { visibilityButtonHelper(VISIBLE, GONE, GONE); addPopup(); if (mPopupMenu==null && mCardHeader.getCustomOverflowAnimation() != null) { addCustomOverflowAnimation(); } } else { if (mCardHeader.isButtonExpandVisible()) { visibilityButtonHelper(GONE, VISIBLE, GONE); } else { if (mCardHeader.isOtherButtonVisible() && mImageButtonOther != null) { visibilityButtonHelper(GONE, GONE, VISIBLE); //Check if button is not null if (mImageButtonOther != null) { if (mCardHeader.getOtherButtonDrawable() > 0) { mHelperImpl.setButtonBackground(mImageButtonOther, mCardHeader.getOtherButtonDrawable() ); } addOtherListener(); } } else { visibilityButtonHelper(GONE, GONE, GONE); } } } } /** * Add Custom Overflow Animation */ private void addCustomOverflowAnimation() { final CardHeader.CustomOverflowAnimation animation = mCardHeader.getCustomOverflowAnimation(); if (animation != null && mImageButtonOverflow != null) { //Add a PopupMenu and its listener mImageButtonOverflow.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { animation.doAnimation(mCardHeader.getParentCard(), v); } }); } else { if (mImageButtonOverflow != null) mImageButtonOverflow.setVisibility(GONE); } } /** * Sets listener for OtherButtonAction */ protected void addOtherListener() { if (mCardHeader.getOtherButtonClickListener() != null) { if (mImageButtonOther != null) { mImageButtonOther.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mCardHeader.getOtherButtonClickListener() != null) mCardHeader.getOtherButtonClickListener().onButtonItemClick(mCardHeader.getParentCard(), v); } }); } } else { if (mImageButtonOther != null) { mImageButtonOther.setClickable(false); } } } /** * Sets the inner view. */ protected void setupInnerView() { if (mFrameInner != null) { //Check if view can be recycled //It can happen in a listView to improve performances or while refreshing a card if (!isRecycle() || isForceReplaceInnerLayout()) { if (isForceReplaceInnerLayout() && mFrameInner != null && mInternalInnerView != null) mFrameInner.removeView(mInternalInnerView); //Inflate inner view mInternalInnerView = mCardHeader.getInnerView(getContext(), mFrameInner); } else { //View can be recycled. //Only setup Inner Elements if (mCardHeader.getInnerLayout() > -1) mCardHeader.setupInnerViewElements(mFrameInner, mInternalInnerView); } } } /** * Helper method to setup buttons visibility * * @param overflowButtonVisibility <code>VISIBLE</code> to make visibile this button , otherwise <code>GONE</code> * @param expandButtonVisibility <code>VISIBLE</code> to make visibile this button , otherwise <code>GONE</code> * @param otherButtonVisibility <code>VISIBLE</code> to make visibile this button , otherwise <code>GONE</code> */ protected void visibilityButtonHelper(int overflowButtonVisibility, int expandButtonVisibility, int otherButtonVisibility) { if (overflowButtonVisibility == VISIBLE || overflowButtonVisibility == GONE) { if (mImageButtonOverflow != null) { mImageButtonOverflow.setVisibility(overflowButtonVisibility); } } if (expandButtonVisibility == VISIBLE || expandButtonVisibility == GONE) { if (mImageButtonExpand != null) { mImageButtonExpand.setVisibility(expandButtonVisibility); } } if (otherButtonVisibility == VISIBLE || otherButtonVisibility == GONE) { if (mImageButtonOther != null) { mImageButtonOther.setVisibility(otherButtonVisibility); } } } /** * Adds Popup menu */ protected void addPopup() { //To prevent recycle mPopupMenu = null; if (mImageButtonOverflow != null) { // allow dynamic customization on popup menu boolean prepareMenu = mCardHeader.getPopupMenu() > CardHeader.NO_POPUP_MENU ? true : false; if (mCardHeader.getPopupMenuPrepareListener() != null) { //Build the popupMenu mPopupMenu = _buildPopupMenu(); //Dynamic customization prepareMenu = mCardHeader.getPopupMenuPrepareListener().onPreparePopupMenu(mCardHeader.getParentCard(), mPopupMenu); //Check if the menu has visible items if (mPopupMenu.getMenu()==null || !mPopupMenu.getMenu().hasVisibleItems()) prepareMenu = false; } if (prepareMenu) { //Add a PopupMenu and its listener mImageButtonOverflow.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mPopupMenu==null){ //It is null if the PopupMenuPrepareListener is null //PopupMenu is built inside onClick() method to avoid building the menu when it is not necessary mPopupMenu = _buildPopupMenu(); } if (mPopupMenu!=null) { mPopupMenu.show(); mImageButtonOverflow.setSelected(true); } } }); } else { if (mCardHeader.getCustomOverflowAnimation()==null) { mImageButtonOverflow.setVisibility(GONE); } } } else { if (mImageButtonOverflow != null) mImageButtonOverflow.setVisibility(GONE); } } /** * Build the menu * @return */ private PopupMenu _buildPopupMenu(){ PopupMenu popup = new PopupMenu(getContext(), mImageButtonOverflow); if (mCardHeader.getPopupMenu()> CardHeader.NO_POPUP_MENU){ MenuInflater inflater = popup.getMenuInflater(); inflater.inflate(mCardHeader.getPopupMenu(), popup.getMenu()); } popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { if (mCardHeader.getPopupMenuListener() != null) { // This individual card has it unique menu mCardHeader.getPopupMenuListener().onMenuItemClick(mCardHeader.getParentCard(), item); } return false; } }); popup.setOnDismissListener(new PopupMenu.OnDismissListener() { @Override public void onDismiss(PopupMenu menu) { if (mImageButtonOverflow != null) mImageButtonOverflow.setSelected(false); } }); return popup; } //-------------------------------------------------------------------------- // Getters and Setters //-------------------------------------------------------------------------- /** * Returns Listener invoked when expand/collpse button is clicked * * @deprecated * @return listener */ /*public OnClickListener getOnClickExpandCollapseActionListener() { return mOnClickExpandCollapseActionListener; }*/ /** * Attaches Listener to expand/collapse button * * @deprecated * @param onClickExpandCollapseActionListener listener */ /*public void setOnClickExpandCollapseActionListener(OnClickListener onClickExpandCollapseActionListener) { this.mOnClickExpandCollapseActionListener = onClickExpandCollapseActionListener; }*/ /** * Indicates if view can recycle ui elements. * * @return <code>true</code> if views can recycle ui elements */ public boolean isRecycle() { return mIsRecycle; } /** * Sets if view can recycle ui elements * * @param isRecycle <code>true</code> to recycle */ public void setRecycle(boolean isRecycle) { this.mIsRecycle = isRecycle; } /** * Indicates if inner layout have to be replaced * * @return <code>true</code> if inner layout can be recycled */ public boolean isForceReplaceInnerLayout() { return mForceReplaceInnerLayout; } /** * Sets if inner layout have to be replaced * * @param forceReplaceInnerLayout <code>true</code> to recycle */ public void setForceReplaceInnerLayout(boolean forceReplaceInnerLayout) { this.mForceReplaceInnerLayout = forceReplaceInnerLayout; } /** * Returns the {@link ImageButton} used for overflow menu * * @return {@link ImageButton} */ public ImageButton getImageButtonOverflow() { return mImageButtonOverflow; } /** * Returns the {@link ImageButton} used for expand/collapse * * @return {@link ImageButton} */ public ImageButton getImageButtonExpand() { return mImageButtonExpand; } /* *Returns the {@link ImageButton} used for other Button * * @return {@link ImageButton} */ public ImageButton getImageButtonOther() { return mImageButtonOther; } /** * Returns the Frame which contains the buttons * * @return */ public ViewGroup getFrameButton() { return mFrameButton; } }