Having an issue with CheckedTextView that I can't seem to find a solution. I'm not even entirely sure what's happening.
I have a custom ListView whose rows contain TextViews and a CheckedTextView.
row.xml
<CheckedTextView
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:id="#+id/title"
android:text="Name"
android:gravity="center_vertical"
android:paddingRight="6dip"
android:typeface="sans"
android:checkMark="?android:attr/textCheckMark"
android:textSize="16sp"
android:textStyle="bold"/>
MyAdapterView.java
public class RuleAdapterView extends LinearLayout
{
private CheckedTextView title;
...
title = (CheckedTextView)v.findViewById(R.id.title);
title.setText(entry.getName());
title.setChecked(entry.isActive());
// setup a listener for the checkbox
title.setOnClickListener(new View.OnClickListener() {
public void onClick(View v)
{
((CheckedTextView) v).toggle();
}
});
}
And in the main XML file I set the ListView to android:choiceMode="multipleChoice".
So what I want is for the ListView rows to be long and short clickable and for the CheckedTextView to be separate click execution. This works with the exception of the text part of the CheckedTextView. Whenever either the CheckedTextView is pressed, the text 'flickers'. I did it slowly to figure out exactly what was going on. When you press down on the CheckTextView, the white text either disappears or toggles black (possibly inverts?) and when you release, the text reappears and the checkmark toggles. There is no 'flicker' effect when the ListView is pressed.
Any ideas on what's going on here?
Thanks
Instead of using OnClickListener you should try using OnTouchListener
ckToggle.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
CheckedTextView ck = ((CheckedTextView) v);
ck.toggle();
return false;
}
});
ListView uses its own scheme when an item is clicked (highlighting the background). You might want to check out this solution Android how to make View highlight when clicked?
Related
I am contributing to an app project and I have this issue.
I have a Horizontal LinearLayout with an ArrayAdapter list of Items: image -- EditText -- image
I use EditText to enable scrolling horizontally a long text field while disabling cursor and click events. The parent layout needs to manage the click event on the items + horizontal swipe to change tabs in Main View
The code works properly:
If EditText has a long text: I can scroll horizontally the text to read it all. At the end of the text, the parent layout takes the swipe and can change tabs
I can long touch on the EditText to enable a select checkmark from the parent layout
The cursor and slider are properly hidden in the EditText
The only issue is that EditText consumes the simple click events and never passes them to the parent layout. When I click the images or when I click on the right/left of the EditText, the parent layout properly captures the click action on the adapter item. If I click directly on the EditText, the event is consumed and never reaches the parent layout setOnItemClickListener.
Here's a short video of how it is working now. The click at 10 seconds position in video must be done outside the text: https://www.youtube.com/watch?v=P306p7AcwAI
Here is the xml sample:
https://github.com/PhilZ-cwm6/SMBSync2/blob/philz/SMBSync2/src/main/res/layout/sync_task_item_view.xml
<LinearLayout
android:id="#+id/profile_list_sync_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:layout_marginLeft="5dp" >
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<ImageView
android:id="#+id/sync_task_master_icon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center_vertical|top"
android:src="#drawable/ic_16_server" />
<EditText
android:id="#+id/sync_task_master_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="none"
android:background="#android:color/transparent"
android:cursorVisible="false"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:singleLine="true"
android:ellipsize="end"
android:lines="1"
android:text="Master"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<ImageView
android:id="#+id/sync_task_direction_image"
android:layout_width="12dp"
android:layout_height="18dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:src="#drawable/arrow_right_enabled" />
<ImageView
android:id="#+id/sync_task_target_icon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="top"
android:src="#drawable/ic_16_server" />
<EditText
android:id="#+id/sync_task_target_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="none"
android:background="#android:color/transparent"
android:cursorVisible="false"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:singleLine="true"
android:ellipsize="end"
android:lines="1"
android:text="Target"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
the adapter code extract:
https://github.com/PhilZ-cwm6/SMBSync2/blob/philz/SMBSync2/src/main/java/com/sentaroh/android/SMBSync2/AdapterSyncTask.java
public class AdapterSyncTask extends ArrayAdapter<SyncTaskItem> {
final public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
final SyncTaskItem o = getItem(position);
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(id, null);
holder = new ViewHolder();
holder.tv_row_master = (EditText) v.findViewById(R.id.sync_task_master_info);
holder.tv_row_target = (EditText) v.findViewById(R.id.sync_task_target_info);
}
if (o != null) {
holder.tv_row_master.setText("source dir path");
holder.tv_row_master.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); //disable auto-correct highlight in EditText
holder.tv_row_target.setText(destination dir path);
holder.tv_row_target.setTextColor(mTextColor);
holder.tv_row_target.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); //disable auto-correct highlight in EditText
}
return v;
}
static class ViewHolder {
EditText tv_row_master, tv_row_target;
}
}
And the ActivityMain that needs the onClick events:
https://github.com/PhilZ-cwm6/SMBSync2/blob/philz/SMBSync2/src/main/java/com/sentaroh/android/SMBSync2/ActivityMain.java
private void setSyncTaskListItemClickListener() {
mGp.syncTaskListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
SyncTaskItem item = mGp.syncTaskAdapter.getItem(position);
editSyncTask(item.getSyncTaskName(), item.isSyncTaskAuto(), position);
}
});
}
These are really simple code snippets
Basically, the original code works properly with a TextView but long text path cannot be viewed/scrolled in a single line. Using HorizontalScrollView works also but doesn't pass any event, include swipe and long press to the parent if the click happens on the text field.
The EditText trick fixes all except the simple click events when they happen on the EditTex box. Wired because long touch and swipe are properly passed. Also, the setOnItemClickListener doesn't capture any event when clicking on the EditText, but captures them if it is a TextView
Any idea how to fix this ? I thought about a custom EditText but ended up unsure how would the ArrayAdapter get the EditText position. Am I obliged to use a second adapter for the EditText ?
best regards
Edit: I can add this code to the getView() of Adapter:
holder.tv_row_target.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//click events of EditText are captured
}
});
However, I cannot propagate them to the ListView or a Parent view from the onClick()
This would avoid a heavy rewrite in code based on actions that Parent should do on click
Edit: see next post with proper fix to preserve the native ListView animation
I fixed it this way:
in adapter:
holder.tv_row_target.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
((ActivityMain) mContext).dispatchSyncTaskListItemClick(o, position);
}
});
In MainActivity:
public void dispatchSyncTaskListItemClick(SyncTaskItem item, int position) {
// my actions
}
That way, I can pass the needed info (position, object item) to the activity
I am open to any other suggestion to simply propagate the click to the parent ListView so that it handles it directly
Proper fix:
I post this for any one having the same issue. I saw so many threads on this but no one ever came with a good fix
My previous answer has an issue: it doesn't preserve the native ListView onClick fading animation
I tried the performclick() but it is the same.
I finally fixed it completely using MotionEvent to pass the touch events to the list view
In Adapter: I set the onClick listeners for click and long click and send the action to MainActivity where the ListView action code is present
Adapter listeners:
holder.tv_row_target.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
((ActivityMain) mContext).dispatchSyncTaskListItemClick(o, position);
}
});
holder.tv_row_target.setOnLongClickListener(new View.OnLongClickListener() {
public boolean onLongClick(View v) {
v.setHapticFeedbackEnabled(false);
((ActivityMain) mContext).dispatchSyncTaskListLongClick(o, position);
return true;//notify long touch event is consumed
}
});
In Main Activity: The setOnItemLongClickListener and setOnItemClickListener must be set there. The issue was to click the EditText or a an icon which handles touch and perform the ListView action and animation
// simulate click on teh ListView item
public void dispatchSyncTaskListItemClick(SyncTaskItem item, int position) {
long downTime = SystemClock.uptimeMillis();
long eventTime = SystemClock.uptimeMillis();
int first_visible_pos = mGp.syncTaskListView.getFirstVisiblePosition();
View v = mGp.syncTaskListView.getChildAt(position - first_visible_pos);
float x = v.getX() + 1;//view items start at 0, MotionEvent doesn't handle the UP action at 0
float y = v.getY();
//first touch
MotionEvent motionDown = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0);
motionDown.setSource(InputDevice.SOURCE_TOUCHSCREEN);
mGp.syncTaskListView.onTouchEvent(motionDown);
//release touch
downTime = SystemClock.uptimeMillis();
eventTime = SystemClock.uptimeMillis();
MotionEvent motionUp = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
motionUp.setSource(InputDevice.SOURCE_TOUCHSCREEN);
mGp.syncTaskListView.onTouchEvent(motionUp);
motionUp.recycle();
motionDown.recycle();
}
// simulate long touch
public void dispatchSyncTaskListLongClick(SyncTaskItem item, int position) {
long downTime = SystemClock.uptimeMillis();
long eventTime = SystemClock.uptimeMillis();
int first_visible_pos = mGp.syncTaskListView.getFirstVisiblePosition();
View v = mGp.syncTaskListView.getChildAt(position - first_visible_pos);
MotionEvent motion = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, v.getX(), v.getY(), 0);
motion.setSource(InputDevice.SOURCE_TOUCHSCREEN);
mGp.syncTaskListView.onTouchEvent(motion);
motion.recycle();
}
The hardest part was the x+1 because my View started at float x = 0.0, and the UP action was not considered until it is > 0. Not sure why ? ACTION_DOWN handles it properly
The other thing to take care of is the getChildAt: it returns the view position starting from first visible position and not the adapter passed position. Else, you get a null returned view.
Now, it works just like when interacting with the ListView
New edit: added v.setHapticFeedbackEnabled(false); to remove the duplicate haptic feedback on long touch !
Remaining issue: I feel all this is a workaround. There is still a small delay on the animation trigger when long click. The ideal would be the EditText View completely letting ListView to handle the onClick
So probably only a custom HorizontalScrollView or a custom EditText would fix it
The premise is quite simple. I have a list of items, and each item has a TextView containing the title of the item, and a Switch showing whether the item is on or off. Instead of tapping on the Switch to toggle the item being on or off, I want to be able to click anywhere on the item to toggle it. Basically:
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:onClick="toggleSwitch">
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Switch
android:id="#+id/switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</android.support.constraint.ConstraintLayout>
However, this will create a nested layout when used in a screen, which might be bad for performance. I was wondering, since this ConstraintLayout is literally just a container with an onClick, whether there was a way to implement this layout in a way which avoids nested layouts. Thanks!
You can just give your textView and your button the same method that will be called on click, that way on every view click (anywhere on the item) you will call your method.
For example:
textView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//change you switch state
}
});
switch.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//change you switch state
}
});
I'm creating a ListView with a custom Adapter for displaying my entries. Each row contains a checkbox, and my adapter contains the following code:
CheckBox cb = (CheckBox) v.findViewById(R.id.item_sold);
cb.setChecked(p.isSold);
setupCheckboxListener(cb, v, position);
...
private void setupCheckboxListener(CheckBox check, final View v, final int position) {
check.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
CheckBox cb = (CheckBox) v;
if (cb.isChecked()) {
System.out.println("Should become false!");
cb.setChecked(false);
} else {
System.out.println("Should become true!");
cb.setChecked(true);
}
}
});
My row XML file includes the following:
<CheckBox android:id="#+id/item_sold"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.10"
android:layout_gravity="center"
android:gravity="center"
android:focusable="false"
android:clickable="false"/>
But anytime I press one of the checkboxes, check.isChecked() returns true, even if the box is unchecked. I even checked to make sure that the checkboxes were distinct and weren't picking up just the last value/etc.
Setting up the listeners inline instead of in a method doesn't seem to help, either. It's literally just the isChecked() condition that isn't working appropriately - it seems to always give me the inverse value.
Setting an onClick on the row is not acceptable in this case because I need to allow row selection for something else.
What could be causing this issue?
I have a listview containing a textview and I need to zoom this textview on button click but I am not able to do it, I tried everything to override the simplecursor adapter and override the getview() method but couldn't zoom the textview on button click, the button is outside the listview and my code is:
this is the listview layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<CheckBox
android:id="#+id/bt_rating"
android:focusable="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:button="#android:drawable/btn_star"
android:onClick="onclick"/>
<TextView
android:id="#+id/text1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="#dimen/FontSizeInListView"
android:gravity="right"
/>
</LinearLayout>
Edit
adapter.setViewBinder(new SimpleCursorAdapter.ViewBinder(){
Binds the Cursor column defined by the specified index to the specified view
public boolean setViewValue(View view, Cursor cursor, int columnIndex){
if(view.getId() == R.id.text1){
tv=(TextView)view;
}}**
so that way I get the textview but sure only the last one created will be rendered and in the zoom I do tv.settextsize(..)
but only the last textview created on page will change because I only have the id of that one so how can I get all the textviews and not just the last one created?
In your custom adapter, add a variable (with public setters and getters) to represent zoom level. In your getView method, use this variable to change the text views' text sizes (just add that right before your return line. You must always set the size to something, since the views are reused.
Then on your button that changes the zoom level, change the adapter's zoom variable and then call notifyDataSetChanged on it to force it to call getView on all its visible views. (This is assuming you subclassed ArrayAdapter).
It worked What I did is:
adapter =new SimpleCursorAdapter(this,R.layout.rating,cu,new String[]{"Title","Fav"}, new int[]{R.id.text1,R.id.bt_rating},CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
//added in last update
adapter.setViewBinder(new SimpleCursorAdapter.ViewBinder(){
/** Binds the Cursor column defined by the specified index to the specified view */
public boolean setViewValue(View view, Cursor cursor, int columnIndex){
if(view.getId() == R.id.text1){
tv=(TextView)view;
tv.setTextSize(SizeOfTextInListView);
}
});
And in the zoom button:
//zoom in button click
final Button zoom_in_btn = (Button) findViewById(R.id.button_zoom_in);
zoom_in_btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if(Zoom_counter<10){
SizeOfTextInListView= SizeOfTextInListView+Zoom_In_Out_value;
adapter.notifyDataSetChanged();
Zoom_counter++;
}
}
});
and it worked perfectly..
I have a series of ToggleButtons which represent a series of topics. When toggled, the ToggleButton changes its background color to indicate its state. When in the checked state, the ToggleButton has a darker color.
A Spinner overlays the ToggleButton and allows the user to select a difficulty for the topic.
How do I change the text color (to white) of the Spinner when the ToggleButton is pressed? I think I can handle changing the spinner selector, but I'm struggling to work out a way to change the text color.
Try the following way.
Create a xml named
spinnertext.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/spinnerText"
style="?android:attr/spinnerItemStyle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingBottom="2dp"
android:paddingLeft="6dp"
android:textColor="#41f2f3" />
Now in code.
ArrayAdapter<String> sp_adapter = new ArrayAdapter<String>(this, R.layout.spinnertext, your_array);
sp.setAdapter(sp_adapter);
Then work with toggle button
ToggleButton tb = (ToggleButton) findViewById(R.id.toggleButton1);
tb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton arg0, boolean isChecked) {
TextView tv = (TextView) findViewById(R.id.spinnerText);
if (isChecked)
tv.setTextColor(Color.RED);
else
tv.setTextColor(Color.BLUE);
}
});
On Spinner onItemSelected Method you have to change like this:
public void onItemSelected(AdapterView<?> parent, View arg1, int arg2,
long arg3) {
// TODO Auto-generated method stub
item = (String) parent.getItemAtPosition(arg2);
((TextView) parent.getChildAt(0)).setTextColor(0x00000000);
}
One thing that we have on our app is a custom spinner view. It has a translucent black rounded square behind it and slightly larger than it. It works over any background.
The answer from Gunaseelan helped point me in the right direction. As he suggested, working with the ToggleButton is the way to go. The ToggleButton widget has an android:onClick XML attribute which you can use to specify a method to run when the ToggleButton is toggled/clicked (as described here.
Setting the color of the spinner text and the spinner selector can be a bit difficult. To change the spinner text color:
ToggleButton cardiologyToggle = (ToggleButton) findViewById(R.id.cardiology_toggle);
if (cardiologyToggle.isChecked()) {
spinnerText.setTextColor(Color.WHITE);
} else {
spinnerText.setTextColor(Color.BLACK);
}
This changes only the text displayed when the spinner has been selected.