I have a custom Listview with an ImageView for drawing. In my Mainactivity I start a thread which redraws the ImageView in my Listview every 20ms.
The ImageView is only refreshed when I call adapter.notifyDataSetChanged(); in my Listfragment.
This works fine, but my problem is, that onListItemClick only fires sometimes in this case. When I remove the adapter.notifyDataSetChanged(), onListItemClick fires always but now, my ImageViews are not refreshed.
Here the important parts of my code:
public class FragmentOscilloscope extends ListFragment
{
private ListViewAdapter adapter;
private List<ListViewItem> rowItems;
private Handler sampleUpdateHandler = null;
#Override
public void onActivityCreated(Bundle savedInstanceState)
{
sampleUpdateHandler = new Handler();
}
public void InitFragment()
{
adapter = new ListViewAdapter(getActivity(), rowItems);
setListAdapter(adapter);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id)
{
super.onListItemClick(l, v, position, id);
Log.d("FragmentOscilloscope", "onListItemClick");
}
public void UpdateOscilloscope(final PositionMarker pos)
{
for (int i = 0; i < listItems; i++);
{
Canvas canvas = rowItems.get(i).getCanvas();
// do the drawings
}
sampleUpdateHandler.post(new Runnable()
{
#Override
public void run()
{
adapter.notifyDataSetChanged();
}
});
}
}
This is my getView() in my ListViewAdapter:
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
ListViewItem row_pos = rowItem.get(position);
if (convertView == null)
{
convertView = mInflater.inflate(R.layout.oscilloscope_list_item, parent, false);
}
imageView = (ImageView) convertView.findViewById(R.id.osc_image);
imageView.setImageBitmap(row_pos.getBitmap());
row_pos.setImageView(imageView);
return convertView;
}
Can someone help me with this? I´m really frustrated... Thanks!
You can also find the full code of the described behavior here:
Android ListFragment update/refresh and onItemClick
When I remove the adapter.notifyDataSetChanged(), onListItemClick fires always but now, my ImageViews are not refreshed.
when you call notifyDataSetChanged(), items in your listview will be init and draw again. The main cause the onListItemClick fires sometimes because at that time your UI thread was VERY BUSY, it's processing other tasks and onListItemClick command will be put on the task queue to process.
I guess that in the getView() from adapter you do very heavy tasks, Try to improve it or create Thread/AsynTask for heavy processes. Hope it help.
Any way, if you provide more details in your code (getView() is a good point) I think some guys can help a lot.
Related
First of all, I'm a beginner in android programming. So dont be too harsh :P
Anyway, I have a recycling ListView, containing an Image and a Text per List Item.
Let's say there are 100 different Items. Every Item clicked leads me to my Activity "DetailActivity.class" with the "detail_layout.xml". Now I want to configurate this layout depending on which Item was clicked.
TestActivity.java:
ArrayList<String> list = new ArrayList<String>();
for(int i = 0; i <= 99; i++) {
list.add(detail_array[i]);
//detail_array[] contains Strings which are used to add Items to the list.
}
private ListView myList = (ListView)findViewById(R.id.list);
myList.setAdapter(new MyCustomAdapter(TestActivity.this,list));
MyCustomAdapter.java:
public class MyCustomAdapter extends BaseAdapter {
private ArrayList<String> mListItemsTV;
private LayoutInflater mLayoutInflater;
public View getView(int position, View view, ViewGroup viewGroup) {
ViewHolder holder;
if (view == null) {
holder = new ViewHolder();
view = mLayoutInflater.inflate(R.layout.list_item, null);
holder.itemName = (TextView) view.findViewById(R.id.list_item_text_view);
view.setTag(holder);
} else {
holder = (ViewHolder)view.getTag();
}
String stringItem = mListItemsTV.get(position);
if (stringItem != null) {
if (holder.itemName != null) {
holder.itemName.setText(stringItem);
ImageView imageView = (ImageView) view.findViewById(R.id.list_item_image_view);
if(holder.itemName.getText() == "SomeName") {
imageView.setImageResource(R.drawable.somedrawable);
}
return view;
}
To be honest, I've got those from some kind of tutorial and I guess I know whats happening there^^
Now, how can I manage to get which Item was clicked? I tried it several times with "setOnClickListener" but it never seems to work. How do I use this? Should I use this? Where do I have to use it?
The basic idea is listview is generally dynamic so you will have a collection of an arbatrary number of items that you want to register with a listener. Oclicklistener will not work unless you register each item separate (not a good idea). Use onitemclicklistener to register the collection this link should help. http://www.ezzylearning.com/tutorial.aspx?tid=1351248&q=handling-android-listview-onitemclick-event
You'll want to use setOnItemClickListener. This is an example on how it's used. You'll need to adapt it to your code. But you can see how you would reference an individual item in the list.
listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, final View view,
int position, long id) {
final String item = (String) parent.getItemAtPosition(position);
view.animate().setDuration(2000).alpha(0)
.withEndAction(new Runnable() {
#Override
public void run() {
list.remove(item);
adapter.notifyDataSetChanged();
view.setAlpha(1);
}
});
}
});
}
I'll fix the formatting when I get home. On mobile ATM. If anyone wants to edit the code feel free.
If you don't want to use an adapter then try this:
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
switch(position){
/*
* The case is the list position
*/
case 0:
break;
case 1:
break;
}
}
I'm Settings up a Custom ListView.
The pull-to-refresh feature comes straight from https://github.com/chrisbanes/Android-PullToRefresh
The ListView displayes Images, so i created a custom Adapter:
class mAdapter extends BaseAdapter{
public mAdapter(Context context){
// nothing to do
}
#Override
public int getCount() {
return mValues.size();
}
#Override
public Object getItem(int position) {
return mValues.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public boolean areAllItemsEnabled()
{
return false;
}
#Override
public boolean isEnabled(int position)
{
return false;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if(v == null){
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.list_item, null);
}
ImageView iv = (ImageView) v.findViewById(R.id.imageView);
if(iv != null){
displayImageInView(iv);
iv.setClickable(true);
iv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context, "ImageView", Toast.LENGTH_SHORT).show();
}
});
}
return v;
}
}
in onCreate(), i get the listView and assign the adapter:
mListView = (PullToRefreshListView) findViewById(R.id.listView);
mListView.setAdapter(new mAdapter(context));
After that i add an image to mValues (url for image to load from web) and call notifiyDataSetChanged on the adapter.
in mListView.onRefresh(), i add an image to mValues.
This works smoothly for adding the first image, or even the first bunch of images (before calling mAdapter.notifyDataSetChanged()).
The refresh indicator shows and hides as intended.
The weird things start happening when i try to add another image (or bunch) after that.
The refresh indicator shows, the image is displayed in the list view.
BUT : the refresh indicator never hides again after that. "onRefreshComplete()" gets called, but seems not to work properly the second time.
The UI Thread is not blocking, so operation is still possible.
If i delete all items in mValues, notify the adapter and pull to refresh again, the image is added properly, and the refresh indicator is hidden properly.
Conclusion: The pull-to-refresh only hides properly if the list was empty before refreshing.
I really don't know where to look for a solution for this weird error.
Maybe someone familiar with the Pull-To-Refresh Library from Chirs Banes can help me out here.
Thank You !
I just figured it out myself -.-
For anyone interested:
You have to set onRefreshComplete from the UI Thread.
Use a Handler to .post it from inside onRefresh(). <- which by the way runs on a separate thread.
Have a nice day.
I've found 2 ways:
Dynamically, when you need pulltorefreshview to stop do task on pull up, you can set a custom AsyncTask, for example:
private class GetDataTask extends AsyncTask<Void, Void, String[]> {
#Override
protected String[] doInBackground(Void... params) {
return null;
}
#Override
protected void onPostExecute(String[] result) {
lv.onRefreshComplete();
showToast(getResources().getString(R.string.no_more));
super.onPostExecute(result);
}
}
Dynamically call setMode to the pulltorefreshView
ptrlv.setMode(Mode.Both); // both direction can be used
ptrlv.setMpde(Mode.PULL_FROM_START); // only pull down can be used.
So I'm having a weird order of events in my code. It'll probably be something minor that I haven't seen yet. A have ListView that and is pulling the string from an EditText after button click.
The EditText lives in a dialog that's being pulled back to the main Activity by an interface
What happens now, is when I type something in the EditText field say "a", nothing shows up. But when I go to add another, "b", "a" shows up. and so forth. So one has to be created in order for the previous to show up.
Here's my what I have.
public class NewActivity extends FragmentActivity implements AddSiteDialog.AddSiteDialogListener {
ListView mSiteListView;
ArrayList<String> siteList = new ArrayList<String>();
CustomAdapter arrayAdapter = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.add_site);
mSiteListView = (ListView) findViewById(R.id.siteAddList);
arrayAdapter = new CustomAdapter();
mSiteListView.setAdapter(arrayAdapter);
Button b = (Button) findViewById(R.id.addSiteButton);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
showDialog();
}
});
}
public void showDialog() {
FragmentManager fm = getSupportFragmentManager();
AddSiteDialog addSiteDialog = new AddSiteDialog();
addSiteDialog.show(fm, "main");
}
public void onSignIn(String inputText) {
siteList.add(inputText);
}
class CustomAdapter extends ArrayAdapter<String> {
CustomAdapter() {
super(NewActivity.this, R.layout.add_site, siteList);
}
public View getView(int position, View convertView, ViewGroup parent){
View row = convertView;
if (row == null){
LayoutInflater inflater = getLayoutInflater();
row = inflater.inflate(R.layout.list_row, parent, false);
}
((TextView) row.findViewById(R.id.textViewId)).setText(siteList.get(position));
return (row);
}
}
}
Can anyone spot where this is happening?
Simple.
call arrayAdapter.notifyDataSetChanged() in
public void onSignIn(String inputText) {
siteList.add(inputText);
//add that here
}
If the data which is backed by your adapter changes, you need to call notifyDataSetChanged() on your adapter to instruct the ListView to update itself.
I don't see anything like that in your code.
Just to add:
Your adapter implementation is sub-optimal - although in this case it doesn't really matter.
You should create a ViewHolder and store it as your View's tag, so that you can retrieve it if convertView is given. The ViewHolder should keep references to all sub-views of your view (TextView, ImageView, whatever you need to update). This would avoid the repeated call to findViewById() - which is slow.
I'm using the tabbed layout (with swipe).
Here I have 3 tabs with controlled by a SectionsPagerAdapter. Each tab is a ListFragment.
Now I want to get an event fired when one of the items in the list is clicked. I would like a listener for each tab.
Here's the code now (Which isn't working, event is not fired).
public class NyhederFragment extends ListFragment {
public static final String ARG_SECTION_NUMBER = "section_number";
private static final String TAG="NyhederFragment";
private List<Item> newsItems;
private ArrayList newsHeadlines;
private ArrayAdapter adapter;
private BroadcastReceiver updateReciever;
public NyhederFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ListView newsList = new ListView(getActivity());
newsList.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
newsList.setId(R.id.list);
DatabaseHelper dbConn = new DatabaseHelper(getActivity());
newsItems = dbConn.getAllItemsFromNews();
newsHeadlines = new ArrayList();
for(Item i : newsItems){
newsHeadlines.add(i.getTitle());
}
adapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, newsHeadlines);
setListAdapter(adapter);
newsList.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Log.i("debug", "single click");
}
});
dbConn.close();
getActivity().registerReceiver(updateReciever, new IntentFilter("ArticlesUpdated"));
return newsList;
}
}
What is it, I'm doing wrong?
Thanks a lot in advance!
If you are using ListFragment then you can simply use its override method onListItemClick()
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
super.onListItemClick(l, v, position, id);
}
It is due to custom list item. The default focus is with custom list item (button/textview). It causes this issue.
Please add android:descendantFocusability="blocksDescendants" in root layout of list element.
Hope this will help someone.
change setListAdapter(adapter); to newsList.setAdapter(adapter);
If you wanted to create or re-use or an existing handler that implements AdapterView.OnItemClickListener, rather than implementing onListItemClick() within the ListFragment, you can do so by getting a reference to the ListView of the ListFragment and setting its listener. In your ListFragment.onResume() you could do:
#Override
public void onResume() {
super.onResume();
ListView listView = getListView();
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Log.d("TAG", "Stop touching me");
}
});
}
In my case, I can use the same listener from an Activity with a ListView, a ListActivity or a ListFragment, e.g.
listView.setOnItemClickListener(new MyListViewOnClickListener(getActivity()));
I've got some troubles with notifyDataSetChanged() of a BaseAdapter. This method is called in refreshItems() and shall update the BaseAdapter of my ListActivity. On calling notifyDataSetChanged() nothing happens until I scroll down the ListView for example with the arrow keys. Somehow the modified getView() method also is not called. Maybe you can give me a hint - thanks! :)
public class WinampControlClientPlaylist extends ListActivity {
static WinampControlClientPlaylist activity = null;
static EfficientAdapter adapter = null;
static class EfficientAdapter extends BaseAdapter {
public EfficientAdapter(Context context) {
mInflater = LayoutInflater.from(context);
}
private LayoutInflater mInflater;
#Override
public int getCount() {
return Settings.playlistlength;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null)
{
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.listview, null);
holder.text = (TextView) convertView.findViewById(R.string.playlist_title);
holder.image = (ImageView) convertView.findViewById(R.string.playlist_play);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.text.setText(Settings.playlist[position]);
if (position == Settings.playlistPosition)
{
holder.text.setTypeface(null, Typeface.ITALIC);
holder.image.setVisibility(0);
}
else
{
holder.text.setTypeface(null, Typeface.NORMAL);
holder.image.setVisibility(4);
}
return convertView;
}
static class ViewHolder {
TextView text;
ImageView image;
}
#Override
public Object getItem(int position) {
return Settings.playlist[position];
}
}
void initialize()
{
adapter = new EfficientAdapter(this);
setListAdapter(adapter);
//registerForContextMenu(getListView());
}
#Override
public void onResume()
{
super.onResume();
// REFRESH PLAYLIST
if (getListAdapter() == null && Settings.playlist != null)
initialize();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.playlist);
activity = this;
}
static void refreshItems()
{
try {
adapter.notifyDataSetChanged();
} catch (Exception e) {}
}
}
I had the same problem (ListView updates only when i scroll it, even notifyDataSetChanged didn't help). i solve it this way: just try to do your "view modifications" in thread which creates your user interface i.e.
activity.runOnUiThread(new Runnable() {
public void run() {
//do your modifications here
// for example
adapter.add(new Object());
adapter.notifyDataSetChanged()
}
});
Try calling invalidate() on your ListView.
As Franco pointed out, notifyDataSetChanged() is used to tell the ListView that the contents of its adapter have changed, not that it needs to redraw itself. You are just changing a setting that affects how something is rendered. Try calling refreshDrawableState to tell the list to redraw.
I had the same issue, and the solution for me was to call requestLayout() on the ListView.
I think there may be some problems with the adapter;
maybe it's not set.
In my experience, there was always some kind of reason which prevented the listview (and adapter) to update.
call AbsListView.invalidateViews() on your listview after BaseAdapter.notifyDataSetChanged()
I encountered the same problem, and I tried to call notifyDataSetChanged() on the adapter. Besides, I also tried to call refreshDrawableState(), invalidateViews() on the view and none of those worked. All these methods are called in the UI thread. Then I found this How to clear the views which are held in the ListView's RecycleBin? . Finally setAdapter() worked, only if I create a new adapter.
The main reason behind this is the wrong reference of the adapter on which you are calling notifyDataSetChanged();
I think you need to make sure that you are creating adapter object once and call notifyDataSetChanged() on the same object.
You can debug the object reference value at creating time of adapter object and when you are calling notifyDataSetChanged() method.
Why it works in first code ?
--- Because you are setting the values to temp List and passing it the adapter and it shows it into listview.
Why not work in second code ?
--- Because you are setting temp to adapter far before you set value into temp
second,your adapter class might not getting the updated value when you set new value to temp ..that because temp is not public or not at class level or not static.. Put the temp declaration at root level and try.
And please show your full code as much as required and Logcat if you getting any warnings than also.