Background Selector in RecyclerView Item - android

I'm using the RecyclerView like below:
<android.support.v7.widget.RecyclerView
android:id="#+id/list"
android:layout_width="320dp"
android:layout_height="match_parent"/>
and my list item:
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/selector_medium_high">
<com.basf.suvinil.crie.ui.common.widget.CircleView
android:id="#+id/circle"
android:layout_width="22dp"
android:layout_height="22dp"/>
<TextView
android:id="#+id/label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="57.5dp"/>
</LinearLayout>
see in detail this part android:background="#drawable/selector_medium_high" it's a normal selector:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/background_high" android:state_activated="true"/>
<item android:drawable="#color/background_high" android:state_pressed="true"/>
<item android:drawable="#color/background_high" android:state_checked="true"/>
<item android:drawable="#color/background_high" android:state_focused="true"/>
<item android:drawable="#color/background_medium"/>
</selector>
but when I run this code, i have no changes in background color when I touch the row....

Set clickable, focusable, focusableInTouchMode to true in all elements of RecyclerView "list".

Add :
android:background="?android:attr/selectableItemBackground"
in item.xml

If nothing of this works for you, like it didn't for me, use this code:
android:foreground="?android:attr/selectableItemBackground"
The trick is in android:foreground attribute...

Adding android:background="?android:attr/selectableItemBackground" to my_list_item.xml's root layout seems to work for me (assuming you want the default selection colour).
Also make sure the root layout's android:layout_width is match_parent rather than wrap_content to ensure that the whole row is selectable.

Unfortunately using focusable views to simulate item selection is not a good solution because:
Focus is lost when notifyDataSetChanged is called
Focus is problematic when child views are focusable
I wrote a base adapter class to automatically handle item selection with a RecyclerView. Just derive your adapter from it and use drawable state lists with state_selected, like you would do with a list view.
I have a Blog Post Here about it, but here is the code:
public abstract class TrackSelectionAdapter<VH extends TrackSelectionAdapter.ViewHolder> extends RecyclerView.Adapter<VH> {
// Start with first item selected
private int focusedItem = 0;
#Override
public void onAttachedToRecyclerView(final RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
// Handle key up and key down and attempt to move selection
recyclerView.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();
// Return false if scrolled to the bounds and allow focus to move off the list
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
return tryMoveSelection(lm, 1);
} else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
return tryMoveSelection(lm, -1);
}
}
return false;
}
});
}
private boolean tryMoveSelection(RecyclerView.LayoutManager lm, int direction) {
int tryFocusItem = focusedItem + direction;
// If still within valid bounds, move the selection, notify to redraw, and scroll
if (tryFocusItem >= 0 && tryFocusItem < getItemCount()) {
notifyItemChanged(focusedItem);
focusedItem = tryFocusItem;
notifyItemChanged(focusedItem);
lm.scrollToPosition(focusedItem);
return true;
}
return false;
}
#Override
public void onBindViewHolder(VH viewHolder, int i) {
// Set selected state; use a state list drawable to style the view
viewHolder.itemView.setSelected(focusedItem == i);
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
// Handle item click and set the selection
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Redraw the old selection and the new
notifyItemChanged(focusedItem);
focusedItem = mRecyclerView.getChildPosition(v);
notifyItemChanged(focusedItem);
}
});
}
}
}

viewHolder.mRlPrince.setOnTouchListener(new View.OnTouchListener() {
#Override public boolean onTouch(View v, MotionEvent event) {
if (event.getAction()==MotionEvent.ACTION_DOWN){
viewHolder.mRlPrince.setBackgroundColor(Color.parseColor("#f8f8f8"));
}if (event.getAction()==MotionEvent.ACTION_UP){
viewHolder.mRlPrince.setBackgroundColor(Color.WHITE);
}
return false;
}
});

Add this below attribute in your Item "my_list_item.xml"
android:descendantFocusability="blocksDescendants"
This works for me !

You need to set android:clickable="true" in the element xml, and if you have more selectors in some view inside your view, you need to set android:duplicateParentState="true"
there too.
Thats works on pre honeycomb apis.

As many others answered the only way is to combine selectors and new Classes to keep track of the selection, but better to delegate this calculation to the Adapter. The library FlexibleAdapter keeps track of the selections for you, configuration change is also compatible.
Backgrounds color with ripple can now be done without XML part, but in the code to manage dynamic data.
Finally, you can use lot of features with the same library, selection is a tiny basic feature you can have.
Please have a look at the description, Wiki pages and full working example: https://github.com/davideas/FlexibleAdapter

on your design or xml file just place the following lines.
android:clickable="true"
android:focusable="true"
android:background="?android:attr/activatedBackgroundIndicator"

Related

Multiple EditText in ListView, tap to focus on one EditText and focus jumps to the first

I've got EditTexts in my rows in a ListView. When I tap on one of the EditTexts the soft keyboard appears and the focus jumps to the first EditText in the list instead of staying in the field where I tapped.
Here is a video of it:
https://youtu.be/ZwuFrX-WWBo
I created a completely stripped down app to demonstrate the problem. The full code is here: https://pastebin.com/YT8rxqKa
I'm not doing anything to alter the focus in my code:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.cell_textfield, parent, false);
}
TextView label = (TextView) convertView.findViewById(R.id.textview1);
EditText textfield = (EditText) convertView.findViewById(R.id.textview2);
String text = String.format("Row %d", position);
label.setText(text);
textfield.setText(text);
return convertView;
}
I found another post on StackOverflow giving a workaround for this dumb Android behavior, which involves putting an OnFocusChangedListener on all of the textfields so they can retake focus if it's taken from them improperly.
That worked to regain focus, but then I discovered that when a textfield retakes focus the cursor ends up at the start of the text instead of end, which is unnatural and annoying to my users.
Here is a video of that:
https://youtu.be/A35wLqbuIac
Here's the code for that OnFocusChangeListener. It works to fight the stupid Android behavior of moving focus, but the cursor is misplaced after it regains focus.
View.OnFocusChangeListener onFocusChangeListener = new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View view, boolean hasFocus) {
long t = System.currentTimeMillis();
long delta = t - focusTime;
if (hasFocus) { // gained focus
if (delta > minDeltaForReFocus) {
focusTime = t;
focusTarget = view;
}
}
else { // lost focus
if (delta <= minDeltaForReFocus && view == focusTarget) {
focusTarget.post(new Runnable() { // reset focus to target
public void run() {
Log.d("BA", "requesting focus");
focusTarget.requestFocus();
}
});
}
}
}
};
I hate having to put a bandaid on a bandaid on a bandaid to try to get Android to just behave as it would naturally be expected to behave, but I'll take what I can get.
1) Is there something I can do to fix this problem at the source and not have to have the OnFocusChangeListener at all?
2) If (1) isn't possible, then how can I make sure that when I force focus back to the correct field that I make sure the cursor is placed at the end? I tried using setSelection() right after requestFocus() but since the textfield wasn't yet focused the selection is ignored.
Here was my "solution." In short: ListViews are stupid and will always be a total nightmare when EditTexts are involved, so I changed my Fragment/Adapter code to be able to adapt to either a ListView layout or a ScrollView layout. It only works if you have a small number of rows, because the scrollview implementation isn't able to take advantage of lazy-loading and view recycling. Thankfully, any situation wherein I want EditTexts in a ListView, I rarely have more than 20 rows or so.
When inflating my view in my BaseListFragment, I get my layout id via a method that relies on a hasTextFields() method:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(getLayoutId(), container, false);
return view;
}
public boolean hasTextfields() {
return false;
}
public int getLayoutId() {
if (hasTextfields()) {
return R.layout.scrollfragment;
} else {
return R.layout.listfragment;
}
}
In my various subclasses of my BaseListFragment, if I need to have an EditText in one of my fields, I just override the hasTextFields() method to return true and then my fragment/adapter switchs over to using the basic scrollview implementation.
From there, it's a matter of making sure that the Adapter handles the standard ListView actions for both the ListView and the ScrollView scenarios. Like this:
public void notifyDataSetChanged() {
// If scrollContainer is not null, that means we're in a ScrollView setup
if (this.scrollContainer != null) {
// intentionally not calling super
this.scrollContainer.removeAllViews();
this.setupRows();
} else {
// use the real ListView
super.notifyDataSetChanged();
}
}
public void setupRows() {
for (int i = 0; i < this.getCount(); i++) {
View view = this.getView(i, null, this.scrollContainer);
view.setOnClickListener(myItemClickListener);
this.scrollContainer.addView(view);
}
}
One issue that the click listener presented is that a ListView wants an AdapterView.OnItemClickListener, but arbitrary Views inside a ScrollView want a simple View.OnClickListener. So, I made my ItemClickListener also implement View.OnClickListener and then just dispatched the OnClick to the OnItemClick method:
public class MyItemClickListener implements AdapterView.OnItemClickListener, View.OnClickListener {
#Override
public void onClick(View v) {
// You can either have your Adapter set the tag on the View to be its position
// or you could have your click listener use v.getParent() and iterate through
// the children to find the position. I find its faster and easier to have my
// adapter set the Tag on the view.
int position = v.getTag();
this.onItemClick(null, v, config.getPosition(), 0);
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// ...
}
}
Then in MyEditTextListFragment, I create the adapter like this:
listener = createClickListener();
adapter = createListAdapter();
if (scrollContainer != null) {
adapter.setScrollContainer(scrollContainer);
adapter.setMenuItemClickListener(listener);
adapter.setupRows();
} else {
getListView().setOnItemClickListener(listener);
getListView().setAdapter(adapter);
}
Here is my scrollfragment.xml for reference:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff"
android:clickable="true"
>
<!--
The following LinearLayout as a focus catcher that won't cause the keyboard to
show without it, the virtual keyboard shows up immediately/always which means we
never get to the enjoy the full size of our screen while scrolling, and
that sucks.
-->
<LinearLayout
android:focusable="true"
android:focusableInTouchMode="true"
android:layout_width="0px"
android:layout_height="0px"/>
<!--
This ListView is still included in the layout but set to visibility=gone. List
fragments require a standard ListView in the layout, so this gets us past that
check and allows us to use the same adapter code in both listview and scrollview
situations.
-->
<ListView android:id="#id/android:list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:drawSelectorOnTop="false"
android:background="#null"
android:layout_alignParentTop="true"
android:descendantFocusability="afterDescendants"
android:visibility="gone"
/>
<!--
This scrollview will act as our fake listview so that we don't have to deal with
all the stupid crap that comes along with having EditTexts inside a ListView.
-->
<ScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="afterDescendants"
>
<LinearLayout
android:id="#+id/scrollContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
</LinearLayout>
</ScrollView>
</RelativeLayout>
Try this once, it worked for me:
public void setCursorPosition() {
focusTarget.requestFocus();
focusTarget.setCursorVisible(true);
other.setCursorVisible(false);
} else {
other.setCursorVisible(true);
focusTarget.setCursorVisible(false);
}
}

Android Set button color on first click, unset on second click

please i need help on this.
I have searched here but the answers i have seen are not working for me, the posts being old, the functions are mostly deprecated.
I am trying to set the color of buttons on a single click in order to highlight them and unset the color on a second click. It's like making some choice from a number of buttons, and if I click on a selected button again maybe after changing my mind on my selection, the color should revert to the default. So that i am only left with the selected buttons highlighted.
The buttons are generated with an adapter in gridview and the onclicklistener applies to all of them.
The code i'm using is as shown:
public class ButtonAdapter extends BaseAdapter {
private Context context;
public View getView(int position, View convertView, ViewGroup parent)
{
final Button btn;
if (convertView == null) {
btn = new Button(context);
btn.setLayoutParams(new GridView.LayoutParams(40, 40));
btn.setPadding(2, 2, 2, 2);
}
else {
btn = (Button) convertView;
}
//exus
btn.setText(Integer.toString(gridNumbers[position]));
btn.setTextColor(Color.BLACK);
btn.setId(position);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
//btn.setBackgroundColor(Color.GREEN);
//Toast.makeText(getBaseContext(), "Button clicked", Toast.LENGTH_LONG).show();
if (v.getSolidColor()!=Color.GREEN)
{
btn.setBackgroundColor(Color.GREEN);
}
else
{
btn.setBackgroundColor(Color.GRAY);
}
}
});
return btn;
}
}
}
My XML:
<GridView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/gridview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:numColumns="8"
android:columnWidth="20dp"
android:stretchMode="columnWidth"
android:gravity="center" />
You can use a list of boolean properties instead of doing this.
Set a public boolean list in your class (it should be public and outside of any functions otherwise the onclicklistener will have error)
List<boolean> blist=new Arraylist<boolean>(Size);
//Size is maximum number of buttons
int index;
Then whenever you create a new button add this:
blist.add(index,false);
index++;
in the onclicklistener; find the index of the button from its position and save the index in an integer named pos.
if(blist.get(pos)==false)
{
//not clicked yet
blist.remove(pos);
blist.add(pos,true);
//here write the code u need for this if
}
else
{
blist.remove(pos);
blist.add(pos,false);
//todo: ur code for else
}
I tried this way and it worked for me,if you want to change on click
counter = 1;
//By Default set color
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (counter == 1)
{
// Default color
counter = 2;
}
else
{
//your color
counter = 1;
}
}
});
View reused in GridView. So, you should define state for your buttons in your base adapters.
Take an ArrayList that will hold your selected index and remove it when grid is not selected.
Ex:
ArrayList<Integer> selectedItems;
In Construtor
selectedItems = new ArrayList<Integer>();
In OnClickListener
public void onClick(View v) {
if (selectedItems.contains(new Integer(position))) {
selectedItems.remove(new Integer(position));
notifyDataSetChanged();
} else {
selectedItems.add(new Integer(position));
notifyDataSetChanged();
}
}
In getView():
if (selectedItems.contains(new Integer(position))) {
btn.setBackgroundColor(Color.GREEN);
} else {
btn.setBackgroundColor(Color.GRAY);
}
Use toggle button insted of normal button. Like
<ToggleButton
android:id="#+id/toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/check" //check.xml
android:layout_margin="10dp"
android:textOn=""
android:textOff=""
android:focusable="false"
android:focusableInTouchMode="false"
android:layout_centerVertical="true"/>
and then make an xml file check.xml in drawable folder something like
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- When selected, use grey -->
<item android:drawable="#drawable/selected_image"
android:state_checked="true" />
<!-- When not selected, use white-->
<item android:drawable="#drawable/unselected_image"
android:state_checked="false"/>
</selector>
refrence.

How to handle clicks on the rows of listView, while notifyDataSetChanged might be called?

Background
I have a complex adapter for a listView.
Each row has some inner views that need to be clickable (and hande the clicks), and they also have selectors (to show the effect of touching).
on some cases, notifyDataSetChanged() needs to be called quite frequently (for example once/twice a second), to show some changes on the listView's items.
As an example, consider seeing a list of downloading files, where you show the user the progress of each file being downloaded.
The problem
Each time notifyDataSetChanged is called, the touch event is lost on the touched view, so the user can miss clicking on it , and especially miss long clicking on it.
Not only that, but the selector also loses its state, so if you touch it and see the effect, when the notifyDataSetChanged is called, the view loses its state and you see it as if it isn't get touched.
This happens even for views that have nothing in them being updated (meaning I just return the convertView for them) .
Sample code
The code below demonstrates the problem. It is not the original code but a super short sample to make it clear what I'm talking about.
Again, this is not the original code, so I've removed the ViewHolder usage and taking care of the clicking to do some operations, in order to make it simple to read and understand. But it's still the same logic.
Here's the code:
MainActivity.java
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ListView listView = (ListView) findViewById(R.id.listView);
final BaseAdapter adapter = new BaseAdapter() {
#Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
TextView tv = (TextView) convertView;
if (tv == null) {
tv = new TextView(MainActivity.this);
tv.setBackgroundDrawable(getResources().getDrawable(R.drawable.item_background_selector));
tv.setOnClickListener(new OnClickListener() {
#Override
public void onClick(final View v) {
android.util.Log.d("AppLog", "click");
}
});
}
//NOTE: putting the setOnClickListener here won't help either.
final int itemViewType = getItemViewType(position);
tv.setText((itemViewType == 0 ? "A " : "B ") + System.currentTimeMillis());
return tv;
}
#Override
public int getItemViewType(final int position) {
return position % 2;
}
#Override
public long getItemId(final int position) {
return position;
}
#Override
public Object getItem(final int position) {
return null;
}
#Override
public int getCount() {
return 100;
}
#Override
public boolean areAllItemsEnabled() {
return false;
}
#Override
public boolean isEnabled(final int position) {
return false;
}
};
listView.setAdapter(adapter);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
// fake notifying
adapter.notifyDataSetChanged();
android.util.Log.d("AppLog", "notifyDataSetChanged");
handler.postDelayed(this, 1000);
}
}, 1000);
}
}
item_background_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"><shape>
<solid android:color="#android:color/holo_blue_light" />
</shape></item>
<item android:state_focused="true"><shape>
<solid android:color="#android:color/holo_blue_light" />
</shape></item>
<item android:drawable="#android:color/transparent"/>
</selector>
activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.test.MainActivity"
tools:ignore="MergeRootFrame" >
<ListView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
</FrameLayout>
Partial solution
It's possible to update only the needed views, by finding the view and then calling getView on it, but this is a workaround.
Also, it won't work in the case of adding/removing items from the listView, which needs to have notifyDataSetChangedbeing called. Plus it also makes the updated view to lose its touching state.
EDIT: even the partial solution doesn't work. Maybe it's causing a layout of the entire listView, which causes the other views to lose their states.
The question
How can I let views stay "in sync" with the touch events after calling notifyDataSetChanged() ?
You are not updating click listener of "Recycled" views.
Place tv.setOnClickListener() out of the if (tv == null) check.
Also, the properties you want to "stay synced" should be in the model backing the ListView. Never trust Views to hold important data, they should only reflect data from model.
class Item{
String name;
boolean enabled;
boolean checked
}
class ItemAdapter extends ArrayAdapter<Item>{
#Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
if(convertView == null){
// create new instance
}
// remove all event listeners
Item item = getItem(position);
// set view properties from item (some times, old event listeners will fire when changing view properties , so we have cleared event listeners above)
// setup new event listeners to update properties of view and item
}
}
As mentioned you can use the View.setOnTouchListener() and catch the ACTION_DOWN and the ACTION_UP Event.
For the Selector Animation you can use your Custom Color Animation. Here is an example for changing the backgroundColor with an Animation.
Integer colorFrom = getResources().getColor(R.color.red);
Integer colorTo = getResources().getColor(R.color.blue);
ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
colorAnimation.addUpdateListener(new AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animator) {
view.setBackgroundColor((Integer)animator.getAnimatedValue());
}
});
colorAnimation.start();
Alternative solution
EDIT (by the OP, meaning the thread-creator): An alternative solution, based on the above, is to use the touchListener to set the state of the background of the view.
The disadvantage of it (though it should be fine for most cases) is that if the list gets more/less items, the touch is lost (by ACTION_CANCEL) , so, if you wish to handle it too, you could use this event and handle it yourself using your own logic.
Even though the solution is a bit weird and doesn't handle all possible states, it works fine.
Here's a sample code:
public static abstract class StateTouchListener implements OnTouchListener,OnClickListener
{
#Override
public boolean onTouch(final View v,final MotionEvent event)
{
final Drawable background=v.getBackground();
// TODO use GestureDetectorCompat if needed
switch(event.getAction())
{
case MotionEvent.ACTION_CANCEL:
background.setState(new int[] {});
v.invalidate();
break;
case MotionEvent.ACTION_DOWN:
background.setState(new int[] {android.R.attr.state_pressed});
v.invalidate();
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
background.setState(new int[] {});
v.invalidate();
v.performClick();
onClick(v);
break;
}
return true;
}
}
and the fix in my code:
tv.setOnTouchListener(new StateTouchListener()
{
#Override
public void onClick(final View v)
{
android.util.Log.d("Applog","click!");
};
});
This should replace the setOnClickListener I used.

ListView receives touches, but onItemClickListener does not receive clicks after a pause and resume

We generate several ListViews that hold info for a user to filter information in another fragment. It works fine, unless you pause and resume the app (say, backgrounding it, or locking the screen). Once you do that, the list can be scrolled, but not clicked.
List generating code:
private View addList(LayoutInflater inflater, ViewGroup container, final FilterValue.SearchCategory type, final String[] labels) {
ArrayAdapter<String> adapter = generateArrayAdapter(inflater, labels, type);
if(adapter == null) {
return null;
}
filterAdapters.add(adapter);
ListView list = (ListView) inflater.inflate(R.layout.on_demand_filter_list, container, false);
list.setAdapter(adapter);
list.setItemsCanFocus(false);
list.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
list.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
list.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(final View view, final MotionEvent motionEvent) {
LOG.d(TAG, "NO TOUCHING!");
return false; //To change body of implemented methods use File | Settings | File Templates.
}
});
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
LOG.d(TAG, "onItemClick!");
CheckedTextView textView = (CheckedTextView) view.findViewById(R.id.title);
textView.toggle();
if (textView.isChecked()) {
filterValue.addToSelectedList(labels[i], type);
} else {
filterValue.removeFromSelectedList(labels[i], type);
}
}
});
list.setAdapter(adapter);
list.setVisibility(View.GONE);
filterListContainer.addView(list);
return list;
}
The onTouch listener only exists to ensure the Touch is received. (It is.) The DescendantFocusability appears to have no effect, this bug exists before and after it was added.
Each is tied to a button that shows or hides the list.
titleHeader.setOnClickListener(new View.OnClickListener() {
public void onClick(View clickedView) {
closeNetworkList();
closeGenreList();
titlesOpen = !titlesOpen;
ImageView indicator = (ImageView) clickedView.findViewById(R.id.filter_expansion_indicator_icon);
if (indicator != null) {
if (titlesOpen) {
indicator.setImageResource(R.drawable.arrow_filter_up);
} else {
indicator.setImageResource(R.drawable.arrow_filter_down);
}
}
if (titlesOpen) {
titlesListView.setVisibility(View.VISIBLE);
} else {
titlesListView.setVisibility(View.GONE);
}
}
});
Tapping this button to hide and then show the listView (which was generated with addList) resets something, and the items can be clicked again.
XML for an item row:
<?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:focusable="false"
android:focusableInTouchMode="false"
android:padding="8dp"
android:orientation="horizontal">
<CheckedTextView
android:id="#+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#drawable/on_demand_filter_checked_text_sel"
android:gravity="center_vertical"
android:paddingLeft="76dp"
android:focusable="false"
android:focusableInTouchMode="false"
android:drawableLeft="#drawable/checkbox_sel"
android:drawablePadding="14dp"
style="#style/LargeRegular"/>
</LinearLayout>
The focusables are new additions, but neither worked. The problem occurred before they were added.
The ListView itself:
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/list"
android:layout_width="fill_parent"
android:layout_height="275dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:divider="#color/Transparent"
android:descendantFocusability="blocksDescendants"
android:cacheColorHint="#ffffff"/>
I am at my absolute wits' end. No one on my team has a sensible solution to this. It works fine, right up until you pause and resume. We do absolutely nothing that touches the views in resume or pause. Can anyone help? I can provide more detail as needed.
I had similar problem with my app (extended SurfaceView which lost touch events after resume) and resolved it by calling the setFocusable( true ) in the onResume() implementation. Apparently the view didn't get the focus and therefore did not receive the touch events. Not sure whether this is the case here, but worth trying.
Remembered that I had had a similar problem with fragment activities. I had a case when layout requests were blocked, they did not cause actual layout traverse.
I've fixed it in Enroscar library (BaseFragment class) with the following snippet of code in a fragment class:
#Override
public void onStart() {
// ... other staff ...
super.onStart();
/*
XXX I don't know the reason but sometimes after coming back here from other activity all layout requests are blocked. :(
It looks like some concurrency issue or a views tree traversal bug
*/
final View contentView = getActivity().findViewById(android.R.id.content);
if (contentView != null) {
final ViewParent root = contentView.getParent();
if (contentView.isLayoutRequested() && !root.isLayoutRequested()) {
if (DebugFlags.DEBUG_GUI) { Log.i("View", "fix layout request"); }
root.requestLayout();
}
}
}

android child view no press state

I have made a linear layout on which I am adding views, simulating a table because I have read that a listview inside a scrollview is not an option :)
So I have a linear layout with wrap_content inside a scrollview.
The problem is that for the views I am adding I can't trigger any press state to change the graphics as before when it was in the listview it worked great.
I have the following:
if (arrayWorksWith.size()!=0)
{
this.listAdapter = new WorksWithListAdapter(this, R.layout.works_with_cell, R.id.lblItemTitle, arrayWorksWith);
for (int i=0; i<arrayWorksWith.size(); i++)
{
View v = this.listAdapter.getView(i, null, layoutTblWorksWith);
v.setTag(new Integer(1000 + arrayWorksWith.get(i).getId()));
v.setOnClickListener(this);
layoutTblWorksWith.addView(v);
}
}
The problem is that the click event gets fired but I don't have any visual feedback. Inside that view I have an Imageview with the background set to the following drawable:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="#drawable/r1" /> <!-- pressed -->
<item android:state_focused="true" android:drawable="#drawable/r1" /> <!-- focused -->
<item android:drawable="#drawable/transparent_background" /> <!-- default -->
</selector>
Also I have just tried a touch listener on the view I am adding, but this fails as well :(
v.setOnTouchListener(new OnTouchListener()
{
public boolean onTouch (View v, MotionEvent event)
{
TextView lblCategoryTitle = (TextView)v.findViewById(R.id.lblItemTitle);
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
lblCategoryTitle.setTextColor(R.color.white);
}
else if (event.getAction() == MotionEvent.ACTION_UP)
{
lblCategoryTitle.setTextColor(R.color.textLabel_blue);
}
return false;
}
});
So when this code was inside a ListView when I tapped on a cell the image was changing the background giving visual feedback, but when I am creating it now and adding it to a LinearLayout it stopped working.
Any ideas? I have tried playing with focusable, clickable, duplicate parent state, but with no luck.
You have to set the listView on to an OnItemClickListener, not on an OnClickListener.

Categories

Resources