This is my main activity:
adapter.setNotifyOnChange(true);
lv = (ListView) findViewById(R.id.listView1);
lv.setAdapter(adapter);
public void onClick(View view) {
adapter.add(text);
new DoSomethingWithListViewChild(lv, index).execute();
}
And this is the AsyncTask DoSomethingWithListViewChild:
public class DoSomethingWithListViewChild extends AsyncTask<Void, Void, Void> {
TextView tv;
public DoSomethingWithListViewChild (ListView lv, int index) {
View v = lv.getChildAt(index - lv.getFirstVisiblePosition());
tv = (TextView) v.findViewById(R.id.textTitle);
}
#Override
protected Void doInBackground(Void... params) {
// Do something with TV
return null;
}
The problem is that lv is still empty when the code View v = lv.getChildAt(index - lv.getFirstVisiblePosition()); is being executed.
I tried some synchronization with lock objects but I'm ending with deadlocks or other errors.
How do I wait untill lv is not empty?
Edit:
Ok, I tried this:
...
ConditionVariable lock = new ConditionVariable(false);
...
public void onClick(View view) {
adapter.add(text);
lock.block();
new DoSomethingWithListViewChild(lv, index).execute();
}
And in my custom adapter:
public class MyAdapter extends ArrayAdapter<SpannableString> {
ConditionVariable lock;
public MyAdapter(Context context, int layout_resource_id, ConditionVariable lock) {
...
this.lock = lock;
...
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
...
lock.open();
return convertView;
}
}
But the programm just hangs. I need to find the exact moment when lv is populated to open the lock. It clearly not in the getView method.. It even don't reach the lock.open();
May be needs to call adapter.notifyDayaSetChanged() after adapter.add(text)
Related
I am trying to update the contents of the list object which has been passed to the adapter inorder to create a GridView. Here is the code:
public class GameActivity extends Activity {
List<String> nums;
GridView sudoku_grid;
ArrayAdapter<String> adapter;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_temp);
sudoku_grid=(GridView)findViewById(R.id.sudoku_grid);
nums= Arrays.asList("1","2","3","5","7","8","2","3","1","9","3","3","1");
adapter=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,nums);
sudoku_grid.setAdapter(adapter);
}
public void trial(View view)
{
Runnable r=new Runnable() {
#Override
public void run() {
nums.add("7");
adapter.notifyDataSetChanged();
}};
new Thread(r).start();
}
trial() executes every time a button is clicked. As soon as the thread in trial() starts, instead of the contents being updated, it results in starting the previous activity. Can anyone tell me where am I going wrong?
You need to pass the nums object again to the adapter.
Create a class that extends ArrayAdapter<String>
as follows:
class MyArrayAdapter extends ArrayAdapter<String>{
List<String> nums;
public MyArrayAdapter(Context context,int itemID,List<String> nums){
this.nums=nums;
}
#ovverride
public int getCount() {
return nums.size();
}
public void changeObjects(List<String> nums){
this.nums=nums;
this.notifyDataSetChanged();
}
public View getView(int position, View convertView, ViewGroup parent){
//.... Implement own code
}
}
Let me know if there is any more problem or queries.
I think you can not run notifyDataSetChanged() in your own thread. It needs to run on UI Thread.
Try:
GameActivity.this.runOnUiThread(new Runnable() {
...
instead.
In my app, I am showing a list of items downloaded from internet (of type pItem). I am using a custom ArrayAdapter that extends ArrayAdapter<pItem> which is basically like this:
public class PArrayAdapter extends ArrayAdapter<pItem> {
private List<pItem> pList = new ArrayList<pItem>();
public PArrayAdapter(Context context) {
super(context, R.layout.p_row);
this.context = context;
}
public void SetList(List<PItem> pl){
pList = pl;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = convertView;
if (rowView == null) {
LayoutInflater vi;
vi = LayoutInflater.from(getContext());
rowView = vi.inflate(R.layout.p_row, null);
}
// ...
return rowView;
}
}
Now, normally when creating a custom ArrayAdapter, the list of items is set in the constructor. But in my case, as I dynamically fetch the items in an AsyncTask (the list is changing constantly), I separated the setting of the items with the creation of the adapter.
In my main activity, this is what I do: (only the main part of the code)
public class MainPageActivity extends SherlockFragmentActivity implements
OnScrollListener {
// List of items
private List<pItem> pList = new ArrayList<pItem>();
// Adapter
private arrayAdapter pAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_page);
//...
listView = (ListView) findViewById(R.id.p_list);
adpater = new PArrayAdapter(getApplicationContext());
new ASTask().execute("");
}
private class ASTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... args) {
// Add the downloaded items to list
pList.addAll(Downloader.GetItems());
return "";
}
#Override
protected void onPostExecute(String result) {
Log.d("Main", String.valueOf(pList.size())); // Shows the right number
// Show Items
pAdapter.setList(pList);
listView.setAdapter(pAdapter);
Handler mHandler = new Handler(Looper.getMainLooper());
Runnable rn = new Runnable() {
#Override
public void run() {
pAdapter.notifyDataSetChanged();
}
};
mHandler.post(rn);
}
#Override
protected void onPreExecute() {}
#Override
protected void onProgressUpdate(Void... values) {}
}
}
Here is the problem: When I run it, nothing is shown in the listView. It doesn't give any errors.
But if I set the list in the constructor of PArrayAdapter, everything works fine and I see the items.
What am I doing wrong here?
in onPostExecute
remove this code
Handler mHandler = new Handler(Looper.getMainLooper());
Runnable rn = new Runnable() {
#Override
public void run() {
pAdapter.notifyDataSetChanged();
}
};
mHandler.post(rn);
add this line only
pAdapter.notifyDataSetChanged();
Add
pAdapter.notifyDataSetChanged();
at the end of onPostExecute() method.
Try like this:
#Override
protected void onPostExecute(String result) {
Log.d("Main", String.valueOf(pList.size())); // Shows the right number
// Show Items
pAdapter.setList(pList);
listView.setAdapter(pAdapter);
pAdapter.notifyDataSetChanged();
}
OK. I found what was my problem. It was actually a rookie mistake:
Instead of assigning the input list to the list inside adapter:
pList = pl;
I should have add all the items like this:
pList.addAll(pl);
This solved the problem.
i have this listview update:
timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
display.elemek.clear();
em1.new AsyncEmBase().execute();
}
}, 0, 1000);
public class AsyncEmBase extends AsyncTask<Void, ListView, Void> {
protected void onPreExecute() {
// display.dataAdapter.clear();
}
#Override
protected Void doInBackground(Void... params) {
readInputRegisters(); /*this only makes display.elemek.add("SomeString")*/
return null;
}
protected void onPostExecute(Void unsed) {
if (display.dataAdapter == null) {
display.dataAdapter = new ArrayAdapter<String>(
display.activity, android.R.layout.simple_list_item_1,
display.elemek);
display.lv.setAdapter(display.dataAdapter);
} else {
display.dataAdapter.notifyDataSetChanged();
}
}
}
it runs smootly but after a while it stops and says:
11-27 15:21:05.769: E/AndroidRuntime(17991): java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(2131296261, class android.widget.ListView) with Adapter(class android.widget.ArrayAdapter)]
i have tried everithing to update the listview but nothing is good.
Please help me!
edit1: new error: Invalid index 7, size is 0
But i clear the elemek arraylist before starting to insert the datas
display.java:
public class display extends Fragment {
public static TextView ain1, ain2, ain3, ain4, dout1, dout2, din1, din2,
din3, din4, cin1, cin2, cin3, cin4, k1, k2, k3, k4, k5, k6, k7, k8;
public static Activity activity;
public static ListView lv;
public static ArrayList<String> elemek;
public static ArrayAdapter<String> dataAdapter;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.display, container, false);
ain1 = (TextView) rootView.findViewById(R.id.textView2);
activity = getActivity();
lv = (ListView) rootView.findViewById(R.id.listView1);
elemek = new ArrayList<String>();
return rootView;
}
display.activity.runOnUiThread(...) is useless because onPostExecute() is already executed inside the UI thread
and I don't think you have to call notifyDataSetChanged() because you're setting the adapter just before.
Remove all your Thread because you dont need uiThread in onPostExecute() and add like this :
if ( display.dataAdapter == null) {
display.dataAdapter = new ArrayAdapter<String>(
display.activity,
android.R.layout.simple_list_item_1, display.elemek);
display.lv.setAdapter(display.dataAdapter);
}else{
display.dataAdapter.notifyDataSetChanged();
}
I have an Android application with a ListView in it, the ListView will setup fine but now I want a image in the ListView to be clickable. I do this by using 2 classes, the Activity class (parent) and an ArrayAdapter to fill the list. In the ArrayAdapter I implement a OnClickListener for the image in the list that I want to be clickable.
So far it all works.
But now I want to run a function from the activity class when the onClick, for the image in the list, is run but I do not know how. Below are the 2 classes that I use.
First the Activity class:
public class parent_class extends Activity implements OnClickListener, OnItemClickListener
{
child_class_list myList;
ListView myListView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// setup the Homelist data
myList = new child_class_list (this, Group_Names, Group_Dates);
myListView = (ListView) findViewById(R.id.list);
// set the HomeList
myListView.setAdapter( myList );
myListView.setOnItemClickListener(this);
}
void function_to_run()
{
// I want to run this function from the LiscView Onclick
}
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3)
{
// do something
}
}
And the ArrayAdapter from where I want to call a function from the Activity class:
public class child_class_list extends ArrayAdapter<String>
{
// private
private final Context context;
private String[] mName;
private String[] mDate;
public child_class_list (Context context, String[] Name, String[] Date)
{
super(context, R.layout.l_home, GroupName);
this.context = context;
this.mName = Name;
this.mDate = Date;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.l_home, parent, false);
ImageView selectable_image = (ImageView) rowView.findViewById(R.id.l_selectable_image);
selectable_image.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
// I want to run the function_to_run() function from the parant class here
}
}
);
// get the textID's
TextView tvName = (TextView) rowView.findViewById(R.id.l_name);
TextView tvDate = (TextView) rowView.findViewById(R.id.l_date);
// set the text
tvName.setText (mName[position]);
tvDate.setText (mDate[position]);
return rowView;
}
}
If anyone knows how to run the function in the activity class from the arrayadapter or how to set the image onClickListener in the Activity Class I would greatly apriciate the help.
Inside onClick() Do something like this:
((ParentClass) context).functionToRun();
Just for clarity to expand on provided answers
In a BaseAdapter you can get the parent class by calling this.getActivity();If you then typecast this to the actual activity class you can then call a function as per #AdilSoomro answer below so you actually end up with something like this
public class MyAdapter extends BaseAdapter<Long> {
public MyAdapter(Activity activity,
TreeStateManager<Long> treeStateManager, int numberOfLevels) {
super(activity, treeStateManager, numberOfLevels);
}
#Override
public void handleItemClick(final View view, final Object id) {
((MyActivity) this.activity).someFunction();
}
}
Then just declare someFunction in MyActivity to do what you want
protected void someFunction(){
// Do something here
}
I should write in the title instead of 'doesn't work' something like 'I
don't know how to do it' but the first version feels better :).
What I am trying to do is the following:
Download the xml from the web, parse it and create ArrayList of some
objects (done and working)
Display the objects using custom Adapter (doesn't work)
The second one works if I add the items to my ArrayList before I add it to
the view using
m_orderAdapter = new OrderAdapter(this,m_orders); //code for orderadapter
below
setListAdapter(m_orderAdapter);
I have found on the web something like this: (in my onCreate method)
handler = new Handler();
viewOrders = new Runnable(){
#Override
public void run() {
getOrders();
}
};
new Thread(){
#Override
public void run(){
handler.post(viewOrders);
}
}.start();
then, the following code for the methods:
private void getOrders(){
try{
OrderManager om = new OrderManager();
m_orders = om.getOrdersFromWeb();
Log.i("ARRAY", ""+ m_orders.size());
} catch (Exception e) {
Log.e("BACKGROUND_PROC", e.getMessage());
}
runOnUiThread(returnRes);
}
OrderManager downloads and parse the xml into Order objects and returns
array list of those. Then I set this list to my member array list m_orders.
Once downloading and parsing is done I run returnRes method on the ui thread
using runOnUiThread method
private Runnable returnRes = new Runnable() {
#Override
public void run() {
if(m_orders != null && m_orders.size() > 0){
Log.i("ORDER",m_orders.get(0).getOrder_id());
setListAdapter(m_orderAdapter);
m_orderAdapter.notifyDataSetChanged();
}
m_orderAdapter.notifyDataSetChanged();
}
};
and I call notifyDataSetChanged() on my adapter.
The view I do all this stuff extends ListView and the code for the adapter
itself is listed below:
public class OrderAdapter extends BaseAdapter{
private Context ctx;
private List<Order> orders;
public OrderAdapter(Context ctx, List<Order> orderLst){
this.ctx = ctx;
this.orders = orderLst;
}
#Override
public int getCount() {
return orders.size();
}
#Override
public Object getItem(int pos) {
return orders.get(pos);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Order o = orders.get(position);
return new OrderListAdapterView(this.ctx,o);
}
}
When I debug I have the data inside my m_orders list but when I call
notifyDataSetChanged nothing happens, I've read that I have to execute that
on the UI thread which I think I do. So whats the problem ?
any help highly appreciated, or maybe just a link to the nice tutorial on
the web explaining this issue on how to update the list view at runtime ?
Hey Arutha - why don't you see my answer for this post - I think it is what you need.
Or just let me repost it here
You can extend ArrayAdapter. Here's code example for you. In this example - SearchItem is some custom POJO. Basically you need to override getView() method to build your row by inflating row layout and then populating values based on List of items and current position
class SearchItemsAdapter extends ArrayAdapter<SearchItem> {
Activity context;
List<SearchItem> items;
SearchHeader header;
#SuppressWarnings("unchecked")
public SearchItemsAdapter(final Activity context,
final Map<SearchHeader, List<SearchItem>> result) {
super(context, R.layout.item, (List) ((Object[]) result.values()
.toArray())[0]);
this.context = context;
this.header = result.keySet().iterator().next();
this.items = result.get(this.header);
}
#Override
public View getView(final int position, final View convertView,
final ViewGroup parent) {
final View view = this.context.getLayoutInflater().inflate(
R.layout.item, null);
final SearchItem item = this.items.get(position);
((TextView) view.findViewById(R.id.jt)).setText(item.jt);
((TextView) view.findViewById(R.id.dp)).setText(item.dp);
((TextView) view.findViewById(R.id.cn)).setText(item.cn);
((TextView) view.findViewById(R.id.loc)).setText(item.loc.name);
final TextView body = ((TextView) view.findViewById(R.id.e));
body.setText(item.e);
body.setTag(item.src[0]);
((TextView) view.findViewById(R.id.src)).setText(item.src[1]);
return view;
}
}