2015-10-15 23 views
0

我想知道是否有任何简单的解决方案来创建一个元素将突出显示的覆盖。自定义指令覆盖与突出显示视图(不使用ShowcaseViewLibrary)

所以,最终的结果会是这个样子:

enter image description here

我想避免使用ShowcaseViewLibrary从种种理由(它不具有的外观我需要的,它不再支持等)。

我想过使用FrameLayout,但我不确定如何实现突出显示的现有元素。同时将箭头或气泡放在元素上,以便精确连接。

回答

0

一个快速简便的方法是制作一个您想要演示的活动的副本,并添加叠加层并仅显示该活动。这是我做的,它工作正常。

0
/** 
* Created by Nikola D. on 10/1/2015. 
*/ 
@TargetApi(Build.VERSION_CODES.HONEYCOMB) 
public class ShowCaseLayout extends ScrimInsetsFrameLayout { 
    private static final long DEFAULT_DURATION = 1000; 
    private static final int DEFAULT_RADIUS = 100; 
    private Paint mEmptyPaint; 
    private AbstractQueue<Pair<String, View>> mTargetQueue; 
    private int mLastCenterX = 600; 
    private int mLastCenterY = 100; 
    private ValueAnimator.AnimatorUpdateListener mAnimatorListenerX = new ValueAnimator.AnimatorUpdateListener() { 
     @TargetApi(Build.VERSION_CODES.HONEYCOMB) 
     @Override 
     public void onAnimationUpdate(ValueAnimator animation) { 

      mLastCenterX = (int) animation.getAnimatedValue(); 
      setWillNotDraw(false); 
      postInvalidate(); 
     } 
    }; 
    private ValueAnimator.AnimatorUpdateListener mAnimatorListenerY = new ValueAnimator.AnimatorUpdateListener() { 
     @TargetApi(Build.VERSION_CODES.HONEYCOMB) 
     @Override 
     public void onAnimationUpdate(ValueAnimator animation) { 
      mLastCenterY = (int) animation.getAnimatedValue(); 
      setWillNotDraw(false); 
      postInvalidate(); 
     } 
    }; 
    private ValueAnimator mCenterAnimatorX; 
    private ValueAnimator mCenterAnimatorY; 
    private boolean canRender = false; 
    private OnAttachStateChangeListener mAttachListener = new OnAttachStateChangeListener() { 
     @Override 
     public void onViewAttachedToWindow(View v) { 
      canRender = true; 
     } 

     @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) 
     @Override 
     public void onViewDetachedFromWindow(View v) { 
      canRender = false; 
      removeOnAttachStateChangeListener(this); 
     } 
    }; 
    private long mDuration = DEFAULT_DURATION; 
    private int mRadius = (int) DEFAULT_RADIUS; 
    private Interpolator mInterpolator = new LinearOutSlowInInterpolator(); 
    private ValueAnimator mRadiusAnimator; 
    private ValueAnimator.AnimatorUpdateListener mRadiusAnimatorListener = new ValueAnimator.AnimatorUpdateListener() { 
     @TargetApi(Build.VERSION_CODES.HONEYCOMB) 
     @Override 
     public void onAnimationUpdate(ValueAnimator animation) { 
      mRadius = (int) animation.getAnimatedValue(); 
     } 
    }; 
    private TextView mDescriptionText; 
    private Button mGotItButton; 
    private OnClickListener mExternalGotItButtonlistener; 
    private OnClickListener mGotItButtonClickListener = new OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      setNextTarget(); 
      if (mExternalGotItButtonlistener != null) { 
       mExternalGotItButtonlistener.onClick(v); 
      } 
     } 
    }; 
    private Animator.AnimatorListener mAnimatorSetListener = new AnimatorListenerAdapter() { 
     @Override 
     public void onAnimationEnd(Animator animation) { 
      super.onAnimationEnd(animation); 
      setNextTarget(); 
      invalidate(); 
      //mDescriptionText.layout(mTempRect.left, mTempRect.bottom + mTempRect.bottom, mDescriptionText.); 
     } 
    }; 
    private Rect mTempRect; 
    private Paint mBackgroundPaint; 
    private Bitmap bitmap; 
    private Canvas temp; 
    private int mStatusBarHeight = 0; 

    public ShowCaseLayout(Context context) { 
     super(context); 
     setupLayout(); 
    } 

    public ShowCaseLayout(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     setupLayout(); 
    } 

    public ShowCaseLayout(Context context, AttributeSet attrs, int defStyleAttr) { 
     super(context, attrs, defStyleAttr); 
     setupLayout(); 
    } 

    public void setTarget(View target, String hint) { 
     mTargetQueue.add(new Pair<>(hint, target)); 
    } 

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 
    private void setupLayout() { 
     mTargetQueue = new LinkedBlockingQueue<>(); 
     setWillNotDraw(false); 
     mBackgroundPaint = new Paint(); 
     int c = Color.argb(127, Color.red(Color.RED), Color.blue(Color.RED), Color.green(Color.RED)); 
     mBackgroundPaint.setColor(c); 
     mEmptyPaint = new Paint(); 
     mEmptyPaint.setColor(Color.TRANSPARENT); 
     mEmptyPaint.setStyle(Paint.Style.FILL); 
     mEmptyPaint.setAntiAlias(true); 
     mEmptyPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); 
     if (!ViewCompat.isLaidOut(this)) 
      addOnAttachStateChangeListener(mAttachListener); 
     else canRender = true; 
     mDescriptionText = new TextView(getContext()); 
     mGotItButton = new Button(getContext()); 
     mGotItButton.setText("GOT IT"); 
     mGotItButton.setOnClickListener(mGotItButtonClickListener); 
     addView(mGotItButton, generateDefaultLayoutParams()); 
     //ViewCompat.setAlpha(this, 0.5f); 

    } 

    @Override 
    protected LayoutParams generateDefaultLayoutParams() { 
     return new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 
    } 

    @Override 
    protected void onDraw(Canvas canvas) { 
     super.onDraw(canvas); 
     if (!canRender) return; 
     temp.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mBackgroundPaint); 
     temp.drawCircle(mLastCenterX, mLastCenterY, mRadius, mEmptyPaint); 
     canvas.drawBitmap(bitmap, 0, 0, null); 
    } 

    @TargetApi(Build.VERSION_CODES.M) 
    private void animateCenterToNextTarget(View target) { 
     int[] locations = new int[2]; 
     target.getLocationInWindow(locations); 
     int x = locations[0]; 
     int y = locations[1]; 
     mTempRect = new Rect(x, y, x + target.getWidth(), y + target.getHeight()); 
     int centerX = mTempRect.centerX(); 
     int centerY = mTempRect.centerY(); 
     int targetRadius = Math.abs(mTempRect.right - mTempRect.left)/2; 
     targetRadius += targetRadius * 0.05; 
     mCenterAnimatorX = ValueAnimator.ofInt(mLastCenterX, centerX).setDuration(mDuration); 
     mCenterAnimatorX.addUpdateListener(mAnimatorListenerX); 
     mCenterAnimatorY = ValueAnimator.ofInt(mLastCenterY, centerY).setDuration(mDuration); 
     mCenterAnimatorY.addUpdateListener(mAnimatorListenerY); 
     mRadiusAnimator = ValueAnimator.ofInt(mRadius, targetRadius); 
     mRadiusAnimator.addUpdateListener(mRadiusAnimatorListener); 
     playTogether(mCenterAnimatorY, mCenterAnimatorX, mRadiusAnimator); 

    } 


    @Override 
    protected void onSizeChanged(int w, int h, int oldw, int oldh) { 
     super.onSizeChanged(w, h, oldw, oldh); 
     bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); 
     bitmap.eraseColor(Color.TRANSPARENT); 
     temp = new Canvas(bitmap); 
    } 

    @TargetApi(Build.VERSION_CODES.HONEYCOMB) 
    private void playTogether(ValueAnimator... animators) { 
     AnimatorSet set = new AnimatorSet(); 
     set.setInterpolator(mInterpolator); 
     set.setDuration(mDuration); 
     set.playTogether(animators); 
     set.addListener(mAnimatorSetListener); 
     set.start(); 
    } 

    public void start(Activity activity) { 
     if (getParent() == null) { 
      attachLayoutToWindow(activity); 
     } 
     setNextTarget(); 
    } 

    private void setNextTarget() { 
     Pair<String, View> pair = mTargetQueue.poll(); 
     if (pair != null) { 
      if (pair.second != null) 
       animateCenterToNextTarget(pair.second); 
      mDescriptionText.setText(pair.first); 
     } 
    } 

    private void attachLayoutToWindow(Activity activity) { 
     FrameLayout rootLayout = (FrameLayout) activity.findViewById(android.R.id.content); 
     rootLayout.addView(this); 
    } 

    public void hideShowcaseLayout() { 

    } 


    public void setGotItButtonClickistener(OnClickListener mExternalGotItButtonlistener) { 
     this.mExternalGotItButtonlistener = mExternalGotItButtonlistener; 
    } 

    public TextView getDescriptionTextView() { 
     return mDescriptionText; 
    } 

    public void setDescriptionTextView(TextView textView) { 
     mDescriptionText = textView; 
    } 


} 

请注意,此代码是不完整的和正在开发中,你应该根据你的需要调整它。

此布局将在其Rect上围绕View绘制一个圆圈。

相反画圆,你可以drawRect到如果View的矩形目标视图或drawRoundRect和背景绘制RectRect范围是互补的。

绘制线(drawLine())应该从目标视图:

startX = (rect.right - rect.left)/2; 
startY = rect.bottom; 
endX = startX; 
endY = startY + arbitraryLineHeight; 

如果恩迪比布局高度你应该向上rect.top - arbitraryLineHeight绘制它更大,否则在绘制时,因为它是。

arbitraryLineHeight可能是descriptionViewRect.top这使得它更具动态性,而不是使用常数值。

+0

你能告诉我“ScrimInsetsFrameLayout”从哪里来? –

+1

从设计支持库,你没有义务扩展它,你可以扩展到'FrameLayout' –