TextView selection with Spannable and LinkMovementMethod - android

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.

Use TextView's setHighlightColor(Color.TRANSPARENT);

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

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.

Related

Android ListView unselect item

I am developing an application that have a ListView. In this ListView, when I touch in one item the background color of this item changes, to show that the item is selected.
Howerver, the layout have an EditText too:
<ListView
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="#+id/pratosListView"
android:layout_marginBottom="#dimen/activity_vertical_margin"
android:layout_weight="0.8"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:id="#+id/quantidadeEditText"
android:layout_marginRight="#dimen/activity_horizontal_margin"
style="#style/EditText"/>
and when I touch the EditText the item selected in ListView is unselected, in other words, the background color back to the original color. I don't want this happens.
This is my selector (list_item_selector.xml):
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="#drawable/item_list_selected"/>
<item android:state_pressed="true" android:drawable="#drawable/item_list_pressed"/>
<item android:state_focused="true" android:drawable="#drawable/item_list_focused"/>
<item android:drawable="#drawable/item_list_normal"/>
</selector>
Here I set the background of the item (item_lista_pedidos.xml):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="#dimen/activity_all_magim"
android:orientation="horizontal"
android:weightSum="5"
android:baselineAligned="false"
android:background="#drawable/list_item_selector">
.....
</LinearLayout>
And here is the implementation of the click listener:
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// salva a posicao do item selecionado
lastPositionSelected = position;
// salva o objeto relativo ao item selecionado
servicoSelected = servicos.get(position);
view.setSelected(true);
}
Someone have some idea to avoid the item to be unselected when I touch in EditText?
As mentioned here, If you want to detect outside touch only outside of EditText, you can detect touch event in the containing view, and then, given you have your EditText view:
Rect editTextRect = new Rect();
myEditText.getHitRect(editTextRect);
if (!editTextRect.contains((int)event.getX(), (int)event.getY())) {
//touch was outside edittext
}
Or you add a touch listener both to the EditText and the container, and return false in the one of the EditText, this way it will be intercepted and not forwarded to the parent. So, all the touches you detect in the listener of the parent, will not belong to the EditText.

Move layout up when soft keyboard is shown

I'm trying to adjust the layout when the soft keyboard appears after an edit text gets focus. Right now if I have many edit text and the keyboard appears, the last edit text are hidden and I can't scroll up.
This is how my layout is builded up:
Template:
<LinearLayout>
<LinearLayout>
// header 1
</LinearLayout>
<LinearLayout>
// header 1
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:orientation="vertical">
// where I inflate view_1
</LinearLayout>
<LinearLayout>
// footer
</LinearLayout>
</LinearLayout>
View (view_1):
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:focusableInTouchMode="true">
<LinearLayout>
// ...
</LinearLayout>
<LinearLayout>
// ...
</LinearLayout>
<LinearLayout>
<TextView/>
<EditText/>
<TextView/>
<EditText/>
<TextView/>
<EditText/>
<TextView/>
<EditText/>
</LinearLayout>
</LinearLayout>
</ScrollView>
I already try all kinds of combinations of android:windowSoftInputMode (on manifest.xml and programmatically). I tried to set android:isScrollContainer="false" on the scroll view, but nothing.
I also tried this answer, putting an GlobalLayoutListener in my scroll view, but the onGlobalLayout is not called when the keyboard appears. And the isKeyboardShown is always false.
The best solution I found is to add adjustpan property in the activity<> tag in the manifest.xml file .
<activity
android:name="MyActivity"
android:windowSoftInputMode="adjustPan"/>
I ended up doing it my way.
I created a class that implements OnFocusChangeListener to handle all my EditText:
public class EditTextFocusChangeListener implements OnFocusChangeListener {
private ScrollView scrollView;
public EditTextFocusChangeListener(ScrollView scrollView) {
this.scrollView = scrollView;
}
#Override
public void onFocusChange(View view, boolean hasFocus) {
if(hasFocus) {
int left = view.getLeft();
int top = view.getTop();
int bottom = view.getBottom();
int keyboardHeight = scrollView.getHeight() / 3;
// if the bottom of edit text is greater than scroll view height divide by 3,
// it means that the keyboard is visible
if (bottom > keyboardHeight) {
// increase scroll view with padding
scrollView.setPadding(0, 0, 0, keyboardHeight);
// scroll to the edit text position
scrollView.scrollTo(left, top);
}
}
}
}
Then in the activity, I setted the listener for each edit text:
EditTextFocusChangeListener listener = new EditTextFocusChangeListener(mainScrollView);
editText1 = (EditText) findViewById(R.id.editText1);
editText1.setOnFocusChangeListener(listener);
editText2 = (EditText) findViewById(R.id.editText2);
editText2.setOnFocusChangeListener(listener);
...
editTextN = (EditText) findViewById(R.id.editTextN);
editTextN.setOnFocusChangeListener(listener);
And for the last edit text, I setted an EditorAction listerner to handle the 'Done' button on soft keyboard - to hide the keyboard and put the scroll view back to its original position:
editTextN.setOnEditorActionListener(new OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
int result = actionId & EditorInfo.IME_MASK_ACTION;
switch(result) {
// user taped on keyboard DONE button
case EditorInfo.IME_ACTION_DONE:
// put the scroll view back to its original position
mainScrollView.setPadding(0, 0, 0, 0);
// hide keyboard
((InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(v.getWindowToken(), 0);
// remove focus from any edit text
LinearLayout scrollViewLL = (LinearLayout) mainScrollView.getChildAt(0);
scrollViewLL.requestFocus();
break;
}
return false;
}
});
And finally, a way to handle when the user touches outside an edit text to hide the keyboard and put the scroll view back to its original position (found this on web and changed a little to fit my needs):
public void setupUI(View view) {
// Set up touch listener for non-text box views to hide keyboard.
if (!(view instanceof EditText)) {
view.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// put the scroll view back to its original position
if (v instanceof ScrollView) {
v.setPadding(0, 0, 0, 0);
LinearLayout scrollViewLL = (LinearLayout) ((ScrollView) v).getChildAt(0);
scrollViewLL.requestFocus();
}
hideKeyboard();
return false;
}
});
}
// If a layout container, iterate over children and seed recursion.
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
View innerView = ((ViewGroup) view).getChildAt(i);
setupUI(innerView);
}
}
}
This is a late answer, but it may be helpful for anyone that is still looking for an alternative solution. I created a custom ViewTreeObserver.OnGlobalLayoutListener that may fit your use case if you're looking for a way to control the position of the View that you want to ensure is visible when the soft keyboard is shown. Here is a gist to that solution.
The OnGlobalLayoutListener animates changes to the view's translationY property by smoothly moving the view just above the soft keyboard bounds when the keyboard is shown and back to the view's starting position when the the keyboard is dismissed. Let me know if you have any questions on usage.
Put all of your top code in ScrollView, not just view_1. This allows you to move all the parent layout on click by any child EditText.
EDIT: view_1 in this case MUST NOT contains ScrollView!
If you create the Activity using Android Studio Basic Activity wizard (with CoordinatorLayout and theme="#style/AppTheme.NoActionBar"), the default behavior is adjustPan, where the top portion of the activity is push offscreen and the EditText is shown above the Keyboard. You can also change it to adjustResize where the top portion of the activity is maintained.
Edit AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
<application ...>
<activity
android:name=".TestInputActivity"
android:label="#string/title_activity_test_input"
android:windowSoftInputMode="adjustResize"
android:theme="#style/AppTheme.NoActionBar">
</activity>
</application>
</manifest>
Keep in mind though the effect and behavior might differ slightly if you are using Scrolling Activity, such as NestedScrollView.
https://code.luasoftware.com/tutorials/android/move-layout-when-keyboard-shown/
android:weightSum="1"
add this
The below code is working for me. Just try this example:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/RelativeAdd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context="com.example.scrollview.MainActivity">
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center">
<EditText
android:id="#+id/editTextUserName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="100dp"
android:ems="10"
android:inputType="textPersonName"
android:hint="Name" />
<EditText
android:id="#+id/address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignEnd="#+id/editTextUserName"
android:layout_alignLeft="#+id/editTextUserName"
android:layout_alignRight="#+id/editTextUserName"
android:layout_alignStart="#+id/editTextUserName"
android:layout_below="#+id/editTextUserName"
android:layout_marginTop="20dp"
android:ems="10"
android:inputType="textPersonName"
android:hint="Address" />
<Button
android:id="#+id/buttonLogin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/address"
android:layout_centerHorizontal="true"
android:layout_marginTop="47dp"
android:text="Button" />
</RelativeLayout>
</ScrollView>
</RelativeLayout>
In manifest.xml add these line:
android:theme="#style/AppTheme"
android:windowSoftInputMode="stateHidden|adjustPan"
Declare AppTheme in style.xml as per your theme requirement. Then if you do not need keyboard comes up while page loads, you can add below line in activity:
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
Happy Coding :-)

Customized Android Widget

I need to create a Widget which look like photo below:
Upper part of photo resembles normal state of my costume widget and lower part of it resembles highlight state of the widget.
Widget primary features:
Both Normal and Highlighted sates could have a background with image and color at same time.
It could re-size by width easily (for example 200dp or 100dp)
I have 3 questions:
Is there any particular widget which could help me to create something like what I said?
Or should I create a custom widget myself?
Create a custom layout with a Vertical LinearLayout and two child TextView for Title and description.
Set the description TextView visibility as GONE , since you need to show the description only on highlight state.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layout_highlight"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#drawable/image_shape">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Title"
android:textStyle="bold"
android:textSize="30dp"
android:layout_margin="10dp"/>
<TextView
android:id="#+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello! Iam here to display the description"
android:textStyle="italic"
android:textSize="25dp"
android:layout_margin="5dp"
android:visibility="gone"/>
</LinearLayout>
Now, on the code change the description TextView visibility as visible on long click.
LinearLayout linearLayout = (LinearLayout)findViewById(R.id.layout_highlight);
final TextView descriptionTextView = (TextView)findViewById(R.id.description);
linearLayout.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
descriptionTextView.setVisibility(View.VISIBLE);
return true;
}
});
Also , you need to hide back the description when long click is released. so you have to have onTouch listener like this..
linearLayout.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
descriptionTextView.setVisibility(View.GONE);
}
return false;
}
});

Textview onClick is being called instead of the Listview onItemClick.

OK I've searched and seen similar issues, tried them and no avail. I have a listView with some elements and I want to click on one element and display a detail somewhere else.
This is my listView
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pedidos);
ListView listView = (ListView) findViewById(R.id.actualStoresList);
Model.initialize();
Vector<String> values = Model.stores;
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapter, View view, int position,
long id) {
Object o = adapter.getItemAtPosition(position);
String str_text = o.toString();
Log.i("", "I have selected this: " + str_text);
}
});
CustomStringAdapter adapter = new CustomStringAdapter(this, R.layout.my_list_layout, R.id.list_content, values);
listView.setAdapter(adapter);
}
This is the my_list_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:gravity="center_vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:id="#+id/list_content"
android:textColor="#000000"
android:gravity="center"
android:layout_margin="4dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="20dp"/>
</LinearLayout>
The list gets displayed correctly. When I click on an element of the list (A textView) it "steals" the click event so nothing happens (the onItemClickListener is attached to the listView, not the TextView).
The textView has an small margin where, if careful, I can click just behind it, in fact, touching the listView. In this case, the event gets fired ok and I see the log.
I've tried to set the TextView android:focusable="false" but still, the TextView is "above" of the listView and always gets the click events.
How can I either make the TextView "transparent" so it actually clicks on the listView, or add a onclickListener to the TextView so I can handle its events?
Thanks!
Alejandro
Setting clickable property of TextView to false should solve this problem. Try this:
<TextView
android:id="#+id/list_content"
android:textColor="#000000"
android:gravity="center"
android:layout_margin="4dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="20dp"
android:clickable="false" />
To make items not focusable, do this:
listView.setItemsCanFocus(false);
source
I think this will solve your issue.
Also make sure the
android:textIsSelectable
property is not set to true.

ListItem should change bg color, color of text and images on it while pressed

I have a ListView, where each item has a custom LinearLayout with a bg image, a textView and 2 imageViews.
Now I need that while the user is touching the item, all of those switch to the "pressed" state:
the bg image of the LiearLayout must be replaced with another one
the TextView should change textColor
both ImageViews in the item should switch to alternative images
Normally such stuff would be done using an xml resource with selector inside, e.g. the LinearLayout would use a drawable with selector inside for background, the TextView a drawable with selector and colors for textColor, and ImageViews use selector with images inside for src.
The problem is that the pressed state is only detected by the LinearLayout and not by the child views (?), so only the background image changes.
I've tried implementing this using OnTouchListener, but then comes the problem that I can't securely get access to Views inside the list item.
I tried caching the view which I return in getView() of the list item to then later change the images and text color. This works usually, but e.g. if one of the list items opens another activity, then the view somehow gets lost and the highlighted state stays indefinitely. I've tried debugging and it works correctly if I step thru with the debugger.
Also, reusing the cachedView seems to bring no good and messes things up completely, so I'm just inflating a new view for the list item each time (this must be inefficient).
Just in case, here is the code of the custom list item item i'm using for the custom list adapter:
public class MyListItem extends AbstractListItem
{
private int iconResource, iconHighlightedResource;
private int textResource;
private View.OnClickListener onClickListener;
private LinearLayout currentView;
private ImageView imgIcon;
private TextView txtText;
private ImageView imgArrow;
private boolean bIsHighlighted;
public MyListItem(int iconResource, int iconHighlightedResource, int textResource, View.OnClickListener onClickListener)
{
this.iconResource = iconResource;
this.iconHighlightedResource = iconHighlightedResource;
this.textResource = textResource;
this.onClickListener = onClickListener;
}
public View getView(View cachedView)
{
this.currentView = buildView();
populateView();
update();
return this.currentView;
}
private LinearLayout buildView()
{
LayoutInflater inflater = (LayoutInflater)App.get().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return (LinearLayout)inflater.inflate(R.layout.my_menu_item, null);
}
private void populateView()
{
this.imgIcon = (ImageView)this.currentView.findViewById(R.id.img_menu_item_icon);
this.txtText = (TextView)this.currentView.findViewById(R.id.txt_menu_item_text);
this.txtText.setText(this.textResource);
this.txtText.setTypeface(App.fontCommon);
this.imgArrow = (ImageView)this.currentView.findViewById(R.id.img_menu_item_arrow);
this.currentView.setOnClickListener(this.onClickListener);
this.currentView.setOnTouchListener(this.highlighter);
}
private View.OnTouchListener highlighter = new View.OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
int nAction = event.getAction();
int nActionCode = nAction & MotionEvent.ACTION_MASK;
switch (nActionCode)
{
case MotionEvent.ACTION_DOWN:
bIsHighlighted = true;
update();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
bIsHighlighted = false;
update();
break;
}
return false;
}
};
private void update()
{
if (this.bIsHighlighted)
{
updateForHighlightedState();
}
else
{
updateForNormalState();
}
}
private void updateForHighlightedState()
{
Resources r = App.get().getResources();
this.currentView.setBackgroundResource(R.drawable.button_beveled_m_call_to_action_taking_input);
this.imgIcon.setImageResource(this.iconHighlightedResource);
this.txtText.setTextColor(r.getColor(R.color.white));
this.imgArrow.setImageResource(R.drawable.arrow_highlighted);
}
private void updateForNormalState()
{
Resources r = App.get().getResources();
this.currentView.setBackgroundColor(r.getColor(R.color.white));
this.imgIcon.setImageResource(this.iconResource);
this.txtText.setTextColor(r.getColor(R.color.text_dark));
this.imgArrow.setImageResource(R.drawable.arrow);
}
}
Here is the layout file (xml):
<?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="wrap_content"
android:orientation="horizontal"
android:background="#color/white"
android:gravity="center_vertical"
android:padding="5dp" >
<ImageView
android:id="#+id/img_menu_item_icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="#drawable/info" />
<TextView
android:id="#+id/txt_menu_item_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="24dp"
android:text="Menu item"
android:textColor="#color/text_dark"
android:layout_marginLeft="5dp" />
<ImageView
android:id="#+id/img_menu_item_arrow"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="#drawable/arrow" />
</LinearLayout>
After lots of experimenting finally this worked:
Every child view inside the list item layout must have android:duplicateParentState="true".
Then all of them can just use selector drawables. No extra effort inside the code is required.
<?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="wrap_content"
android:orientation="horizontal"
android:background="#drawable/my_item_bg"
android:gravity="center_vertical"
android:padding="5dp" >
<ImageView
android:id="#+id/img_menu_item_icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="#drawable/selector_info"
android:duplicateParentState="true" />
<TextView
android:id="#+id/txt_menu_item_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="24dp"
android:text="Menu item"
android:textColor="#drawable/selector_color_my_button_text"
android:layout_marginLeft="5dp"
android:duplicateParentState="true" />
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:src="#drawable/selector_arrow"
android:duplicateParentState="true" />
</LinearLayout>
You should create custom drawable selectors, and set them as the background to your listview element.
Step#1, create another layout (named: layout_selected for this example), with the appropriate background color for your pressed state (like the layout file you supplied, but with the background attribute of the linear set to another color).
Then you will define a drawable selector, which will be placed in your drawable folder), defining which background should be use in which instance. This will look something like this:
<!-- pressed -->
<item android:drawable="#drawable/layout_selected" android:state_pressed="true"/>
<!-- focused -->
<item android:drawable="#drawable/layout_normal" android:state_focused="true"/>
<!-- default -->
<item android:drawable="#drawable/layout_normal"/>
Finally, to use this in your list, when you set the layout for your adapter, set it to the selector we just created, instead of your standard layout.
Maybe a little hard to explain, but you want to use 'Drawable Selectors' to accomplish what you want.
I would suggest to add ViewHolder pattern for listview. This will optimize your listview drawing & creating UI.
Also in that we can use setTag to save instance of row. In that you can handle touch event.

Categories

Resources