2016-07-11 39 views
10

Ho una vista testuale con più estensioni cliccabili in esso. Voglio essere in grado di testare cliccando su questi span.Come fare clic su un dispositivo di scatto con l'espresso?

Ho provato a impostare una ViewAction personalizzata che avrebbe trovato i Clickable Pad nel TextView e quindi avrebbe abbinato il loro testo al testo desiderato e quindi fare clic sulle coordinate xy di quel testo. Tuttavia, sembra che gli span aggiunti a TextView non siano di tipo ClickableSpan e siano invece il frammento che ha aggiunto l'estensione.

Pertanto, non sono in grado di distinguere i campi di collegamento. C'è un modo migliore per farlo?

Aggiunta campate: metodo

Util.addClickableSpan(spannableString, string, linkedString, new  ClickableSpan() { 
@Override 
public void onClick(View textView) {} 
}); 

tvAcceptTc.setText(spannableString); 
tvAcceptTc.setMovementMethod(LinkMovementMethod.getInstance()); 

Utility:

public static void addClickableSpan(SpannableString spannableString, 
           String text, 
           String subText, 
           ClickableSpan clickableSpan) { 
     int start = text.indexOf(subText); 
     int end = text.indexOf(subText) + subText.length(); 
     int flags = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE; 

     spannableString.setSpan(clickableSpan, start, end, flags); 
} 

Definizione del ViewAction:

@Override 
     public void perform(UiController uiController, View view) { 
      uiController.loopMainThreadUntilIdle(); 
      if (view instanceof TextView) { 

       TextView textView = (TextView) view; 
       Layout textViewLayout = textView.getLayout(); 


       SpannableString fullSpannable = new SpannableString(textView.getText()); 

       Object[] spans = fullSpannable.getSpans(0, fullSpannable.length(), Object.class); 

       ClickableSpan span = null; 
       for (Object object : spans) { 
        if (object instanceof BaseFragment) { 
         ClickableSpan foundSpan = (ClickableSpan)object; 
         int spanStart = fullSpannable.getSpanStart(foundSpan); 
         int spanEnd = fullSpannable.getSpanEnd(foundSpan); 
         if (fullSpannable.subSequence(spanStart, spanEnd).equals(aSubstring)) { 
          //Found the correct span! 
          span = foundSpan; 
         } 
        } 
       } ... go on to click the xy-coordinates 
+0

come stai aggiungendo le tue span? hai provato a chiamare 'TextUtils # dumpSpans'? – pskink

+0

Ho aggiunto il codice per aggiungere gli span. Funziona davvero ora se rimuovo l'istanza di controllo e casting, ma troverà span con il testo invece di un solo ClickableSpan. Ho guardato gli span nel debugger e nessuno di loro era di tipo ClickableSpan, ma erano invece dal frammento che ha aggiunto gli span. –

+0

vedi l'ultimo parametro di 'getSpans' – pskink

risposta

14

Questa è la mia soluzione. È più semplice perché non abbiamo bisogno di trovare le coordinate. Una volta abbiamo trovato il ClickableSpan, ci basta cliccarci sopra:

public static ViewAction clickClickableSpan(final CharSequence textToClick) { 
    return new ViewAction() { 
     @Override 
     public Matcher<View> getConstraints() { 
      return Matchers.instanceOf(TextView.class); 
     } 

     @Override 
     public String getDescription() { 
      return "clicking on a ClickableSpan"; 
     } 

     @Override 
     public void perform(UiController uiController, View view) { 
      TextView textView = (TextView) view; 
      SpannableString spannableString = (SpannableString) textView.getText(); 

      if (spannableString.length() == 0) { 
       // TextView is empty, nothing to do 
       throw new NoMatchingViewException.Builder() 
         .includeViewHierarchy(true) 
         .withRootView(textView) 
         .build(); 
      } 

      // Get the links inside the TextView and check if we find textToClick 
      ClickableSpan[] spans = spannableString.getSpans(0, spannableString.length(), ClickableSpan.class); 
      if (spans.length > 0) { 
       ClickableSpan spanCandidate; 
       for (ClickableSpan span : spans) { 
        spanCandidate = span; 
        int start = spannableString.getSpanStart(spanCandidate); 
        int end = spannableString.getSpanEnd(spanCandidate); 
        CharSequence sequence = spannableString.subSequence(start, end); 
        if (textToClick.toString().equals(sequence.toString())) { 
         span.onClick(textView); 
         return; 
        } 
       } 
      } 

      // textToClick not found in TextView 
      throw new NoMatchingViewException.Builder() 
        .includeViewHierarchy(true) 
        .withRootView(textView) 
        .build(); 

     } 
    }; 
} 

Ora è possibile utilizzare il nostro personalizzato ViewAction proprio così:

onView(withId(R.id.myTextView)).perform(clickClickableSpan("myLink")); 
+2

Dovresti inviarlo alla libreria di supporto di Android Testing e trasferirlo in espresso. https://google.github.io/android-testing-support-library/contribute/index.html – tir38

0

Questo funziona per me:

/** 
* Clicks the first ClickableSpan in the TextView 
*/ 
public static ViewAction clickFirstClickableSpan() { 
    return new GeneralClickAction(
      Tap.SINGLE, 
      new CoordinatesProvider() { 
       @Override 
       public float[] calculateCoordinates(View view) { 
        //https://leons.im/posts/how-to-get-coordinate-of-a-clickablespan-inside-a-textview/ 
        TextView textView = (TextView) view; 
        Rect parentTextViewRect = new Rect(); 

        SpannableString spannableString = (SpannableString) textView.getText(); 
        Layout textViewLayout = textView.getLayout(); 
        ClickableSpan spanToLocate = null; 

        if (spannableString.length() == 0) { 
         return new float[2]; 
        } 

        ClickableSpan[] spans = spannableString.getSpans(0, spannableString.length(), ClickableSpan.class); 
        if (spans.length > 0) { 
         spanToLocate = spans[0]; 
        } 

        if (spanToLocate == null) { 
         // no specific view found 
         throw new NoMatchingViewException.Builder() 
           .includeViewHierarchy(true) 
           .withRootView(textView) 
           .build(); 
        } 

        double startOffsetOfClickedText = spannableString.getSpanStart(spanToLocate); 
        double endOffsetOfClickedText = spannableString.getSpanEnd(spanToLocate); 
        double startXCoordinatesOfClickedText = textViewLayout.getPrimaryHorizontal((int) startOffsetOfClickedText); 
        double endXCoordinatesOfClickedText = textViewLayout.getPrimaryHorizontal((int) endOffsetOfClickedText); 

        // Get the rectangle of the clicked text 
        int currentLineStartOffset = textViewLayout.getLineForOffset((int) startOffsetOfClickedText); 
        int currentLineEndOffset = textViewLayout.getLineForOffset((int) endOffsetOfClickedText); 
        boolean keywordIsInMultiLine = currentLineStartOffset != currentLineEndOffset; 
        textViewLayout.getLineBounds(currentLineStartOffset, parentTextViewRect); 

        // Update the rectangle position to his real position on screen 
        int[] parentTextViewLocation = {0, 0}; 
        textView.getLocationOnScreen(parentTextViewLocation); 

        double parentTextViewTopAndBottomOffset = (
          parentTextViewLocation[1] - 
            textView.getScrollY() + 
            textView.getCompoundPaddingTop() 
        ); 
        parentTextViewRect.top += parentTextViewTopAndBottomOffset; 
        parentTextViewRect.bottom += parentTextViewTopAndBottomOffset; 
        parentTextViewRect.left += (
          parentTextViewLocation[0] + 
            startXCoordinatesOfClickedText + 
            textView.getCompoundPaddingLeft() - 
            textView.getScrollX() 
        ); 
        parentTextViewRect.right = (int) (
          parentTextViewRect.left + 
            endXCoordinatesOfClickedText - 
            startXCoordinatesOfClickedText 
        ); 

        int screenX = (parentTextViewRect.left + parentTextViewRect.right)/2; 
        int screenY = (parentTextViewRect.top + parentTextViewRect.bottom)/2; 
        if (keywordIsInMultiLine) { 
         screenX = parentTextViewRect.left; 
         screenY = parentTextViewRect.top; 
        } 
        return new float[]{screenX, screenY}; 
       } 
      }, 
      Press.FINGER); 
} 
0

si può utilizzare al posto di SpannableSpannableString compatibile con SpannableStringBuilder.

dispiace, io sono un uomo nuovo, hanno solo 1 reputazione, non si può aggiungere un comment.Even il mio inglese è molto povero .....

vi suggerisco di utilizzare:

Spannable spannableString = (Spannable) textView.getText(); 

invece di:

SpannableString spannableString = (SpannableString) textView.getText(); 

postale tutto il codice qui sotto:

public class CustomViewActions { 

    /** 
    * click specific spannableString 
    */ 
    public static ViewAction clickClickableSpan(final CharSequence textToClick) { 
     return clickClickableSpan(-1, textToClick); 
    } 

    /** 
    * click the first spannableString 
    */ 
    public static ViewAction clickClickableSpan() { 
     return clickClickableSpan(0, null); 
    } 

    /** 
    * click the nth spannableString 
    */ 
    public static ViewAction clickClickableSpan(final int index) { 
     return clickClickableSpan(index, null); 
    } 

    public static ViewAction clickClickableSpan(final int index,final CharSequence textToClick) { 
     return new ViewAction() { 
      @Override 
      public Matcher<View> getConstraints() { 
       return instanceOf(TextView.class); 
      } 

      @Override 
      public String getDescription() { 
       return "clicking on a ClickableSpan"; 
      } 

      @Override 
      public void perform(UiController uiController, View view) { 
       TextView textView = (TextView) view; 
       Spannable spannableString = (Spannable) textView.getText(); 
       ClickableSpan spanToLocate = null; 
       if (spannableString.length() == 0) { 
        // TextView is empty, nothing to do 
        throw new NoMatchingViewException.Builder() 
          .includeViewHierarchy(true) 
          .withRootView(textView) 
          .build(); 
       } 

       // Get the links inside the TextView and check if we find textToClick 
       ClickableSpan[] spans = spannableString.getSpans(0, spannableString.length(), ClickableSpan.class); 

       if (spans.length > 0) { 
        if(index >=spans.length){ 
         throw new NoMatchingViewException.Builder() 
          .includeViewHierarchy(true) 
          .withRootView(textView) 
          .build(); 
        }else if (index >= 0) { 
         spanToLocate = spans[index]; 
         spanToLocate.onClick(textView); 
         return; 
        } 
        for (int i = 0; i < spans.length; i++) { 
         int start = spannableString.getSpanStart(spans[i]); 
         int end = spannableString.getSpanEnd(spans[i]); 
         CharSequence sequence = spannableString.subSequence(start, end); 
         if (textToClick.toString().equals(sequence.toString())) { 
          spanToLocate = spans[i]; 
          spanToLocate.onClick(textView); 
          return; 
         } 
        } 
       } 

       // textToClick not found in TextView 
       throw new NoMatchingViewException.Builder() 
         .includeViewHierarchy(true) 
         .withRootView(textView) 
         .build(); 

      } 
     }; 
    } 

} 
+0

Anche se questo potrebbe essere un suggerimento prezioso per risolvere il problema, una buona risposta dimostra anche la soluzione. Si prega di [EDIT] (http: // StackOverflow.it/posts/5419867/edit) per fornire codice di esempio per mostrare cosa intendi. In alternativa, considera la possibilità di scrivere questo come commento invece di –

+0

btw, il diretto 'onClick' potrebbe perdere qualche bug. Ad esempio, textView è mascherato da altre aree cliccabili. questa è la mia ipotesi senza test. –

Problemi correlati