I have Listview with editext and textview.
When i touch on edittext then edittext lost focus!
I resolved this problem by setting android:windowSoftInputMode="adjustPan"(AndroidManifest.xml).
Now i touch on edittext than editext get focus but application label and some raw of listview disappear(top part).
I want to get focus when user touch on edittext without loss application label and some raw of listview.
Code that i have implemented :
Below coding get focus when user touch on edittext but application label and some raw of listview disappear when soft keypad pop up.I want to get focus when user touch on edittext without loss application label and some raw of listview.
1)AndroidManifest.xml
<application android:icon="#drawable/icon" android:label="#string/app_name">
<activity android:name=".MyListViewDemoActivity"
android:label="#string/app_name"
android:windowSoftInputMode="adjustPan"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
2) raw_layout.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<EditText android:id="#+id/mEditText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
3) main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ListView android:id="#+id/mListView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
4) MyListViewDemoActivity
public class MyListViewDemoActivity extends Activity {
private ListView mListView;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mListView=(ListView)findViewById(R.id.mListView);
mListView.setAdapter(new MyAdapter(this));
}
}
class MyAdapter extends BaseAdapter {
private Activity mContext;
private String character[]={"a","b","c","d","e","f","g","h","i","j"};
public MyAdapter(Activity context)
{
mContext=context;
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return character.length;
}
#Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return null;
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
private class Holder
{
EditText mEditText;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
final Holder holder;
if (convertView == null) {
holder = new Holder();
LayoutInflater inflater =mContext.getLayoutInflater();
convertView = inflater.inflate(R.layout.raw_layout, null);
holder.mEditText = (EditText) convertView
.findViewById(R.id.mEditText);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
holder.mEditText.setText(character[position]);
holder.mEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
// TODO Auto-generated method stub
if (!hasFocus){
final EditText etxt = (EditText) v;
holder.mEditText.setText(etxt.getText().toString());
}
}
});
return convertView;
}
}
I was having the same problem. My numberic keyboard would momentarily appear before being replaced by the qwerty keyboard and the EditText losing focus.
The problem is that the keyboard appearing makes your EditText lose focus. To prevent this put the following in your AndroidManifest.xml for the appropriate Activity (or Activities):
android:windowSoftInputMode="adjustPan"
See Android documentation:
When the input method appears on the screen, it reduces the amount of space available for your app's UI. The system makes a decision as to how it should adjust the visible portion of your UI, but it might not get it right. To ensure the best behavior for your app, you should specify how you'd like the system to display your UI in the remaining space.
To declare your preferred treatment in an activity, use the android:windowSoftInputMode attribute in your manifest's <activity> element with one of the "adjust" values.
For example, to ensure that the system resizes your layout to the available space—which ensures that all of your layout content is accessible (even though it probably requires scrolling)—use "adjustResize"
Without seeing your code, how can we suggest the possible solution for your problem. So keep practice of posting possible code whenever you ask any question.
However, here i have found one tutorial for implementing Android Focusable EditText inside ListView. Go through the example and try to implement in your way or find out the solution for your problem.
I solved this "Putting EditText in ListView as an item" problem recently. I am not very good at English. So if there's something I don't explain clearly please tell me.
We know ListView can be scrolled verticaly and we want to put EditText in ListView as an item.
First:
Add
android:windowSoftInputMode="adjustResize"
in your AndroidManifest.xml at the activity node.
Second:
We create an pojo as model data source to control EditText state
Line.java
public class Line{
int num;
String text;
boolean focus;
get set method and so on...
}
Third:
We write an adapter to adapt EditText to ListView.
Item item_line.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="80dp">
<EditText
android:id="#+id/etLine"
android:focusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"/>
</FrameLayout>
Adapter:
#Override
public View getView(final int position, View convertView, final ViewGroup parent) {
final ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_line, parent, false);
holder.etLine = (EditText) convertView.findViewById(R.id.etLine);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final Line line = lines.get(position);
// step 1: remove android.text.TextWatcher added in step 5 to make sure android.text.TextWatcher
// don't trigger in step 2;
// why?
//
// note: When an object of a type is attached to an Editable,
// TextWatcher's methods will be called when the EidtText's text is changed.
//
// EditText use a ArrayList<TextWatcher> type object to store the listener, so we must
// make sure there's only one TextWatcher object in this list;
//
// Avoid triggering TextWatcher's method in step 2 we remove it at first time.
//
if (holder.etLine.getTag() instanceof TextWatcher) {
holder.etLine.removeTextChangedListener((TextWatcher) (holder.etLine.getTag()));
}
// step 2: set text and focus after remove android.text.TextWatcher(step 1);
holder.etLine.setHint(position + ".");
// set text
if (TextUtils.isEmpty(line.getText())) {
holder.etLine.setTextKeepState("");
} else {
holder.etLine.setTextKeepState(line.getText());
}
// set focus status
// why?
//
// note: ListView has a very elegant recycle algorithm. So views in ListView is not reliable.
// Especially in this case, EditText is an item of ListView. Software input window may cause
// ListView relayout leading adapter's getView() invoke many times.
// Above all if we change EditText's focus state directly in EditText level(not in Adapter).
// The focus state may be messed up when the particularly view reused in other position.
//
// So using data source control View's state is the core to deal with this problem.
if (line.isFocus()) {
holder.etLine.requestFocus();
} else {
holder.etLine.clearFocus();
}
// step 3: set an OnTouchListener to EditText to update focus status indicator in data source
// why?
//
// in step 2, we know we must control view state through data source. We use OnTouchListener
// to watch the state change and update the data source when user move up fingers(ACTION_UP).
// We don't want to consume the touch event, simply return false in method onTouch().
holder.etLine.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
check(position);
}
return false;
}
});
// step 4: set TextWatcher to EditText to listen text changes in EditText to updating the text in data source
// why?
//
// again, use data source to control view state.
// When user edit the text in one EditText item and scroll the ListView. The particularly EditText item will be
// reuse in adapter's getView(), this may lead text messed up in ListView.
// How to deal with this problem?
// Easy! We update the text in data source at the same time when user is editing. TextWatcher is the best way to
// do this.
final TextWatcher watcher = new SimpeTextWather() {
#Override
public void afterTextChanged(Editable s) {
if (TextUtils.isEmpty(s)) {
line.setText(null);
} else {
line.setText(String.valueOf(s));
}
}
};
holder.etLine.addTextChangedListener(watcher);
// step 5: Set watcher as a tag of EditText.
// so we can remove the same object which was setted to EditText in step 4;
// Make sure only one callback is attached to EditText
holder.etLine.setTag(watcher);
return convertView;
}
/**
* change focus status in data source
*/
private void check(int position) {
for (Line l : lines) {
l.setFocus(false);
}
lines.get(position).setFocus(true);
}
static class ViewHolder {
EditText etLine;
}
All done!
You can read more details in my github.
Demo: https://github.com/Aspsine/EditTextInListView
ListView recreate the View,
Try to use a LinearLayout inside in ScrollView, then in your code use a runOnUiThread to fill your view in an other thread like this
public void fillDataTask(Context context, final LinearLayout listView) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
fillView(context, listView);
}
});
}
private void fillView(Context context, LinearLayout listView) {
MyAdapter adapter = new MyAdapter(context);
final int adapterCount = adapter.getCount();
for (int i = 0; i < adapterCount; i++) {
View item = adapter.getView(i, null, null);
listView.addView(item);
}
}
<ListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:drawSelectorOnTop="false"
android:id="#+id/list1"
android:layout_weight="1"
android:cacheColorHint="#00000000"
android:focusable="false"
>
</ListView>
Related
I am inflating edit text in adapter class within of a list view. View is looking cool multiple edit text are appearing properly but when I do focus on the edit text or if I try to type something it loosing focus I tried to adding listeners, changing manifest file but nothing works for me.
following code is not working for me
#Override
public View getChildView(int position, int i1, boolean b, View view,
ViewGroup viewGroup) {
EditText comment = view.findViewById(R.id.txtRecordComment);
comment.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (lastFocussedPosition == -1 || lastFocussedPosition == position) {
lastFocussedPosition = position;
edittext.requestFocus();
}
}
}, 200);
} else {
lastFocussedPosition = -1;
}
}
});
return view;
}
please help me to resolve this issue
Since you are using a ListView, it is bound to happen with your current code implementation.
You must remember that if your EditText is in a ListView, every key down will result in the edit text losing and getting focus because all the views are redrawn, so the edit text representing whatever row used to be focused is now a completely different object.
To get the expected behaviour, declare a variable in your adapter: int focusedRow. In getView method of your adapter, add an onFocusChanged listener to the EditText and when that edit text gains focus, set focusedRow = whatever row the focused EditText happens to be in.
Also set any edit text that is in the currentlyFocusedRow to be focused.
Update:
If you have multiple EditTexts, add an onFocusChanged listener to each edit text.
Add below code in Android Manifest file
<activity android:name=".ActivityName"
android:label="#string/app_name"
android:windowSoftInputMode="adjustPan">
Hope this will work
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);
}
}
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.
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();
}
}
}
There is an really weird thing happening with my listview. I am creating an ListView with buttons and an editText.
It's disposed like this: [Button] [EditText] [Button], The buttons works like an "incrementer" and "decrementer" updating the numerical value of EditText in 1 unit per click.
The problem is, when I click in an button, almost every time an editText of another list view element is changed (the editText of the clicked item is also changed). And if I click in a button of this erroneous changed item, it also changes the editText of the first one. They basically have the same reference of buttons and editText, although they have textViews with data, and this data is different between they.
To accomplish that I created and custom adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
convertView = mInflater.inflate(R.layout.lastproduct_row, null);
holder = new ViewHolder();
holder.btnAddQtd = (Button) convertView.findViewById(R.lastproduct_row.btn_add_qtd);
holder.btnSubQtd = (Button) convertView.findViewById(R.lastproduct_row.btn_sub_qtd);
holder.etQuantidade = (EditText) convertView.findViewById(R.lastproduct_row.et_quantidade);
TextView tv;
holder.tvList = new TextView[PRODUCTROW_INT_KEY.length];
for(int i = 0; i < PRODUCTROW_INT_KEY.length; i++) {
tv = (TextView) convertView.findViewById(PRODUCTROW_INT_KEY[i]);
holder.tvList[i] = tv;
}
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
HashMap<String, String> hm = productsList.get(position);
String key = hm.get(CODIGO_KEY);
for(int i = 0; i < PRODUCTROW_INT_KEY.length; i++) {
holder.tvList[i].setText(hm.get(PRODUCTROW_STR_KEY[i]));
}
holder.btnAddQtd.setTag(key+QTD_FLAG+ADD_ACTION);
holder.btnSubQtd.setTag(key+QTD_FLAG+SUB_ACTION);
holder.btnAddQtd.setOnClickListener(handle);
holder.btnSubQtd.setOnClickListener(handle);
if(novosEstoques.containsKey(key)) {
holder.etQuantidade.setText(MyParseFunctions.parseCentesimal(novosEstoques.get(key).getQuantidade()));
}
return convertView;
}
class ViewHolder {
private TextView []tvList;
private Button btnAddQtd, btnSubQtd;
private Button btnAddQtVol, btnSubQtVol;
private EditText etQuantidade, etQtVolume;
}
I added onClick listenners to the buttons, setting their tags with my listView element ID (concatenated with another informations). Then in my event listener I just get the button parent View (an LinearLayout) and get the EditText from that using getViewAt():
#Override
public void onClick(View v) {
String tag = (String) v.getTag();
if(tag.contains(QTD_FLAG)) {
String []info = ((String) v.getTag()).split(QTD_FLAG);
float qtd;
LinearLayout ll = (LinearLayout) v.getParent();
ll.setBackgroundColor(Color.rgb(0, 128, 30));
EditText et = (EditText) ll.getChildAt(2);
qtd = Float.parseFloat(et.getText().toString().replace(",", "."));
if(info[1].equals(ADD_ACTION)) {
qtd++;
}
else if(info[1].equals(SUB_ACTION)) {
if(qtd > 0)
qtd--;
}
Log.d("TESTE", "MODIFICAR KEY = "+info[0]);
et.setText(qtd+"");
}
}
I'm using an setBackgroundColor in this example to confirm that the LinearLayout instance is duplicated in the lisView. When I click an Button, it's painted in 2 different list view item.
Anyone can point me what could be doing this? I have found people with an duplicated ListView item, I don know if that is my case, cause I have TextView's inside my ListView, and they are not equal, only the LinearLayout portion with buttons and editText is "shared".
I make some changes in my getView method and it's working now! It seems that every time the getView method is called i have not guarantee at all that my editTexts will be filled properly and I didn't realize that. So every getView call I make I set the editText value, if the user edit an ET value, I store it in a HashMap to restore in getView, if there is no entry in HashMap for the given editText, then I set it to the default value (zero):
...
if(convertView == null) {
holder = new ViewHolder();
holder.btnAddQtd = (Button) convertView.findViewById(R.lastproduct_row.btn_add_qtd);
holder.btnSubQtd = (Button) convertView.findViewById(R.lastproduct_row.btn_sub_qtd);
holder.etQuantidade = (EditText) convertView.findViewById(R.lastproduct_row.et_quantidade);
//Now it is easier to get etQuantidade reference in button
//click handle, I just have to do:
// public onClick(View v) {
// EditText etButtonAssociated = (EditText) v.getTag();
// ...
// }
holder.btnAddQtd.setTag(holder.etQuantidade);
holder.btnSubQtd.setTag(holder.etQuantidade);
holder.btnAddQtd.setOnClickListener(handle);
holder.btnSubQtd.setOnClickListener(handle);
...
}
else {
...
}
holder.etQuantidade.setTag(key);
if(novosEstoques.containsKey(key)) {
holder.etQuantidade.setText(MyParseFunctions.parseCentesimal(novosEstoques.get(key).getQuantidade()));
}
else {
holder.etQuantidade.setText("0");
}
return convertView;
Israel,
After looking over your code, I was wondering about an implementation decision you have made. Since each Button is "bound" to a particular EditText, have you considered setting the Tag of those Buttons to the EditText? The Tag may be any Object including a UI element. This is especially useful for dynamic UI elements, such as a runtime populated list.
Since this is handled in your Adapter you wouldn't have to worry about duplicate Parents and such. Additionally, you could avoid having to worry about "finding" the control in your onClick() because you would have it (It's the tag). I'm not sure exactly what your project needs are, but this seems like a potentially viable solution, unless you need those Buttons to accomplish other tasks.
Note of Caution
Just make sure that you erase the Tags' references to the EditText when you are done. Otherwise, you run the risk of leaking some memory.
FuzzicalLogic