2015-10-15 79 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' –