11

What i did so far is a list view of textviews having the normal text and clickable spans: Item untouched

Clicking the span i'm opening the URL, clicking the item View around the textView leads to the listView OnItemClickListener navigating to the item details, that's fine: Item touched outside the textView

Now the problem is:

Item textView normal text touched Item textView span touched touching the textView makes the normal text be kinda highlighted (with the same color it has when the item is selected completely), textView's OnTouchListener touch event fires but not OnFocusChangeListener event and the item's View does not get the selection style. Tried all the variations of FOCUS_BLOCK_DESCENDANTS for listView, item View, the textView focusable was enabled or disabled with the same result.

Fortunately, textView OnClickListener event fires this way, but that's so ugly: the text is invisible while the touch is not released as the selected text color is the same as the item color, there's no other indication that the user is going to the item details other than that ugly text vanishing.

I suspect that happens because the content of the textView is Spannable, and the parts which are not CliclableSpan-s behave in this strange way.

Any chance i could select the item once the normal text is touched ?


The listView item layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:descendantFocusability="blocksDescendants"
    android:orientation="horizontal" >

    <ImageView
        android:id="@+id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginBottom="5dp"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:layout_marginTop="5dp"
        android:focusable="false" />

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/title"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="5dp"
            android:layout_marginTop="5dp"
            android:focusable="false"
            android:text=""
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/info"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="4dp"
            android:layout_marginLeft="4dp"
            android:layout_marginRight="4dp"
            android:focusable="false"
            android:text=""
            android:textAppearance="?android:attr/textAppearanceSmall" />

        <TextView
            android:id="@+id/details"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="4dp"
            android:layout_marginLeft="4dp"
            android:layout_marginRight="4dp"
            android:focusable="false"
            android:gravity="fill_horizontal|right"
            android:text=""
            android:textAppearance="?android:attr/textAppearanceMedium"/>

    </LinearLayout>

</LinearLayout>

With the text view setClickable(false) i'm able to disable this weird selection style in the way that nothing happens while touching the text view area, not good but might be useful for solution.

Also tried to add not focusable & not clickable button to each item, when it's touched the complete item is selected and when touch is released the item's click event is passed, that's exactly what i expected from the textView with Spannable content.

A-Live
  • 8,904
  • 2
  • 39
  • 74
  • did u try focusableIntouch to false for textview – Ron Jul 19 '12 at 18:25
  • At last if nothing works,I suggest u to override the `onInterceptTouchEvent` method and call performClick() in `DOWN` event and return false. See if this works... – Ron Jul 19 '12 at 18:39
  • @userSeven7s thanks for suggestion, I'd try it but you know - the click should not happen immediately after the touch. `focusableIntouch` was set to false, it also seems not like the textView gets the focus as it's onFocusChange listener does not fire the focus change event, the only textView event that fires is onTouch :( – A-Live Jul 20 '12 at 10:53

4 Answers4

4

Did you try setting the background of your TextView to Android's default list_selector_background?

    textView.setMovementMethod(LinkMovementMethod.getInstance());
    textView.setBackgroundResource(android.R.drawable.list_selector_background);

For me it seems to give the result that you want.

UPDATE: After seeing that the item is not just a single TextView

Well, this is not a perfect solution (since it's probably better to fix the TextView - ListView highlighting interaction somehow), but it works well enough.

I figured out that instead of setting the movement method on the TextView (that triggers the issue), it is just simpler to check in the ListView's onItemClick() (after a click is definitely confirmed) to see if we should launch the onClick() on our ClickableSpans:

public class MyActivity extends Activity {

    private final Rect mLastTouch = new Rect();

    private boolean spanClicked(ListView list, View view, int textViewId) {
        final TextView widget = (TextView) view.findViewById(textViewId);
        list.offsetRectIntoDescendantCoords(widget, mLastTouch);
        int x = mLastTouch.right;
        int y = mLastTouch.bottom;

        x -= widget.getTotalPaddingLeft();
        y -= widget.getTotalPaddingTop();
        x += widget.getScrollX();
        y += widget.getScrollY();

        final Layout layout = widget.getLayout();
        final int line = layout.getLineForVertical(y);
        final int off = layout.getOffsetForHorizontal(line, x);

        final Editable buffer = widget.getEditableText();
        final ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);

        if (link.length == 0) return false;

        link[0].onClick(widget);
        return true;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // ...

        listView.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if (spanClicked(listView, view, R.id.details)) return;

                // no span is clicked, normal onItemClick handling code here ..                    
            }
        });
        listView.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    mLastTouch.right = (int) event.getX();
                    mLastTouch.bottom = (int) event.getY();
                }
                return false;
            }
        });

        // ...
    }

}

The spanClicked() method is basically an abbreviated version of the LinkMovementMethod's onTouchEvent() method in the framework. To capture the last MotionEvent coordinates (to check for the click event), we simply add an OnTouchListener on our ListView.

Joe
  • 14,039
  • 2
  • 39
  • 49
  • Can't see how is it possibly helpful, the problem is not only the colors, it's much deeper, thanks for the guess though. – A-Live Jul 22 '12 at 17:53
  • Looks like I didn't really understand your question then :) Do you mind adding the item layout XML to it to make it clearer? – Joe Jul 22 '12 at 17:57
  • Added the item layout to the question, note `descendantFocusability` was set not only in the layout, i tried to set it for the item and listView using every combination as it was probably important, also tried not setting it of course. The important part of the question is that the textView has a `Spannable` content set with `setText (CharSequence text)`. – A-Live Jul 22 '12 at 20:32
  • Good news, with your background resource the text view looks better when clicked outside of the link, of course there're bad things like other views are not displayed as selected, but together with `OnClickListener` it works somehow not as disgusting as before, thank you. Another problem is that when the item is selected outside of the textView the textView selection is different, so that's not a solution yet. – A-Live Jul 23 '12 at 00:28
  • Yeah, it was working good for me because my item consisted only of a single TextView (multiline with embedded ClickableSpan). I tried your layout and can now see the additional problems that you mentioned. I'll update my answer if I found a better solution for you. – Joe Jul 23 '12 at 14:45
  • thanks for update. I'm thinking now about making somehow other views overlapping the textView letting it to cover the item completely. Other than that's an ugly solution there's a problem when the links are selected together with all the text. – A-Live Jul 23 '12 at 15:40
1

Use TextView's setHighlightColor(Color.TRANSPARENT);

biegleux
  • 13,179
  • 11
  • 45
  • 52
  • Doesn't work as I need the textview be clickable and highlighted as a part of the table row; the links inside the textview must also be clickable and ideally only the link style should be changed when it happens, other row parts should not be highlighted at all (or if there's no other way it could be highlighted with a system predefined style - as you see at accepted answer). – A-Live Sep 18 '12 at 06:40
0

I think best way to solve this problem is using a selector. you can chan selection backgroun color so they will seen same

example selector code:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true"
       android:color="#000000" /> <!-- pressed -->
    <item android:state_focused="true"
       android:color="#000000" /> <!-- focused -->
    <item android:color="#FFFFFF" /> <!-- default -->
</selector>

implement sth like that to your link.

let me know about result

bmavus
  • 892
  • 1
  • 7
  • 21
  • The item itself still won't be shown selected, just as it's subviews i'm afraid, this way i'll likely be able to show the part of the item is selected and i want the UI to be solid with the item being selected completely. – A-Live Jul 19 '12 at 12:24
  • you can also implement selector to subview – bmavus Jul 19 '12 at 12:34
  • As you can see at the second image the item and other subviews are shown selected fine when the selection is passed correctly. – A-Live Jul 19 '12 at 12:36
0

Quote from documentation of the setMovementMethod() of TextView

Sets the movement method (arrow key handler) to be used for this TextView. This can be null to disallow using the arrow keys to move the cursor or scroll the view.

Be warned that if you want a TextView with a key listener or movement method not to be focusable, or if you want a TextView without a key listener or movement method to be focusable, you must call android.view.View.setFocusable(boolean) again after calling this to get the focusability back the way you want it.

The second paragraph explains that we should explicitly call the setFocusable method after setting the LinkMovementMethod to get focusability working the way we want.

You need to do it in code after inflating the list item. Once you are able to get the focus, you can set the selector to have a different color text.

Here is the source code of setMovementMethod.

Ron
  • 24,175
  • 8
  • 56
  • 97
  • I've read the documentation and explicitly set `focusable` to false after `setMovementMethod` without any effect. As i said in the comment above, that is unlikely that the text view itself is getting the focus as it's onFocusChangeListener is not fired no matter do i set it not focusable or not, probably because i was using `FOCUS_BLOCK_DESCENDANTS` for the listView. – A-Live Jul 20 '12 at 11:59