ListView element not clickable - android

I have a ListView with SimpleCursorAdapter. The layout was using a LinearLayout, but when reading manuals about memory consumption for complex (nested) layouts, I switched to RelativeLayout instead and I've managed to setup the layout exactly as I want.
Now, there's one quirk. In the emulator, I cannot click the list items. It seems as if the elements are one big "no button". However, if I use the emulator arrows and select the listview element (highlights it) and click the button, it works fine.
Why can't I "click" the listview items since I switched to RelativeLayout?
Here's the XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<TextView
android:id="#+id/locationName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Junk text"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="#+id/numStores"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/locationName"
android:text="Junk text #1: 117"
android:textSize="10dp" />
<TextView
android:id="#+id/numItems"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/numStores"
android:text="Junk text #2: 42"
android:textSize="10dp" />
</RelativeLayout>
I even tried android:clickable="true" for RelativeLayout, to no avail.
EDIT
Code for onClick is as follows:
listItems.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
int countyID = cursor.getInt(cursor.getColumnIndex(LocationDB.C_ID));
int numItems = cursor.getInt(cursor.getColumnIndex(LocationDB.C_NUM_ITEMS));
int numStores = cursor.getInt(cursor.getColumnIndex(LocationDB.C_NUM_STORES));
String countyName = cursor.getString(cursor.getColumnIndex(LocationDB.C_NAME));
String foo = String.format(TAG + "Clicked ID #%d (%s), stores = %d, items = %d", countyID, countyName, numStores, numItems);
Log.i(LOG_TAG, foo);
// Show the item in a new activity
Intent apan = new Intent(avBase, Browser.class);
apan.putExtra("countyID", countyID);
startActivity(apan);
}
});
EDIT 2
The code is now tested on my phone, and I get the same error; can't click the items when using RelativeLayout.

I faced some issues with this, too, which seemed to be related to the combination of the ListView's on click listeners and those I assigned each individual item in my Adapter.
In my Adapter.getView() I had to make sure the items were configured like:
item.setLongClickable( false );
item.setClickable( false );
item.setOnClickListener( null );
while my ListView had to have its OnItemClickListener and/or its OnItemLongClickListener set.
Turning on clicks on the individual items and on the listview would not work. Maybe that's what's causing your issue, too.

Seems this is a bug that Google ignores: http://code.google.com/p/android/issues/detail?id=3414

Related

Android GridView causing lagg

I am currently working on a file sharing application. I created a GridView, where I would like to list the already downloaded files with a thumbnail. To achieve this, I use a custom adapter. It loads fine, everything is visible, seems perfect.
And the trick is the following: after the view is loaded, the getView method of my custom adapter is called on position 0 so many times, that it causes massive laggs, with about 12% CPU usage on my Xperia XZ Premium.
I already read answers about laggs in GridView, but in those cases it was caused by scrolling. In my case, no scroll is needed, currently there is only 1 item in the GridView, but it is still struggling.
While lagging, it also spams a message. This message can disappear, if I add the following line of code to my TextViews:
android:singleLine="true"
But this attribute is deprecated and doesn't solves the issue, only the message disappears.
The lagg is not present, when the view is empty.
The getView method of my adapter:
#Override
public View getView(int position, View convertView, #NonNull ViewGroup parent){
View listItem = convertView;
if(listItem==null){
listItem = LayoutInflater.from(getContext()).inflate(R.layout.downloaded_grid_item, parent, false);
}
final Downloaded current = getItem(position);
final ImageView thumbnail = (ImageView)listItem.findViewById(R.id.downloaded_grid_item_thumb);
final TextView name = (TextView)listItem.findViewById(R.id.downloaded_grid_item_name);
final TextView subtext = (TextView) listItem.findViewById(R.id.downloaded_grid_item_subtext);
thumbnail.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
int width = thumbnail.getMeasuredWidth();
thumbnail.setLayoutParams(new LinearLayout.LayoutParams(width, width));
return true;
}
});
listItem.setTag(position);
thumbnail.setTag(position);
name.setTag(position);
subtext.setTag(position);
name.setText(current.getName());
subtext.setText(DateFormat.getDateInstance().format(new Date(current.getTime())));
thumbnail.setImageBitmap(ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(current.getPath()), 400, 400));
return listItem;
}
The GridView:
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/downloaded_grid_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnWidth="90dp"
android:numColumns="auto_fit"
android:layout_margin="10dp"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:stretchMode="columnWidth"
android:gravity="center"/>
The custom layout of the items:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/downloaded_grid_item"
android:background="#drawable/downloaded_grid_item_background"
android:orientation="vertical"
android:padding="4dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/downloaded_grid_item_thumb"
android:layout_width="match_parent"
android:layout_height="90dp"
android:src="#drawable/ic_menu_gallery"
android:scaleType="centerInside"/>
<TextView
android:id="#+id/downloaded_grid_item_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textAppearance="#style/TextAppearance.AppCompat.Small"
tools:text="name"
android:ellipsize="end"
android:lines="1"/>
<TextView
android:id="#+id/downloaded_grid_item_subtext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="#style/TextAppearance.AppCompat.Small"
tools:text="download date"
android:ellipsize="end"
android:lines="1"/>
</LinearLayout>
And the message, spammed by the device:
W/StaticLayout: maxLineHeight should not be -1. maxLines:1 lineCount:1
After running your code in an emulator (Android 7), I did not manage to get the message about the maxLineHeight but I can confirm that there was no end of getView() being called.
The reason for this behavior is that each time getView() is executed you add a new OnPreDrawListener without ever removing it. Changing the ImageView's height forces a recalculation of how it is to be drawn. This causes the whole GridView to go through a new layout pass (measure - draw - layout). So getView() is called again (normally at least twice for position 0). Because you never remove a OnPreDrawListener they all will fire once more, only causing more and more work for the CPU without actually changing the value for the ImageView's height.
You need to remove the Listeners you add to a ViewTreeObserver as soon as you obtained the desired information. So you need to write
thumbnail.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
thumbnail.getViewTreeObserver().removeOnPreDrawListener(this);
int width = thumbnail.getMeasuredWidth();
thumbnail.setLayoutParams(new LinearLayout.LayoutParams(width, width));
return true;
}
});

check multiple items in list view

I use an adapter to load a list view from a content provider. Each item in the list uses the below 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="wrap_content">
<LinearLayout
android:id ="#+id/wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckedTextView
android:id="#+id/txt_chat"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
/>
</LinearLayout>
</LinearLayout>
I have set the list item to allow multiple choice mode :
onCreate(){
...
lv_chatMessages = (ListView) findViewById(R.id.listview_chat);
lv_chatMessages.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
lv_chatMessages.setItemsCanFocus(false);
...
}
But at run time, i am not able to check any of the items in the list view. If i touch the box, the item just gets highlighted momentarily, but no check mark appears.
I can set a default for all the items if i add an attribute to the CheckedTextView :
android:checked="true"
How do i let the user change the check status of each item at runtime?
refer this link...
CheckedTextView
this may help you..
[Edit by Faizal] :
Using the toggle() inside the onItemClickListener did the trick :
lv_chatMessages.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3){
CheckedTextView ctv = (CheckedTextView) (((ViewGroup) arg1).findViewById(R.id.txt_chat));
ctv.toggle();
}
});
can you add this into list items
android:descendantFocusability="blocksDescendants"
android:focusableInTouchMode="false"
Since, you are using a custom adapter, you'll have to maintain the status of the checkbox in the getView() method of the custom adapter.
What you could probably do is to add another Boolean field to your ArrayList of Objects which you use to populate the adapter and check that value in the getView() method. to Something like :
if(status) {
checkBox.setChecked(true);
}
Also, update the status variable using OnCheckedChangeListener().
This method will let you toggle status and also ensure that the checkbox status is maintained even when a list item goes out of the view.
Edit 1 : When not using a custom adapter. This is how I did it.
ListView rendererList;
ArrayList<String> friendlyNames;
friendlyNames = new ArrayList<String>();
rendererList = (ListView) dialog.findViewById(R.id.rendererList);
ArrayAdapter rendererSelectionAdapter = new ArrayAdapter<String>(activity,R.layout.custom_list_layout, friendlyNames);
rendererList.setAdapter(rendererSelectionAdapter);
rendererList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
custom_list_layout was the same layout which android provides, however, I had to make slight modifications so I copied the default file and edited.
here is the custom_list_layout :
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/text1"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeightSmall"
android:background="#color/white"
android:checkMark="?android:attr/listChoiceIndicatorSingle"
android:gravity="center_vertical"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:textSize="#dimen/dialog_box_button_textsize" />

Textview onClick is being called instead of the Listview onItemClick.

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

Android: ListView with expanding LinearLayouts as items causes duplicates

I'm trying to write a test application that consists of a few fragments.
One fragment should contain a listView of all music artists from the device.
Each item of this list is a linearlayout starting with a TextView with the artist name and an empty linearlayout under it as follows:
The list is of this layout:
<ListView
android:id="#+id/artistsLists"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true" >
</ListView>
Each item is of the following layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/artistName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp"
android:text="" />
<LinearLayout
android:id="#+id/artistsAlbums"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
</LinearLayout>
</LinearLayout>
I'm populating the list using a SimpleCursorAdapter in the following way:
public class MusicTabFragment extends Fragment
{
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_music_tab,container,false);
Cursor artistsCursor = getActivity().getContentResolver().query(Audio.Artists.EXTERNAL_CONTENT_URI, new String[]{Audio.Artists.ARTIST,Audio.Artists._ID}, null, null,Audio.Artists.ARTIST);
SimpleCursorAdapter adapter = new SimpleCursorAdapter(view.getContext(), R.layout.music_artist_list_item_layout, artistsCursor, new String[]{Audio.Artists.ARTIST},new int[]{R.id.artistName},0 );
ListView lView = (ListView)view.findViewById(R.id.artistsLists);
lView.setAdapter(adapter);
lView.setOnItemClickListener(new OnItemClickListener()
{
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
((LinearLayout)view.findViewById(R.id.artistsAlbums)).removeAllViews();
Cursor albumsCursor = getActivity().getContentResolver().query(Audio.Artists.Albums.getContentUri("external", ((Cursor)parent.getItemAtPosition(position)).getLong(1)), new String[]{Audio.Albums.ALBUM, Audio.Albums._ID},null,null,null);
LinearLayout artistLayout = (LinearLayout)view.findViewById(R.id.artistsAlbums);
for(albumsCursor.moveToFirst();!albumsCursor.isAfterLast();albumsCursor.moveToNext())
{
View albumView = LayoutInflater.from(view.getContext()).inflate(android.R.layout.simple_list_item_1,artistLayout,false);
((TextView)albumView.findViewById(android.R.id.text1)).setText(albumsCursor.getString(0));
artistLayout.addView(albumView);
}
Log.d("POPULATE","populated again!");
albumsCursor.close();
}
});
return view;
}
}
This works just fine. when i click an artist name, the linearlayout populates with all of this artist album names.
the problem is, that once a linearLayout scrolls out of view, it shows again from the other edge of the view (PacMan Style) as if another list item's linearLayout was populated.
It happens every time the expanded layout goes out of sight. the funny part is that some times when scrolling back up, the linearLayout shows under a different artist name.
example
I'll be glad to hear how should I implement this fragment. But i will also like to know why this behavior is caused.
Thanks,
Maor.
I have found the solution here at stackoverflow.com
It appears that the view shouldn't hold any data, since it is being used for different data when i scroll back and fourth.
I think holding an external data structure to save each virtual view state is not nice programming. is there a way to keep this data anyway? (for this i will be looking now)

Accessing child views inside a ListItem defined as LinearLayout

I'm trying to retrieve the getText() value of a TextView defined in my ListItem Layout, inside the setOnItemClickListener method.
Basically, I want to log data (Id, LookupKey, DisplayName, or PhoneNumber) allowing me to retrieve the contact selected regardless of the listview/adapter used.
I display a simple TextView and an Imageview which visibility is set according to the data.
here is the layout : contact_entry.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content" android:baselineAligned="false"
android:layout_width="fill_parent" android:padding="5px">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content" android:textSize="16px"
android:text="#+id/contactEntryText" android:id="#+id/contactEntryText">
</TextView>
<TableRow android:paddingLeft="5px" android:visibility="visible"
android:paddingRight="5px" android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/indicator_logo_table"
android:gravity="right">
<ImageView android:layout_width="wrap_content"
android:id="#+id/imageView1"
android:layout_height="fill_parent"
android:src="#drawable/indicator">
</ImageView>
</TableRow>
</LinearLayout>
the adapter i use is defined below :
SimpleAdapterWithImageView adapter = new SimpleAdapterWithImageView(
this, R.layout.contact_entry, cursor, fields,
new int[] { R.id.contactEntryText });
mContactList.setAdapter(adapter);
( You could have guessed it's for displaying the contact's Display_Name from the ContactsContract ContentProvider. )
and here is finally the mContactList.setOnItemClickListener ...
mContactList.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
LinearLayout linear = (LinearLayout) view.findViewById(R.layout.contact_entry);
Log.i("done","linear layout"); // it's not done at all ! findViewById returns null;
//this part is currently useless
TextView displayed_name = (TextView) linear.findViewById(R.id.contactEntryText);
Log.i("done","Displayed name = linear.findViewById contactentrytext");
Toast.makeText(getApplicationContext(),
// displayed_name.getText(), //
//view.getContentDescription(), //
Toast.LENGTH_SHORT).show();
}
As you can see i'm only trying to display a toast for the moment, in the end the name will be put as an extra for an intent.
So : the best result i got these last 36 hours (sigh) is displaying the name of the first contact displayed in the toast, even when clicking on the second item...
TextView displayed_name = (TextView) findViewById(R.id.contactEntryText);
Toast.makeText(getApplicationContext(),displayed_name.getText(),
Toast.LENGTH_SHORT).show();
}
I've been trying stuff like the tutorial google provided here, which (am I wrong? I'm new to Android so don't hesitate to slap me in the face ...) actually casts the whole View in a TextView ...
plus other tricks like view.getChildAt() and so on... for hours.
I'm convinced it's not that hard, yet i'm running in circles !
I don't think it is because of my adapter, but if anyone thinks it could be the cause, i could provide the code in another post.
Thanks in advance !
LinearLayout linear = (LinearLayout) view.findViewById(R.layout.contact_entry);
Here is a mistake - you shan't use R.layout in findViewById. Usually, you need to use R.id there, or replace findViewById by inflating a layout. But in this case you just need the view from arguments.
Replace the quted code with this one:
LinearLayout linear = (LinearLayout) view;

Categories

Resources