Android Open Source - GuiLib Showcase View






From Project

Back to project page GuiLib.

License

The source code is released under:

Apache License

If you think the Android project GuiLib listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright 2014 Alex Curran//from  w  w  w  . j  a  v a2 s .  c o m
 *
 * 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.github.amlcurran.showcaseview;

import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.Button;
import android.widget.RelativeLayout;

import com.gandulf.guilib.R;
import com.github.amlcurran.showcaseview.AnimationFactory.AnimationEndListener;
import com.github.amlcurran.showcaseview.AnimationFactory.AnimationStartListener;
import com.github.amlcurran.showcaseview.targets.Target;

/**
 * A view which allows you to showcase areas of your app with an explanation.
 */
public class ShowcaseView extends RelativeLayout implements View.OnClickListener, View.OnTouchListener,
    ViewTreeObserver.OnPreDrawListener, ViewTreeObserver.OnGlobalLayoutListener {

  private static final int HOLO_BLUE = Color.parseColor("#33B5E5");

  private final Button mEndButton;
  private final TextDrawer textDrawer;
  private final ShowcaseDrawer showcaseDrawer;
  private final ShowcaseAreaCalculator showcaseAreaCalculator;
  private final AnimationFactory animationFactory;
  private final ShotStateStore shotStateStore;

  // Showcase metrics
  private int showcaseX = -1;
  private int showcaseY = -1;
  private float scaleMultiplier = 1f;

  // Touch items
  private boolean hasCustomClickListener = false;
  private boolean blockTouches = true;
  private boolean hideOnTouch = false;
  private OnShowcaseEventListener mEventListener = OnShowcaseEventListener.NONE;

  private boolean hasAlteredText = false;
  private boolean hasNoTarget = false;
  private boolean shouldCentreText;
  private Bitmap bitmapBuffer;

  // Animation items
  private long fadeInMillis;
  private long fadeOutMillis;

  protected ShowcaseView(Context context, boolean newStyle) {
    this(context, null, R.styleable.CustomTheme_showcaseViewStyle, newStyle);
  }

  protected ShowcaseView(Context context, AttributeSet attrs, int defStyle, boolean newStyle) {
    super(context, attrs, defStyle);

    ApiUtils apiUtils = new ApiUtils();
    animationFactory = new AnimatorAnimationFactory();
    showcaseAreaCalculator = new ShowcaseAreaCalculator();
    shotStateStore = new ShotStateStore(context);

    apiUtils.setFitsSystemWindowsCompat(this);
    getViewTreeObserver().addOnPreDrawListener(this);
    getViewTreeObserver().addOnGlobalLayoutListener(this);

    // Get the attributes for the ShowcaseView
    final TypedArray styled = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ShowcaseView,
        R.attr.showcaseViewStyle, R.style.ShowcaseView);

    // Set the default animation times
    fadeInMillis = getResources().getInteger(android.R.integer.config_mediumAnimTime);
    fadeOutMillis = getResources().getInteger(android.R.integer.config_mediumAnimTime);

    mEndButton = (Button) LayoutInflater.from(context).inflate(R.layout.showcase_button, null);
    if (newStyle) {
      showcaseDrawer = new NewShowcaseDrawer(getResources());
    } else {
      showcaseDrawer = new StandardShowcaseDrawer(getResources());
    }
    textDrawer = new TextDrawer(getResources(), showcaseAreaCalculator, getContext());

    updateStyle(styled, false);

    init();
  }

  private void init() {

    setOnTouchListener(this);

    if (mEndButton.getParent() == null) {
      int margin = (int) getResources().getDimension(R.dimen.button_margin);
      RelativeLayout.LayoutParams lps = (LayoutParams) generateDefaultLayoutParams();
      lps.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
      lps.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
      lps.setMargins(margin, margin, margin, margin);
      mEndButton.setLayoutParams(lps);
      mEndButton.setText(android.R.string.ok);
      if (!hasCustomClickListener) {
        mEndButton.setOnClickListener(this);
      }
      addView(mEndButton);
    }

  }

  private boolean hasShot() {
    return shotStateStore.hasShot();
  }

  void setShowcasePosition(Point point) {
    setShowcasePosition(point.x, point.y);
  }

  void setShowcasePosition(int x, int y) {
    if (shotStateStore.hasShot()) {
      return;
    }
    showcaseX = x;
    showcaseY = y;
    // init();
    invalidate();
  }

  public void setTarget(final Target target) {
    setShowcase(target, false);
  }

  public void setShowcase(final Target target, final boolean animate) {
    postDelayed(new Runnable() {
      @Override
      public void run() {

        if (!shotStateStore.hasShot()) {

          updateBitmap();
          Point targetPoint = target.getPoint();
          if (targetPoint != null) {
            hasNoTarget = false;
            if (animate) {
              animationFactory.animateTargetToPoint(ShowcaseView.this, targetPoint);
            } else {
              setShowcasePosition(targetPoint);
            }
          } else {
            hasNoTarget = true;
            invalidate();
          }

        }
      }
    }, 100);
  }

  private void updateBitmap() {
    if (bitmapBuffer == null || haveBoundsChanged()) {
      if (bitmapBuffer != null)
        bitmapBuffer.recycle();

      if (getMeasuredWidth() != 0 && getMeasuredHeight() != 0)
        bitmapBuffer = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
      else
        bitmapBuffer = null;

    }
  }

  private boolean haveBoundsChanged() {
    return getMeasuredWidth() != bitmapBuffer.getWidth() || getMeasuredHeight() != bitmapBuffer.getHeight();
  }

  public boolean hasShowcaseView() {
    return (showcaseX != 1000000 && showcaseY != 1000000) && !hasNoTarget;
  }

  public void setShowcaseX(int x) {
    setShowcasePosition(x, showcaseY);
  }

  public void setShowcaseY(int y) {
    setShowcasePosition(showcaseX, y);
  }

  public int getShowcaseX() {
    return showcaseX;
  }

  public int getShowcaseY() {
    return showcaseY;
  }

  /**
   * Override the standard button click event
   * 
   * @param listener
   *            Listener to listen to on click events
   */
  public void overrideButtonClick(OnClickListener listener) {
    if (shotStateStore.hasShot()) {
      return;
    }
    if (mEndButton != null) {
      mEndButton.setOnClickListener(listener != null ? listener : this);
    }
    hasCustomClickListener = true;
  }

  public void setOnShowcaseEventListener(OnShowcaseEventListener listener) {
    if (listener != null) {
      mEventListener = listener;
    } else {
      mEventListener = OnShowcaseEventListener.NONE;
    }
  }

  public void setButtonText(CharSequence text) {
    if (mEndButton != null) {
      mEndButton.setText(text);
    }
  }

  @Override
  public boolean onPreDraw() {
    boolean recalculatedCling = showcaseAreaCalculator.calculateShowcaseRect(showcaseX, showcaseY, showcaseDrawer);
    boolean recalculateText = recalculatedCling || hasAlteredText;
    if (recalculateText) {
      textDrawer.calculateTextPosition(getMeasuredWidth(), getMeasuredHeight(), this, shouldCentreText);
    }
    hasAlteredText = false;
    return true;
  }

  @Override
  protected void dispatchDraw(Canvas canvas) {
    if (showcaseX < 0 || showcaseY < 0 || shotStateStore.hasShot()) {
      super.dispatchDraw(canvas);
      return;
    }

    // Draw background color
    if (bitmapBuffer != null) {
      showcaseDrawer.erase(bitmapBuffer);

      // Draw the showcase drawable
      if (!hasNoTarget) {
        showcaseDrawer.drawShowcase(bitmapBuffer, showcaseX, showcaseY, scaleMultiplier);
        showcaseDrawer.drawToCanvas(canvas, bitmapBuffer);
      }
    }
    // Draw the text on the screen, recalculating its position if necessary
    textDrawer.draw(canvas);

    super.dispatchDraw(canvas);

  }

  @Override
  public void onClick(View view) {
    hide();
  }

  public void hide() {
    clearBitmap();
    // If the type is set to one-shot, store that it has shot
    shotStateStore.storeShot();
    mEventListener.onShowcaseViewHide(this);
    fadeOutShowcase();
  }

  private void clearBitmap() {
    if (bitmapBuffer != null && !bitmapBuffer.isRecycled()) {
      bitmapBuffer.recycle();
      bitmapBuffer = null;
    }
  }

  private void fadeOutShowcase() {
    animationFactory.fadeOutView(this, fadeOutMillis, new AnimationEndListener() {
      @Override
      public void onAnimationEnd() {
        setVisibility(View.GONE);
        mEventListener.onShowcaseViewDidHide(ShowcaseView.this);
      }
    });
  }

  public void show() {
    mEventListener.onShowcaseViewShow(this);
    fadeInShowcase();
  }

  private void fadeInShowcase() {
    animationFactory.fadeInView(this, fadeInMillis, new AnimationStartListener() {
      @Override
      public void onAnimationStart() {
        setVisibility(View.VISIBLE);
      }
    });
  }

  @Override
  public boolean onTouch(View view, MotionEvent motionEvent) {

    float xDelta = Math.abs(motionEvent.getRawX() - showcaseX);
    float yDelta = Math.abs(motionEvent.getRawY() - showcaseY);
    double distanceFromFocus = Math.sqrt(Math.pow(xDelta, 2) + Math.pow(yDelta, 2));

    if (MotionEvent.ACTION_UP == motionEvent.getAction() && hideOnTouch
        && distanceFromFocus > showcaseDrawer.getBlockedRadius()) {
      this.hide();
      return true;
    }

    return blockTouches && distanceFromFocus > showcaseDrawer.getBlockedRadius();
  }

  private static void insertShowcaseView(ShowcaseView showcaseView, Activity activity) {
    ((ViewGroup) activity.getWindow().getDecorView()).addView(showcaseView);
    if (!showcaseView.hasShot()) {
      showcaseView.show();
    } else {
      showcaseView.hideImmediate();
    }
  }

  private void hideImmediate() {
    setVisibility(GONE);
  }

  public void setContentTitle(CharSequence title) {
    textDrawer.setContentTitle(title);
  }

  public void setContentText(CharSequence text) {
    textDrawer.setContentText(text);
  }

  private void setScaleMultiplier(float scaleMultiplier) {
    this.scaleMultiplier = scaleMultiplier;
  }

  @Override
  public void onGlobalLayout() {
    if (!shotStateStore.hasShot()) {
      updateBitmap();
    }
  }

  public void hideButton() {
    mEndButton.setVisibility(GONE);
  }

  public void showButton() {
    mEndButton.setVisibility(VISIBLE);
  }

  /**
   * Builder class which allows easier creation of {@link ShowcaseView}s. It is recommended that you use this Builder
   * class.
   */
  public static class Builder {

    final ShowcaseView showcaseView;
    private final Activity activity;

    public Builder(Activity activity) {
      this(activity, false);
    }

    public Builder(Activity activity, boolean useNewStyle) {
      this.activity = activity;
      this.showcaseView = new ShowcaseView(activity, useNewStyle);
      this.showcaseView.setTarget(Target.NONE);
    }

    /**
     * Create the {@link com.github.amlcurran.showcaseview.ShowcaseView} and show it.
     * 
     * @return the created ShowcaseView
     */
    public ShowcaseView build() {
      insertShowcaseView(showcaseView, activity);
      return showcaseView;
    }

    /**
     * Set the title text shown on the ShowcaseView.
     */
    public Builder setContentTitle(int resId) {
      return setContentTitle(activity.getString(resId));
    }

    /**
     * Set the title text shown on the ShowcaseView.
     */
    public Builder setContentTitle(CharSequence title) {
      showcaseView.setContentTitle(title);
      return this;
    }

    /**
     * Set the descriptive text shown on the ShowcaseView.
     */
    public Builder setContentText(int resId) {
      return setContentText(activity.getString(resId));
    }

    /**
     * Set the descriptive text shown on the ShowcaseView.
     */
    public Builder setContentText(CharSequence text) {
      showcaseView.setContentText(text);
      return this;
    }

    /**
     * Set the target of the showcase.
     * 
     * @param target
     *            a {@link com.github.amlcurran.showcaseview.targets.Target} representing the item to showcase
     *            (e.g., a button, or action item).
     */
    public Builder setTarget(Target target) {
      showcaseView.setTarget(target);
      return this;
    }

    /**
     * Set the style of the ShowcaseView. See the sample app for example styles.
     */
    public Builder setStyle(int theme) {
      showcaseView.setStyle(theme);
      return this;
    }

    /**
     * Set a listener which will override the button clicks.
     * <p/>
     * Note that you will have to manually hide the ShowcaseView
     */
    public Builder setOnClickListener(OnClickListener onClickListener) {
      showcaseView.overrideButtonClick(onClickListener);
      return this;
    }

    /**
     * Don't make the ShowcaseView block touches on itself. This doesn't block touches in the showcased area.
     * <p/>
     * By default, the ShowcaseView does block touches
     */
    public Builder doNotBlockTouches() {
      showcaseView.setBlocksTouches(false);
      return this;
    }

    /**
     * Make this ShowcaseView hide when the user touches outside the showcased area. This enables
     * {@link #doNotBlockTouches()} as well.
     * <p/>
     * By default, the ShowcaseView doesn't hide on touch.
     */
    public Builder hideOnTouchOutside() {
      showcaseView.setBlocksTouches(true);
      showcaseView.setHideOnTouchOutside(true);
      return this;
    }

    /**
     * Set the ShowcaseView to only ever show once.
     * 
     * @param shotId
     *            a unique identifier (<em>across the app</em>) to store whether this ShowcaseView has been shown.
     */
    public Builder singleShot(long shotId) {
      showcaseView.setSingleShot(shotId);
      return this;
    }

    public Builder setShowcaseEventListener(OnShowcaseEventListener showcaseEventListener) {
      showcaseView.setOnShowcaseEventListener(showcaseEventListener);
      return this;
    }
  }

  /**
   * Set whether the text should be centred in the screen, or left-aligned (which is the default).
   */
  public void setShouldCentreText(boolean shouldCentreText) {
    this.shouldCentreText = shouldCentreText;
    hasAlteredText = true;
    invalidate();
  }

  /**
   * @see com.github.amlcurran.showcaseview.ShowcaseView.Builder#setSingleShot(long)
   */
  private void setSingleShot(long shotId) {
    shotStateStore.setSingleShot(shotId);
  }

  /**
   * Change the position of the ShowcaseView's button from the default bottom-right position.
   * 
   * @param layoutParams
   *            a {@link android.widget.RelativeLayout.LayoutParams} representing the new position of the button
   */
  public void setButtonPosition(RelativeLayout.LayoutParams layoutParams) {
    mEndButton.setLayoutParams(layoutParams);
  }

  /**
   * Set the duration of the fading in and fading out of the ShowcaseView
   */
  private void setFadeDurations(long fadeInMillis, long fadeOutMillis) {
    this.fadeInMillis = fadeInMillis;
    this.fadeOutMillis = fadeOutMillis;
  }

  /**
   * @see com.github.amlcurran.showcaseview.ShowcaseView.Builder#hideOnTouchOutside()
   */
  public void setHideOnTouchOutside(boolean hideOnTouch) {
    this.hideOnTouch = hideOnTouch;
  }

  /**
   * @see com.github.amlcurran.showcaseview.ShowcaseView.Builder#doNotBlockTouches()
   */
  public void setBlocksTouches(boolean blockTouches) {
    this.blockTouches = blockTouches;
  }

  /**
   * @see com.github.amlcurran.showcaseview.ShowcaseView.Builder#setStyle(int)
   */
  public void setStyle(int theme) {
    TypedArray array = getContext().obtainStyledAttributes(theme, R.styleable.ShowcaseView);
    updateStyle(array, true);
  }

  private void updateStyle(TypedArray styled, boolean invalidate) {
    int backgroundColor = styled.getColor(R.styleable.ShowcaseView_sv_backgroundColor, Color.argb(128, 80, 80, 80));
    int showcaseColor = styled.getColor(R.styleable.ShowcaseView_sv_showcaseColor, HOLO_BLUE);
    String buttonText = styled.getString(R.styleable.ShowcaseView_sv_buttonText);
    if (TextUtils.isEmpty(buttonText)) {
      buttonText = getResources().getString(android.R.string.ok);
    }
    boolean tintButton = styled.getBoolean(R.styleable.ShowcaseView_sv_tintButtonColor, true);

    int titleTextAppearance = styled.getResourceId(R.styleable.ShowcaseView_sv_titleTextAppearance,
        R.style.TextAppearance_ShowcaseView_Title);
    int detailTextAppearance = styled.getResourceId(R.styleable.ShowcaseView_sv_detailTextAppearance,
        R.style.TextAppearance_ShowcaseView_Detail);

    styled.recycle();

    showcaseDrawer.setShowcaseColour(showcaseColor);
    showcaseDrawer.setBackgroundColour(backgroundColor);
    tintButton(showcaseColor, tintButton);
    mEndButton.setText(buttonText);
    textDrawer.setTitleStyling(titleTextAppearance);
    textDrawer.setDetailStyling(detailTextAppearance);
    hasAlteredText = true;

    if (invalidate) {
      invalidate();
    }
  }

  private void tintButton(int showcaseColor, boolean tintButton) {
    if (tintButton) {
      mEndButton.getBackground().setColorFilter(showcaseColor, PorterDuff.Mode.MULTIPLY);
    } else {
      mEndButton.getBackground().setColorFilter(HOLO_BLUE, PorterDuff.Mode.MULTIPLY);
    }
  }

}




Java Source Code List

au.com.bytecode.opencsv.CSVReader.java
com.ecloud.pulltozoomview.PullToZoomScrollView.java
com.gandulf.guilib.data.OpenArrayAdapter.java
com.gandulf.guilib.data.OpenFilter.java
com.gandulf.guilib.download.AbstractDownloader.java
com.gandulf.guilib.download.DownloadBroadcastReceiver.java
com.gandulf.guilib.download.DownloaderGinger.java
com.gandulf.guilib.download.DownloaderWrapper.java
com.gandulf.guilib.download.Downloader.java
com.gandulf.guilib.download.MediaScannerWrapper.java
com.gandulf.guilib.download.UnzipIntentService.java
com.gandulf.guilib.listener.CheckableListenable.java
com.gandulf.guilib.listener.OnCheckedChangeListener.java
com.gandulf.guilib.util.ColorUtil.java
com.gandulf.guilib.util.Debug.java
com.gandulf.guilib.util.DefaultTextWatcher.java
com.gandulf.guilib.util.DirectoryFileFilter.java
com.gandulf.guilib.util.FileFileFilter.java
com.gandulf.guilib.util.ListViewCompat.java
com.gandulf.guilib.util.MathUtil.java
com.gandulf.guilib.util.ResUtil.java
com.gandulf.guilib.view.ColorPickerDialog.java
com.gandulf.guilib.view.DynamicListViewEx.java
com.gandulf.guilib.view.HackeyDrawerLayout.java
com.gandulf.guilib.view.SeekBarEx.java
com.gandulf.guilib.view.VersionInfoDialog.java
com.gandulf.guilib.view.ViewScroller.java
com.gandulf.guilib.view.adapter.MultiFragmentPagerAdapter.java
com.getbase.floatingactionbutton.AddFloatingActionButton.java
com.getbase.floatingactionbutton.FloatingActionButton.java
com.getbase.floatingactionbutton.FloatingActionsMenu.java
com.github.amlcurran.showcaseview.AnimationFactory.java
com.github.amlcurran.showcaseview.AnimatorAnimationFactory.java
com.github.amlcurran.showcaseview.ApiUtils.java
com.github.amlcurran.showcaseview.Calculator.java
com.github.amlcurran.showcaseview.NewShowcaseDrawer.java
com.github.amlcurran.showcaseview.OnShowcaseEventListener.java
com.github.amlcurran.showcaseview.ShotStateStore.java
com.github.amlcurran.showcaseview.ShowcaseAreaCalculator.java
com.github.amlcurran.showcaseview.ShowcaseDrawer.java
com.github.amlcurran.showcaseview.ShowcaseView.java
com.github.amlcurran.showcaseview.StandardShowcaseDrawer.java
com.github.amlcurran.showcaseview.TextDrawer.java
com.github.amlcurran.showcaseview.targets.ActionBarReflector.java
com.github.amlcurran.showcaseview.targets.ActionBarViewWrapper.java
com.github.amlcurran.showcaseview.targets.ActionItemTarget.java
com.github.amlcurran.showcaseview.targets.ActionViewTarget.java
com.github.amlcurran.showcaseview.targets.AppCompatReflector.java
com.github.amlcurran.showcaseview.targets.PointTarget.java
com.github.amlcurran.showcaseview.targets.ReflectorFactory.java
com.github.amlcurran.showcaseview.targets.Reflector.java
com.github.amlcurran.showcaseview.targets.SherlockReflector.java
com.github.amlcurran.showcaseview.targets.Target.java
com.github.amlcurran.showcaseview.targets.ViewTarget.java
com.sothree.slidinguppanel.SlidingUpPanelLayout.java
com.sothree.slidinguppanel.ViewDragHelper.java
com.thebnich.floatinghintedittext.FloatingHintEditText.java
com.thebnich.floatinghintedittext.FloatingHintTextView.java
com.wefika.flowlayout.FlowLayout.java
de.hdodenhof.circleimageview.CircleImageView.java
uk.co.senab.photoview.Compat.java
uk.co.senab.photoview.PhotoViewAttacher.java
uk.co.senab.photoview.PhotoView.java
uk.co.senab.photoview.SDK16.java
uk.co.senab.photoview.ScrollerProxy.java
uk.co.senab.photoview.VersionedGestureDetector.java
uk.me.lewisdeane.ldialogs.BaseDialog.java
uk.me.lewisdeane.ldialogs.CustomDialog.java
uk.me.lewisdeane.ldialogs.CustomListAdapter.java