/* * Copyright (C) 2012 www.amsoft.cn * * 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 com.ab.view.app; import java.util.HashMap; import java.util.Map; import android.content.Context; import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.View.OnTouchListener; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.widget.ImageView; import android.widget.RelativeLayout; // TODO: Auto-generated Javadoc /** * © 2012-2015 amsoft.cn * 名称:AbPopoverView.java * 描述:提示框 * * @author zhaoqp * @version v1.0 * @date:2013-11-18 下午5:02:16 */ public class AbPopoverView extends RelativeLayout implements OnTouchListener{ /** * AbPopoverView的监听器. * * @see PopoverViewEvent */ public static interface PopoverViewListener{ /** * Called when the popover is going to show. * * @param view The whole popover view */ void popoverViewWillShow(AbPopoverView view); /** * Called when the popover did show. * * @param view The whole popover view */ void popoverViewDidShow(AbPopoverView view); /** * Called when the popover is going to be dismissed. * * @param view The whole popover view */ void popoverViewWillDismiss(AbPopoverView view); /** * Called when the popover was dismissed. * * @param view The whole popover view */ void popoverViewDidDismiss(AbPopoverView view); } /** * Popover arrow points up. Integer to use with bit operators to tell the popover where the arrow should appear and from where the popover should appear */ public final static int PopoverArrowDirectionUp = 0x00000001; /** * Popover arrow points down. Integer to use with bit operators to tell the popover where the arrow should appear and from where the popover should appear */ public final static int PopoverArrowDirectionDown = 0x00000002; /** * Popover arrow points left. Integer to use with bit operators to tell the popover where the arrow should appear and from where the popover should appear */ public final static int PopoverArrowDirectionLeft = 0x00000004; /** * Popover arrow points right. Integer to use with bit operators to tell the popover where the arrow should appear and from where the popover should appear */ public final static int PopoverArrowDirectionRight = 0x00000008; /** * Popover arrow points any direction. Integer to use with bit operators to tell the popover where the arrow should appear and from where the popover should appear */ public final static int PopoverArrowDirectionAny = PopoverArrowDirectionUp|PopoverArrowDirectionDown|PopoverArrowDirectionLeft|PopoverArrowDirectionRight; /** The delegate of the view. */ private PopoverViewListener popoverViewListener; /** The main popover containing the view we want to show. */ private RelativeLayout popoverView; /** * The view group storing this popover. We need this so, when we dismiss the popover, we remove it from the view group */ private ViewGroup superview; /** The content size for the view in the popover. */ private Point contentSizeForViewInPopover = new Point(0, 0); /** The real content size we will use (it considers the padding). */ private Point realContentSize = new Point(0, 0); /** A hash containing. */ private Map<Integer, Rect> possibleRects; /** Whether the view is animating or not. */ private boolean isAnimating = false; /** The fade animation time in milliseconds. */ private int fadeAnimationTime = 300; /** The layout Rect, is the same as the superview rect. */ private Rect popoverLayoutRect; /** The popover background drawable. */ private Drawable backgroundDrawable; /** The popover arrow up drawable. */ private Drawable arrowUpDrawable; /** The popover arrow down drawable. */ private Drawable arrowDownDrawable; /** The popover arrow left drawable. */ private Drawable arrowLeftDrawable; /** The popover arrow down drawable. */ private Drawable arrowRightDrawable; /** 当前显示的箭头图标. */ private ImageView arrowImageView = null; /** 当前显示的提示的View. */ private View popoverContentView = null; /** * Constructor to create a popover with a popover view. * * @param context The context where we should create the popover view */ public AbPopoverView(Context context) { super(context); initPopoverView(); } /** * Constructor to create a popover with a popover view. * * @param context The context where we should create the popover view * @param attrs Attribute set to init the view */ public AbPopoverView(Context context, AttributeSet attrs) { super(context, attrs); initPopoverView(); } /** * Constructor to create a popover with a popover view. * * @param context The context where we should create the popover view * @param attrs Attribute set to init the view * @param defStyle The default style for this view */ public AbPopoverView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initPopoverView(); } /** * Init the popover view. */ private void initPopoverView(){ //Init the relative layout popoverView = new RelativeLayout(getContext()); setBackgroundColor(Color.WHITE); setOnTouchListener(this); } /** * Get the Rect frame for a view (relative to the Window of the application). * * @param v The view to get the rect from * @return The rect of the view, relative to the application window */ public static Rect getFrameForView(View v){ int location [] = new int [2]; v.getLocationOnScreen(location); Rect viewRect = new Rect(location[0], location[1], location[0]+v.getWidth(), location[1]+v.getHeight()); return viewRect; } /** * Add the popover to the view with a defined rect inside the popover. * * @param insertRect The rect we want to insert the view */ private void addPopoverInRect(Rect insertRect){ //Set layout params LayoutParams insertParams = new LayoutParams(insertRect.width(), insertRect.height()); insertParams.leftMargin = insertRect.left; insertParams.topMargin = insertRect.top; //Add the view addView(popoverView, insertParams); } /** * Inits the arrow. * * @param originRect the origin rect * @param arrowDirection the arrow direction */ private void initArrow(Rect originRect, Integer arrowDirection){ //重新定位 if(arrowImageView != null){ removeView(arrowImageView); } //Add arrow drawable arrowImageView = new ImageView(getContext()); Drawable arrowDrawable = null; int xPos = 0; int arrowWidth = 0; int yPos = 0; int arrowHeight = 0; //Get correct drawable, and get Width, Height, Xpos and yPos depending on the selected arrow direction if (arrowDirection == AbPopoverView.PopoverArrowDirectionUp){ arrowDrawable = arrowUpDrawable; arrowWidth = arrowDrawable.getIntrinsicWidth(); arrowHeight = arrowDrawable.getIntrinsicHeight(); xPos = originRect.centerX() - (arrowWidth/2) - popoverLayoutRect.left; yPos = originRect.bottom - popoverLayoutRect.top; } else if (arrowDirection == AbPopoverView.PopoverArrowDirectionDown){ arrowDrawable = arrowDownDrawable; arrowWidth = arrowDrawable.getIntrinsicWidth(); arrowHeight = arrowDrawable.getIntrinsicHeight(); xPos = originRect.centerX() - (arrowWidth/2) - popoverLayoutRect.left; yPos = originRect.top - arrowHeight - popoverLayoutRect.top; } else if (arrowDirection == AbPopoverView.PopoverArrowDirectionLeft){ arrowDrawable = arrowLeftDrawable; arrowWidth = arrowDrawable.getIntrinsicWidth(); arrowHeight = arrowDrawable.getIntrinsicHeight(); xPos = originRect.right - popoverLayoutRect.left; yPos = originRect.centerY() - (arrowHeight/2) - popoverLayoutRect.top; } else if (arrowDirection == AbPopoverView.PopoverArrowDirectionRight){ arrowDrawable = arrowRightDrawable; arrowWidth = arrowDrawable.getIntrinsicWidth(); arrowHeight = arrowDrawable.getIntrinsicHeight(); xPos = originRect.left - arrowWidth - popoverLayoutRect.left; yPos = originRect.centerY() - (arrowHeight/2) - popoverLayoutRect.top; } //Set drawable arrowImageView.setImageDrawable(arrowDrawable); //Init layout params LayoutParams arrowParams = new LayoutParams(arrowWidth, arrowHeight); arrowParams.leftMargin = xPos; arrowParams.topMargin = yPos; //add view addView(arrowImageView, arrowParams); } /** * Calculates the rect for showing the view with Arrow Up. * * @param originRect The origin rect * @return The calculated rect to show the view */ private Rect getRectForArrowUp(Rect originRect){ //Get available space int xAvailable = popoverLayoutRect.width(); if (xAvailable < 0) xAvailable = 0; int yAvailable = popoverLayoutRect.height() - (originRect.bottom - popoverLayoutRect.top); if (yAvailable < 0) yAvailable = 0; //Get final width and height int finalX = xAvailable; if ((realContentSize.x > 0) && (realContentSize.x < finalX)) finalX = realContentSize.x; int finalY = yAvailable; if ((realContentSize.y > 0) && (realContentSize.y < finalY)) finalY = realContentSize.y; //Get final origin X and Y int originX = (originRect.centerX()-popoverLayoutRect.left) - (finalX/2) ; if (originX < 0) originX = 0; else if (originX+finalX > popoverLayoutRect.width()) originX = popoverLayoutRect.width() - finalX; int originY = (originRect.bottom - popoverLayoutRect.top); //Create rect Rect finalRect = new Rect(originX, originY, originX+finalX, originY+finalY); //And return return finalRect; } /** * Calculates the rect for showing the view with Arrow Down. * * @param originRect The origin rect * @return The calculated rect to show the view */ private Rect getRectForArrowDown(Rect originRect){ //Get available space int xAvailable = popoverLayoutRect.width(); if (xAvailable < 0) xAvailable = 0; int yAvailable = (originRect.top - popoverLayoutRect.top); if (yAvailable < 0) yAvailable = 0; //Get final width and height int finalX = xAvailable; if ((realContentSize.x > 0) && (realContentSize.x < finalX)) finalX = realContentSize.x; int finalY = yAvailable; if ((realContentSize.y > 0) && (realContentSize.y < finalY)) finalY = realContentSize.y; //Get final origin X and Y int originX = (originRect.centerX()-popoverLayoutRect.left) - (finalX/2) ; if (originX < 0) originX = 0; else if (originX+finalX > popoverLayoutRect.width()) originX = popoverLayoutRect.width() - finalX; int originY = (originRect.top - popoverLayoutRect.top) - finalY; //Create rect Rect finalRect = new Rect(originX, originY, originX+finalX, originY+finalY); //And return return finalRect; } /** * Calculates the rect for showing the view with Arrow Right. * * @param originRect The origin rect * @return The calculated rect to show the view */ private Rect getRectForArrowRight(Rect originRect){ //Get available space int xAvailable = (originRect.left - popoverLayoutRect.left); if (xAvailable < 0) xAvailable = 0; int yAvailable = popoverLayoutRect.height(); if (yAvailable < 0) yAvailable = 0; //Get final width and height int finalX = xAvailable; if ((realContentSize.x > 0) && (realContentSize.x < finalX)) finalX = realContentSize.x; int finalY = yAvailable; if ((realContentSize.y > 0) && (realContentSize.y < finalY)) finalY = realContentSize.y; //Get final origin X and Y int originX = (originRect.left - popoverLayoutRect.left) - finalX; int originY = (originRect.centerY()-popoverLayoutRect.top) - (finalY/2) ; if (originY < 0) originY = 0; else if (originY+finalY > popoverLayoutRect.height()) originY = popoverLayoutRect.height() - finalY; //Create rect Rect finalRect = new Rect(originX, originY, originX+finalX, originY+finalY); //And return return finalRect; } /** * Calculates the rect for showing the view with Arrow Left. * * @param originRect The origin rect * @return The calculated rect to show the view */ private Rect getRectForArrowLeft(Rect originRect){ //Get available space int xAvailable = popoverLayoutRect.width() - (originRect.right - popoverLayoutRect.left); if (xAvailable < 0) xAvailable = 0; int yAvailable = popoverLayoutRect.height(); if (yAvailable < 0) yAvailable = 0; //Get final width and height int finalX = xAvailable; if ((realContentSize.x > 0) && (realContentSize.x < finalX)) finalX = realContentSize.x; int finalY = yAvailable; if ((realContentSize.y > 0) && (realContentSize.y < finalY)) finalY = realContentSize.y; //Get final origin X and Y int originX = (originRect.right - popoverLayoutRect.left); int originY = (originRect.centerY()-popoverLayoutRect.top) - (finalY/2) ; if (originY < 0) originY = 0; else if (originY+finalY > popoverLayoutRect.height()) originY = popoverLayoutRect.height() - finalY; //Create rect Rect finalRect = new Rect(originX, originY, originX+finalX, originY+finalY); //And return return finalRect; } /** * Add available rects for each selected arrow direction. * * @param originRect The rect where the popover will appear from * @param arrowDirections The bit mask for the possible arrow directions */ private void addAvailableRects(Rect originRect, int arrowDirections){ //Get popover rects for the available directions possibleRects = new HashMap<Integer, Rect>(); if ((arrowDirections & AbPopoverView.PopoverArrowDirectionUp) != 0){ possibleRects.put(AbPopoverView.PopoverArrowDirectionUp, getRectForArrowUp(originRect)); } if ((arrowDirections & AbPopoverView.PopoverArrowDirectionDown) != 0){ possibleRects.put(AbPopoverView.PopoverArrowDirectionDown, getRectForArrowDown(originRect)); } if ((arrowDirections & AbPopoverView.PopoverArrowDirectionRight) != 0){ possibleRects.put(AbPopoverView.PopoverArrowDirectionRight, getRectForArrowRight(originRect)); } if ((arrowDirections & AbPopoverView.PopoverArrowDirectionLeft) != 0){ possibleRects.put(AbPopoverView.PopoverArrowDirectionLeft, getRectForArrowLeft(originRect)); } } /** * Get the best available rect (bigger area). * * @return The Integer key to get the Rect from posibleRects (PopoverArrowDirectionUp,PopoverArrowDirectionDown,PopoverArrowDirectionRight or PopoverArrowDirectionLeft) */ private Integer getBestRect(){ //Get the best one (bigger area) Integer best = null; for (Integer arrowDir : possibleRects.keySet()) { if (best == null){ best = arrowDir; } else{ Rect bestRect = possibleRects.get(best); Rect checkRect = possibleRects.get(arrowDir); if ((bestRect.width()*bestRect.height()) < (checkRect.width()*checkRect.height())) best = arrowDir; } } return best; } /** * Gets the current fade animation time. * * @return The fade animation time, in milliseconds */ public int getFadeAnimationTime() { return fadeAnimationTime; } /** * Sets the fade animation time. * * @param fadeAnimationTime The time in milliseconds */ public void setFadeAnimationTime(int fadeAnimationTime) { this.fadeAnimationTime = fadeAnimationTime; } /** * Get the content size for view in popover. * * @return The point with the content size */ public Point getContentSizeForViewInPopover() { return contentSizeForViewInPopover; } /** * Sets the content size for the view in a popover, if point is (0,0) the popover will full the screen. * * @param contentSizeForViewInPopover the new content size for view in popover */ public void setContentSizeForViewInPopover(Point contentSizeForViewInPopover) { this.contentSizeForViewInPopover = contentSizeForViewInPopover; //Save the real content size realContentSize = new Point(contentSizeForViewInPopover); realContentSize.x += popoverView.getPaddingLeft()+popoverView.getPaddingRight(); realContentSize.y += popoverView.getPaddingTop()+popoverView.getPaddingBottom(); } /** * Gets the popover view listener. * * @return the popover view listener */ public PopoverViewListener getPopoverViewListener() { return popoverViewListener; } /** * Sets the popover view listener. * * @param popoverViewListener the new popover view listener */ public void setPopoverViewListener(PopoverViewListener popoverViewListener) { this.popoverViewListener = popoverViewListener; } /** * This method shows a popover in a ViewGroup, from an origin rect (relative to the Application Window). * * @param group The group we want to insert the popup. Normally a Relative Layout so it can stand on top of everything * @param originRect The rect we want the popup to appear from (relative to the Application Window!) * @param arrowDirections The mask of bits to tell in which directions we want the popover to be shown * @param animated Whether is animated, or not */ public void showPopoverFromRectInViewGroup(ViewGroup group, Rect originRect, int arrowDirections, boolean animated){ //First, tell delegate we will show if (popoverViewListener != null) popoverViewListener.popoverViewWillShow(this); //Save superview superview = group; //First, add the view to the view group. The popover will cover the whole area android.view.ViewGroup.LayoutParams insertParams = new android.view.ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams.FILL_PARENT, android.view.ViewGroup.LayoutParams.FILL_PARENT); group.addView(this, insertParams); //Now, save rect for the layout (is the same as the superview) popoverLayoutRect = AbPopoverView.getFrameForView(superview); //Add available rects addAvailableRects(originRect, arrowDirections); //Get best rect Integer best = getBestRect(); //Add popover Rect bestRect = possibleRects.get(best); addPopoverInRect(bestRect); //箭头图标 initArrow(originRect, best); //If we don't want animation, just tell the delegate if (!animated){ //Tell delegate we did show if (popoverViewListener != null) popoverViewListener.popoverViewDidShow(this); } //If we want animation, animate it! else{ //Continue only if we are not animating if (!isAnimating){ //Create alpha animation, with its listener AlphaAnimation animation = new AlphaAnimation(0.0f, 1.0f); animation.setDuration(fadeAnimationTime); animation.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) { //Nothing to do here } @Override public void onAnimationRepeat(Animation animation) { //Nothing to do here } @Override public void onAnimationEnd(Animation animation) { //End animation isAnimating = false; //Tell delegate we did show if (popoverViewListener != null) popoverViewListener.popoverViewDidShow(AbPopoverView.this); } }); //Start animation isAnimating = true; startAnimation(animation); } } } /** * Dismiss the current shown popover. * * @param animated Whether it should be dismissed animated or not */ public void dissmissPopover(boolean animated){ //Tell delegate we will dismiss if (popoverViewListener != null) popoverViewListener.popoverViewWillDismiss(AbPopoverView.this); //If we don't want animation if (!animated){ //Just remove views popoverView.removeAllViews(); removeAllViews(); superview.removeView(this); //Tell delegate we did dismiss if (popoverViewListener != null) popoverViewListener.popoverViewDidDismiss(AbPopoverView.this); } else{ //Continue only if there is not an animation in progress if (!isAnimating){ //Create alpha animation, with its listener AlphaAnimation animation = new AlphaAnimation(1.0f, 0.0f); animation.setDuration(fadeAnimationTime); animation.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) { //Nothing to do here } @Override public void onAnimationRepeat(Animation animation) { //Nothing to do here } @Override public void onAnimationEnd(Animation animation) { //Remove the view popoverView.removeAllViews(); removeAllViews(); AbPopoverView.this.superview.removeView(AbPopoverView.this); //End animation isAnimating = false; //Tell delegate we did dismiss if (popoverViewListener != null) popoverViewListener.popoverViewDidDismiss(AbPopoverView.this); } }); //Start animation isAnimating = true; startAnimation(animation); } } } /* (non-Javadoc) * @see android.view.View.OnTouchListener#onTouch(android.view.View, android.view.MotionEvent) */ @Override public boolean onTouch(View v, MotionEvent event) { //If we touched over the background popover view (this) if ((!isAnimating) && (v == this)){ dissmissPopover(true); } return true; } /** * Gets the background drawable. * * @return the background drawable */ public Drawable getBackgroundDrawable() { return backgroundDrawable; } /* (non-Javadoc) * @see android.view.View#setBackgroundDrawable(android.graphics.drawable.Drawable) */ public void setBackgroundDrawable(Drawable backgroundDrawable) { this.backgroundDrawable = backgroundDrawable; popoverView.setBackgroundDrawable(backgroundDrawable); } /** * Gets the arrow up drawable. * * @return the arrow up drawable */ public Drawable getArrowUpDrawable() { return arrowUpDrawable; } /** * Sets the arrow up drawable. * * @param arrowUpDrawable the new arrow up drawable */ public void setArrowUpDrawable(Drawable arrowUpDrawable) { this.arrowUpDrawable = arrowUpDrawable; } /** * Gets the arrow down drawable. * * @return the arrow down drawable */ public Drawable getArrowDownDrawable() { return arrowDownDrawable; } /** * Sets the arrow down drawable. * * @param arrowDownDrawable the new arrow down drawable */ public void setArrowDownDrawable(Drawable arrowDownDrawable) { this.arrowDownDrawable = arrowDownDrawable; } /** * Gets the arrow left drawable. * * @return the arrow left drawable */ public Drawable getArrowLeftDrawable() { return arrowLeftDrawable; } /** * Sets the arrow left drawable. * * @param arrowLeftDrawable the new arrow left drawable */ public void setArrowLeftDrawable(Drawable arrowLeftDrawable) { this.arrowLeftDrawable = arrowLeftDrawable; } /** * Gets the arrow right drawable. * * @return the arrow right drawable */ public Drawable getArrowRightDrawable() { return arrowRightDrawable; } /** * Sets the arrow right drawable. * * @param arrowRightDrawable the new arrow right drawable */ public void setArrowRightDrawable(Drawable arrowRightDrawable) { this.arrowRightDrawable = arrowRightDrawable; } /** * Gets the popover content view. * * @return the popover content view */ public View getPopoverContentView() { return popoverContentView; } /** * 描述:设置显示的View. * * @param popoverContentView the new popover content view */ public void setPopoverContentView(View popoverContentView) { this.popoverContentView = popoverContentView; popoverView.removeAllViews(); popoverView.addView(popoverContentView,LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); } }