What i did so far is a list view of textviews having the normal text and clickable spans:
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:
Now the problem is:
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.
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.