/* * Copyright 2013 Niek Haarman * 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.nhaarman.supertooltips; import java.util.ArrayList; import java.util.Collection; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.PorterDuff; import android.graphics.Rect; import android.os.Build; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewManager; import android.view.ViewTreeObserver; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import com.csq.commonlib.R; import com.nineoldandroids.animation.Animator; import com.nineoldandroids.animation.AnimatorListenerAdapter; import com.nineoldandroids.animation.AnimatorSet; import com.nineoldandroids.animation.ObjectAnimator; import com.nineoldandroids.view.ViewHelper; /** * A ViewGroup to visualize ToolTips. Use * ToolTipRelativeLayout.showToolTipForView() to show ToolTips. */ public class ToolTipView extends LinearLayout implements ViewTreeObserver.OnPreDrawListener, View.OnClickListener { public static final String TRANSLATION_Y_COMPAT = "translationY"; public static final String TRANSLATION_X_COMPAT = "translationX"; public static final String SCALE_X_COMPAT = "scaleX"; public static final String SCALE_Y_COMPAT = "scaleY"; public static final String ALPHA_COMPAT = "alpha"; private ImageView mTopPointerView; private View mTopFrame; private ViewGroup mContentHolder; private TextView mToolTipTV; private View mBottomFrame; private ImageView mBottomPointerView; private View mShadowView; private ToolTip mToolTip; private View mView; private boolean mDimensionsKnown; private int mRelativeMasterViewY; private int mRelativeMasterViewX; private int mWidth; private OnToolTipViewClickedListener mListener; public ToolTipView(final Context context) { super(context); init(); } private void init() { setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); setOrientation(VERTICAL); LayoutInflater.from(getContext()).inflate(R.layout.tooltip, this, true); mTopPointerView = (ImageView) findViewById(R.id.tooltip_pointer_up); mTopFrame = findViewById(R.id.tooltip_topframe); mContentHolder = (ViewGroup) findViewById(R.id.tooltip_contentholder); mToolTipTV = (TextView) findViewById(R.id.tooltip_contenttv); mBottomFrame = findViewById(R.id.tooltip_bottomframe); mBottomPointerView = (ImageView) findViewById(R.id.tooltip_pointer_down); mShadowView = findViewById(R.id.tooltip_shadow); setOnClickListener(this); getViewTreeObserver().addOnPreDrawListener(this); } @Override public boolean onPreDraw() { getViewTreeObserver().removeOnPreDrawListener(this); mDimensionsKnown = true; mWidth = mContentHolder.getWidth(); RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); layoutParams.width = mWidth; setLayoutParams(layoutParams); if (mToolTip != null) { applyToolTipPosition(); } return true; } public void setToolTip(final ToolTip toolTip, final View view) { mToolTip = toolTip; mView = view; if (mToolTip.getText() != null) { mToolTipTV.setText(mToolTip.getText()); } else if (mToolTip.getTextResId() != 0) { mToolTipTV.setText(mToolTip.getTextResId()); } if (mToolTip.getTypeface() != null) { mToolTipTV.setTypeface(mToolTip.getTypeface()); } if (mToolTip.getTextColor() != 0) { mToolTipTV.setTextColor(mToolTip.getTextColor()); } if (mToolTip.getColor() != 0) { setColor(mToolTip.getColor()); } if (mToolTip.getContentView() != null) { setContentView(mToolTip.getContentView()); } if (!mToolTip.shouldShowShadow()) { mShadowView.setVisibility(View.GONE); } if (mDimensionsKnown) { applyToolTipPosition(); } } private void applyToolTipPosition() { final int[] masterViewScreenPosition = new int[2]; mView.getLocationOnScreen(masterViewScreenPosition); final Rect viewDisplayFrame = new Rect(); mView.getWindowVisibleDisplayFrame(viewDisplayFrame); final int[] parentViewScreenPosition = new int[2]; ((View) getParent()).getLocationOnScreen(parentViewScreenPosition); final int masterViewWidth = mView.getWidth(); final int masterViewHeight = mView.getHeight(); mRelativeMasterViewX = masterViewScreenPosition[0] - parentViewScreenPosition[0]; mRelativeMasterViewY = masterViewScreenPosition[1] - parentViewScreenPosition[1]; final int relativeMasterViewCenterX = mRelativeMasterViewX + masterViewWidth / 2; int toolTipViewAboveY = mRelativeMasterViewY - getHeight(); int toolTipViewBelowY = Math.max(0, mRelativeMasterViewY + masterViewHeight); int toolTipViewX = Math.max(0, relativeMasterViewCenterX - mWidth / 2); if (toolTipViewX + mWidth > viewDisplayFrame.right) { toolTipViewX = viewDisplayFrame.right - mWidth; } setX(toolTipViewX); setPointerCenterX(relativeMasterViewCenterX); final boolean showBelow = toolTipViewAboveY < 0; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { ViewHelper.setAlpha(mTopPointerView, showBelow ? 1 : 0); ViewHelper.setAlpha(mBottomPointerView, showBelow ? 0 : 1); } else { mTopPointerView.setVisibility(showBelow ? VISIBLE : GONE); mBottomPointerView.setVisibility(showBelow ? GONE : VISIBLE); } int toolTipViewY; if (showBelow) { toolTipViewY = toolTipViewBelowY; } else { toolTipViewY = toolTipViewAboveY; } if (mToolTip.getAnimationType() == ToolTip.AnimationType.NONE) { ViewHelper.setTranslationY(this, toolTipViewY); ViewHelper.setTranslationX(this, toolTipViewX); } else { Collection<Animator> animators = new ArrayList<Animator>(5); if (mToolTip.getAnimationType() == ToolTip.AnimationType.FROM_MASTER_VIEW) { animators.add(ObjectAnimator.ofFloat(this, TRANSLATION_Y_COMPAT, mRelativeMasterViewY + mView.getHeight() / 2 - getHeight() / 2, toolTipViewY)); animators.add(ObjectAnimator.ofFloat(this, TRANSLATION_X_COMPAT, mRelativeMasterViewX + mView.getWidth() / 2 - mWidth / 2, toolTipViewX)); } else if (mToolTip.getAnimationType() == ToolTip.AnimationType.FROM_TOP) { animators.add(ObjectAnimator.ofFloat(this, TRANSLATION_Y_COMPAT, 0, toolTipViewY)); } animators.add(ObjectAnimator.ofFloat(this, SCALE_X_COMPAT, 0, 1)); animators.add(ObjectAnimator.ofFloat(this, SCALE_Y_COMPAT, 0, 1)); animators.add(ObjectAnimator.ofFloat(this, ALPHA_COMPAT, 0, 1)); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether(animators); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { animatorSet.addListener(new AppearanceAnimatorListener(toolTipViewX, toolTipViewY)); } try{ animatorSet.start(); }catch(Exception e){ e.printStackTrace(); } } } public void setPointerCenterX(final int pointerCenterX) { int pointerWidth = Math.max(mTopPointerView.getMeasuredWidth(), mBottomPointerView.getMeasuredWidth()); ViewHelper.setX(mTopPointerView, pointerCenterX - pointerWidth / 2 - (int) getX()); ViewHelper.setX(mBottomPointerView, pointerCenterX - pointerWidth / 2 - (int) getX()); } public void setOnToolTipViewClickedListener(final OnToolTipViewClickedListener listener) { mListener = listener; } public void setColor(final int color) { mTopPointerView.setColorFilter(color, PorterDuff.Mode.MULTIPLY); mTopFrame.getBackground().setColorFilter(color, PorterDuff.Mode.MULTIPLY); mBottomPointerView.setColorFilter(color, PorterDuff.Mode.MULTIPLY); mBottomFrame.getBackground().setColorFilter(color, PorterDuff.Mode.MULTIPLY); mContentHolder.setBackgroundColor(color); } private void setContentView(final View view) { mContentHolder.removeAllViews(); mContentHolder.addView(view); } public void remove() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) getLayoutParams(); setX(params.leftMargin); setY(params.topMargin); params.leftMargin = 0; params.topMargin = 0; setLayoutParams(params); } if (mToolTip.getAnimationType() == ToolTip.AnimationType.NONE) { if (getParent() != null) { ((ViewManager) getParent()).removeView(this); } } else { Collection<Animator> animators = new ArrayList<Animator>(5); if (mToolTip.getAnimationType() == ToolTip.AnimationType.FROM_MASTER_VIEW) { animators.add(ObjectAnimator.ofInt(this, TRANSLATION_Y_COMPAT, (int) getY(), mRelativeMasterViewY + mView.getHeight() / 2 - getHeight() / 2)); animators.add(ObjectAnimator.ofInt(this, TRANSLATION_X_COMPAT, (int) getX(), mRelativeMasterViewX + mView.getWidth() / 2 - mWidth / 2)); } else { animators.add(ObjectAnimator.ofFloat(this, TRANSLATION_Y_COMPAT, getY(), 0)); } animators.add(ObjectAnimator.ofFloat(this, SCALE_X_COMPAT, 1, 0)); animators.add(ObjectAnimator.ofFloat(this, SCALE_Y_COMPAT, 1, 0)); animators.add(ObjectAnimator.ofFloat(this, ALPHA_COMPAT, 1, 0)); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether(animators); animatorSet.addListener(new DisappearanceAnimatorListener()); animatorSet.start(); } } @Override public void onClick(final View view) { remove(); if (mListener != null) { mListener.onToolTipViewClicked(this); } } /** * Convenience method for getting X. */ @SuppressLint("NewApi") @Override public float getX() { float result; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { result = super.getX(); } else { result = ViewHelper.getX(this); } return result; } /** * Convenience method for setting X. */ @SuppressLint("NewApi") @Override public void setX(final float x) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { super.setX(x); } else { ViewHelper.setX(this, x); } } /** * Convenience method for getting Y. */ @SuppressLint("NewApi") @Override public float getY() { float result; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { result = super.getY(); } else { result = ViewHelper.getY(this); } return result; } /** * Convenience method for setting Y. */ @SuppressLint("NewApi") @Override public void setY(final float y) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { super.setY(y); } else { ViewHelper.setY(this, y); } } public interface OnToolTipViewClickedListener { void onToolTipViewClicked(ToolTipView toolTipView); } private class AppearanceAnimatorListener extends AnimatorListenerAdapter { private final float mToolTipViewX; private final float mToolTipViewY; AppearanceAnimatorListener(final float fToolTipViewX, final float fToolTipViewY) { mToolTipViewX = fToolTipViewX; mToolTipViewY = fToolTipViewY; } @Override public void onAnimationStart(final Animator animation) { } @Override @SuppressLint("NewApi") public void onAnimationEnd(final Animator animation) { RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) getLayoutParams(); params.leftMargin = (int) mToolTipViewX; params.topMargin = (int) mToolTipViewY; setX(0); setY(0); setLayoutParams(params); } @Override public void onAnimationCancel(final Animator animation) { } @Override public void onAnimationRepeat(final Animator animation) { } } private class DisappearanceAnimatorListener extends AnimatorListenerAdapter { @Override public void onAnimationStart(final Animator animation) { } @Override public void onAnimationEnd(final Animator animation) { if (getParent() != null) { ((ViewManager) getParent()).removeView(ToolTipView.this); } } @Override public void onAnimationCancel(final Animator animation) { } @Override public void onAnimationRepeat(final Animator animation) { } } }