I have created an app with a listview with arrayadapter that the user can dynamically populate. I store the info entered as a json string in the preferences and when I refresh the app, I get the list with the entries. The thing is that I want the image next to each entry to change after a network operation. The problem I'm facing seems to be that since the elements in the list are added dynamically, I dont seem to find a good way neither to update the imageview on the onPostExecute() method, either to be able to target each entry specifically since they share the same layout ids.
Here is my getView() method inside my adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.product_list_item, null);
holder = new ViewHolder();
holder.deviceName = (TextView) convertView
.findViewById(R.id.txt_pc_name);
holder.deviceIp = (TextView) convertView
.findViewById(R.id.txt_pdt_desc);
holder.devicePort = (TextView) convertView
.findViewById(R.id.txt_pdt_price);
holder.favoriteImg = (ImageView) convertView
.findViewById(R.id.imgbtn_favorite);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Devices device = (Devices) getItem(position);
holder.deviceName.setText(device.getName());
holder.deviceIp.setText(device.getIpOnline());
holder.devicePort.setText(device.getPortOnline() + "");
return convertView;
}
Here is my AsyncTask:
public class Connection extends AsyncTask<String, Void, String> {
private String ipOnline;
private int portOnline;
private String ipWol;
private int portWol;
private String macAddress;
private ImageView img;
private Context context;
public interface AsyncResponse {
void processFinish(String output);
}
public AsyncResponse delegate = null;
public Connection(Context mContext, ImageView Img,String IpOnline, int PortOnline, String IpWol, int PortWol, String MacAddress, AsyncResponse delegate) {
ipOnline = IpOnline;
portOnline = PortOnline;
ipWol = IpWol;
portWol = PortWol;
macAddress = MacAddress;
context = mContext;
// inflate = Inflate;
img = Img;
// spin = spinner;
this.delegate = delegate;
}
public int status;
#Override
protected String doInBackground(String... arg0) {PreferenceManager.getDefaultSharedPreferences(lastContext);
try {
Socket echoSocket = new Socket();
echoSocket.connect(new InetSocketAddress(ipOnline,portOnline),2000);
if(echoSocket.isConnected())
status = 1;
} catch (Exception e) {
status = 0;
}
if(status == 0)
return "0";
else
return "1";
}
#Override
protected void onProgressUpdate(Void... values) {}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
ImageView img = (ImageView) activity.findViewById(R.id.imgbtn_favorite);
if (status == 0)
img.setImageResource(android.R.drawable.presence_offline);
else
img.setImageResource(android.R.drawable.presence_online);
delegate.processFinish(result);
}
}
And here is my call to it:
new Connection(activity, img, product.getIpOnline(), Integer.parseInt(product.getPortOnline()), product.getIpWol(), Integer.parseInt(product.getPortWol()), product.getMacAddress(), new Connection.AsyncResponse() {
#Override
public void processFinish(String output) {
}
}).execute();
You can use view holder pattern or recyclerview .
You need to store the reference of image view in holder and can update the image with the help of this reference instead of id of view
I came up with a solution.
I created a public variable on my adapter and I'm adding all the images:
public List<ImageView> allImages = new ArrayList<ImageView>();
public List<ImageView> getAllImages(){
return this.allImages;
}
this.allImages.add((ImageView) convertView
.findViewById(R.id.imgbtn_favorite));
Then on my fragmented activity onCreateView method I deployed a delayed runnable:
(new Handler()).postDelayed(new Runnable() {
#Override
public void run() {
updateStatus();
}
}, 500);
the updateStatus() method initializes the images variable and begins network checks to determine which image to use. Then it applies it accordingly.
public void updateStatus() {
List<ImageView> images = deviceListAdapter.getAllImages();
if(count > 0 && images.size() > 0) {
for (int i = 0; i < deviceListAdapter.getCount() ; i++) {
Devices product = (Devices) deviceListAdapter.getItem(i);
if((TextUtils.isDigitsOnly(product.getPortOnline()) && TextUtils.isDigitsOnly(product.getPortWol())) && (!product.getPortOnline().isEmpty() && !product.getPortWol().isEmpty())) {
new Connection(activity, images.get(i), product.getIpOnline(), Integer.parseInt(product.getPortOnline()), product.getIpWol(), Integer.parseInt(product.getPortWol()), product.getMacAddress(), new Connection.AsyncResponse() {
#Override
public void processFinish(Boolean output) {
}
}).execute();
}
}
}
}
It might not be optimal but feel free to add a better solution.
Related
I have an ImageView in a ListView, the ImageView has a default image and while rendering, I asynchronously download images and load them into ImageViews. It works perfectly, but when I scroll down two items and scroll back, then the default images in the first and second row changing to the image of the sixth and seventh row.
I read these topics, but didn't find the solution.
Image change when i scroll gridview
My images changing during scroll in listview android
ListView images changing During Scroll
public class RssAdapter extends BaseAdapter {
private final List<SyndEntry> items;
private static Context context;
private static Map<String, Bitmap> mBitmapCache = new HashMap<String, Bitmap>();
public RssAdapter(Context context, List<SyndEntry> items) {
this.items = items;
this.context = context;
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int position) {
return items.get(position);
}
#Override
public long getItemId(int id) {
return id;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = new ViewHolder();
if (convertView == null) {
convertView = View.inflate(context, R.layout.rss_item, null);
holder.itemTitle = (TextView) convertView.findViewById(R.id.itemTitle);
holder.itemPubDate = (TextView) convertView.findViewById(R.id.itemPubDate);
holder.itemImg = (ImageView) convertView.findViewById(R.id.itemImg);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.itemTitle.setText(items.get(position).getTitle());
holder.itemPubDate.setText(items.get(position).getPublishedDate().toString());
List<SyndEnclosure> encls = items.get(position).getEnclosures();
if(!encls.isEmpty()){
holder.imageUrl = encls.get(0).getUrl();
}
if (!encls.isEmpty() && holder.imageUrl != null && !holder.imageUrl.equals("null")) {
holder.setImage(holder.imageUrl);
}
return convertView;
}
private static class ViewHolder {
TextView itemTitle;
TextView itemPubDate;
ImageView itemImg;
String imageUrl;
public void setImage(String imageUrl) {
this.imageUrl = imageUrl;
Bitmap imageBitmap = mBitmapCache.get(imageUrl);
if(imageBitmap!=null){
itemImg.setImageBitmap(imageBitmap);
} else {
AsyncHttpClient client = new AsyncHttpClient();
client.get(imageUrl, null, fileHandler);
}
}
FileAsyncHttpResponseHandler fileHandler = new FileAsyncHttpResponseHandler(context) {
#Override
public void onFailure(int statusCode, Header[] headers, Throwable throwable, File file) {
}
#Override
public void onSuccess(int statusCode, Header[] headers, File response) {
Bitmap imageBitmap = BitmapFactory.decodeFile(response.getPath());
itemImg.setImageBitmap(imageBitmap);
mBitmapCache.put(imageUrl, imageBitmap);
}
};
}
I tried to set a boolean variable to check whether my image was set or not and also tried to use SharedPreferences, but this part of my code didn't run when the image changing.
if(!encls.isEmpty()){
holder.imageUrl = encls.get(0).getUrl();
}
if (!encls.isEmpty() && holder.imageUrl != null && !holder.imageUrl.equals("null")) {
holder.setImage(holder.imageUrl);
}
It seems I solved the problem finally, I post the solution for the future. So, at this part of code
} else {
holder = (ViewHolder) convertView.getTag();
}
the holder object is not that which has to be rendered for the given row. Thus we have to set the parameters of the holder object for the required values. And it was ok, but I didn't set the image related parameters to null if there was no image. So, the two else part were missing:
if(!encls.isEmpty()){
holder.imageUrl = encls.get(0).getUrl();
} else {
holder.imageUrl = null;
}
if (!encls.isEmpty() && holder.imageUrl != null && !holder.imageUrl.equals("null")) {
holder.setImage(holder.imageUrl);
} else {
holder.itemImg.setImageResource(R.mipmap.news_icon);
}
I am trying to show a lot of images from my server. There is no conventional link since i am using AMazon s3 services. Here is my code to download the images. Since i need to reduce image size, is this better way of achiving smooth scroll or do i need to do something else
public class PinsListAdapter extends BaseAdapter
{
private Activity mContext;
private ArrayList<PingModel> pings = new ArrayList<PingModel>();
private LayoutInflater inflater;
public PinsListAdapter(Activity context, ArrayList<PingModel> pings)
{
super();
this.mContext = context;
this.pings = pings;
inflater = (LayoutInflater) this.mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount()
{
return pings.size();
}
#Override
public Object getItem(int arg0)
{
return null;
}
#Override
public long getItemId(int arg0)
{
return 0;
}
private static class ViewHolder
{
public ImageView vidImgIndicator;
public ImageView pinImg;
public ImageView progress;
}
#Override
public View getView(int position, View convertView, final ViewGroup parent)
{
final PingModel ping = pings.get(position);
ViewHolder holder = null;
if (convertView == null)
{
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.adapter_pin_row, parent, false);
holder.pinImg = (ImageView) convertView.findViewById(R.id.pinImg);
holder.progress = (ImageView) convertView.findViewById(R.id.progress);
holder.vidImgIndicator = (ImageView) convertView.findViewById(R.id.vidImgIndicator);
Animation anim = AnimationUtils.loadAnimation(mContext, R.anim.rotating_img);
holder.progress.setAnimation(anim);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
final ViewHolder mainHolder = holder;
holder.vidImgIndicator.setVisibility(View.GONE);
final String url = ping.getLocalMediaUrl(mContext);
if (url != null) /* Image is already placed */
{
if (ping.mediaAttachmentType == PingModel.PING_MEDIA_ATTACHMENT_TYPE_PHOTO)
{
if(ping.thumbnail == null)
{
ping.thumbnail = ImageUtils.getBitmapFromFile(url, 80);
}
}
else if (ping.mediaAttachmentType == PingModel.PING_MEDIA_ATTACHMENT_TYPE_VIDEO)
{
if(ping.thumbnail == null)
{
//ping.thumbnail = ThumbnailUtils.createVideoThumbnail(url, MediaStore.Images.Thumbnails.MINI_KIND );
}
if (ping.thumbnail != null)
mainHolder.vidImgIndicator.setVisibility(View.VISIBLE);
}
mainHolder.pinImg.setImageBitmap(ping.thumbnail);
}
else
{
holder.pinImg.setImageDrawable(null);
if (ping.isMediaBeingDownloaded == false)
{
AppManager.getInstance().executor.execute(new Runnable()
{
public void run()
{
ping.isMediaBeingDownloaded = true;
ApiManager.getInstance().pingManager.downloadMediaOfPingFromServer(ping);
ping.isMediaBeingDownloaded = false;
if (ping.mediaAttachmentType == PingModel.PING_MEDIA_ATTACHMENT_TYPE_PHOTO)
{
ping.thumbnail = ImageUtils.getBitmapFromFile(url, 80);
}
else if (ping.mediaAttachmentType == PingModel.PING_MEDIA_ATTACHMENT_TYPE_VIDEO)
{
ping.thumbnail = ThumbnailUtils.createVideoThumbnail(url, MediaStore.Images.Thumbnails.MINI_KIND);
}
mContext.runOnUiThread(new Runnable()
{
#Override
public void run()
{
notifyDataSetChanged();
}
});
}
});
}
}
return convertView;
}
}
Please note the executers. Is this correct and logical way of doing it or am i doing it totally wrong and i need to make any other cache type thing?
image downloading, caching, storing in a scrolling list is a very very complex situation that I advise nobody to do themselves.
Instead you should do what most apps do, use a 3rd party library specialised for the job I'll point you to 3 of the current "best" ones, pick which ever you prefer.
Picasso - https://github.com/square/picasso
Glide - https://github.com/bumptech/glide
Fresco - https://github.com/facebook/fresco
You must always load images on a background thread even the thumbnails, unless they are already in memory.
You can use Picasso and implement your own RequestHandler which downloads the image from S3, it will give you more performance and flexibility.
Help to understand whether there is a memory leak or not.
Fragment
public class NewsFragment extends Fragment {
//some code
private OnNewsFeedsContentClickListener onNewsFeedsContentClickListener = new OnNewsFeedsContentClickListener()
{
#Override
public void onClick(String sYoutubeId, ContentType type)
{
//some code
}
}
}
Adapter
public class HowToAdapter extends BaseAdapter {
public static final String TAG = "HowToAdapter";
private List<ContentSkill> mContentList = null;
private Context mContext = null;
private ImageLoader mImageLoader = null;
private OnNewsFeedsContentClickListener mOnNewsFeedsContentClickListener = null;
public HowToAdapter(Context context, List<ContentSkill> contetnList, OnNewsFeedsContentClickListener onNewsFeedsContentClickListener)
{
mContext = context;
mContentList = contetnList;
mImageLoader = new ImageLoader(mContext, ImageType.HOW_TO);
mOnNewsFeedsContentClickListener = onNewsFeedsContentClickListener;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
HowToView view = null;
if (convertView == null)
{
view = new HowToView(mContext);
}
else
{
view = (HowToView) convertView;
}
ContentSkill content = getItem(position);
if (content != null)
{
ViewHolder holder = (ViewHolder) view.getTag();
final String url = Utils.getYouTubeUrlImageFromUrl(content.getUrl());
final String youtubeId = Utils.getYoutubeIdFromUrl(content.getUrl());
mImageLoader.displayImage(url, holder.pIvThumb);
holder.pIvThumb.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
mOnNewsFeedsContentClickListener.onClick(youtubeId, ContentType.VIDEO);
}
});
}
return view;
}
}
Questions:
The adapter variable final String youtubeId will always be created,
but not killed? That is, each time getView() will be created but
the old youtubeId not clear from memory?
Each time call getView() OnClickListener will also be created, and the old
will be cleaned out of memory?
I think that the variables will be re-created each time and catch-up with the old memory.
I use ListActivity with SpecialAdapter that show some log information.
I do not use background thread but I get an error when scroll list:
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. [in ListView(16908298, class android.widget.ListView) with Adapter(class ru.FoxGSM.ui.DebugActivity$SpecialAdapter)]
Please, help me.
(FoxLog is static class that get log information, perhaps from another thread. But in list a want show FoxLog snapshot)
public class DebugActivity extends ListActivity {
private class SpecialAdapter extends BaseAdapter {
private LayoutInflater mInflater;
public SpecialAdapter(Context context) {
super();
// Cache the LayoutInflate to avoid asking for a new one each time.
mInflater = LayoutInflater.from(context);
}
public int getCount() {
return FoxLog.count();
}
public long getItemId(int index) {
return index;
}
public Object getItem(int index) {
return FoxLog.get(FoxLog.count()-index-1);
}
// Get the type of View
public View getView(int position, View convertView, ViewGroup parent) {
TextView text;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.debug_list, null);
text = (TextView)convertView.findViewById(R.id.text);
convertView.setTag(text);
} else
text = (TextView) convertView.getTag();
String s = (String) getItem(position);
if (s==null)
return convertView;
text.setText(s);
boolean isError = false;
if (s!=null && s.length()>0) {
String prefix = s.substring(0, 1);
if (prefix.equals("E") || prefix.equals("W"))
isError = true;
}
if (isError)
text.setBackgroundResource(R.color.red);
else
text.setBackgroundDrawable(null);
return convertView;
}
}
private void RefreshData() {
FoxLog.ReInit(this);
SpecialAdapter adapter = (SpecialAdapter)this.getListAdapter();
adapter.notifyDataSetChanged();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.debug);
FoxLog.ReInit(this);
SpecialAdapter adapter = new SpecialAdapter(this);
setListAdapter(adapter);
Button mExit = (Button)findViewById(R.id.exit);
mExit.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
finish();
}
});
}
}
I am creating an app that requires a ListView with an undetermined number of elements, each of which has a timer that counts down from a variable number. I am able to successfully make one of them count down, but I can't figure out how to include a timer in each element of the ListView.
I am currently using a CountDownTimer (make sure to capitalize the D if copying from the website, they have it wrong).
Any code or sources to point me in the right direction are much appreciated.
Here is my current EventAdapter class, it sets the text displayed in each ListView element's TextView. What I need to do is make the TextView count down every second. Since each element of the ListView is displaying something different, I suppose I need a way of differentiating each element.
I could just update the whole list every second, but there are other elements I have not included such as images loaded from the internet that it would be impractical to refresh every second.
private class EventAdapter extends ArrayAdapter<Event>
{
private ArrayList<Event> items;
public EventAdapter(Context context, int textViewResourceId, ArrayList<Event> items) {
super(context, textViewResourceId, items);
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
Event e = items.get(position);
if (e != null) {
TextView tv = (TextView) v.findViewById(R.id.text);
if (tv != null)
tv.setText(e.getName());
}
return v;
}
}
This is an example of the way I do it and it works perfect:
public class TestCounterActivity extends ListActivity
{
TestAdapter adapter;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Example values
ArrayList<Date> values = new ArrayList<Date>();
values.add(new Date(1482464366239L));
values.add(new Date(1480464366239L));
values.add(new Date(1470464366239L));
values.add(new Date(1460464366239L));
values.add(new Date(1450464366239L));
values.add(new Date(1440464366239L));
values.add(new Date(1430464366239L));
values.add(new Date(1420464366239L));
values.add(new Date(1410464366239L));
values.add(new Date(1490464366239L));
adapter = new TestAdapter(this, values);
setListAdapter(adapter);
}
#Override
protected void onStop()
{
super.onStop();
// Dont forget to cancel the running timers
adapter.cancelAllTimers();
}
}
And this is the adapter
public class TestAdapter extends ArrayAdapter<Date>
{
private final Activity context;
private final List<Date> values;
private HashMap<TextView,CountDownTimer> counters;
static class TestViewHolder
{
public TextView tvCounter;
}
public TestAdapter(Activity context, List<Date> values)
{
super(context, R.layout.test_row, values);
this.context = context;
this.values = values;
this.counters = new HashMap<TextView, CountDownTimer>();
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
View rowView = convertView;
if(rowView == null)
{
LayoutInflater inflater = context.getLayoutInflater();
rowView = inflater.inflate(R.layout.test_row, null);
final TestViewHolder viewHolder = new TestViewHolder();
viewHolder.tvCounter = (TextView) rowView.findViewById(R.id.tvCounter);
rowView.setTag(viewHolder);
}
TestViewHolder holder = (TestViewHolder) rowView.getTag();
final TextView tv = holder.tvCounter;
CountDownTimer cdt = counters.get(holder.tvCounter);
if(cdt!=null)
{
cdt.cancel();
cdt=null;
}
Date date = values.get(position);
long currentDate = Calendar.getInstance().getTime().getTime();
long limitDate = date.getTime();
long difference = limitDate - currentDate;
cdt = new CountDownTimer(difference, 1000)
{
#Override
public void onTick(long millisUntilFinished)
{
int days = 0;
int hours = 0;
int minutes = 0;
int seconds = 0;
String sDate = "";
if(millisUntilFinished > DateUtils.DAY_IN_MILLIS)
{
days = (int) (millisUntilFinished / DateUtils.DAY_IN_MILLIS);
sDate += days+"d";
}
millisUntilFinished -= (days*DateUtils.DAY_IN_MILLIS);
if(millisUntilFinished > DateUtils.HOUR_IN_MILLIS)
{
hours = (int) (millisUntilFinished / DateUtils.HOUR_IN_MILLIS);
}
millisUntilFinished -= (hours*DateUtils.HOUR_IN_MILLIS);
if(millisUntilFinished > DateUtils.MINUTE_IN_MILLIS)
{
minutes = (int) (millisUntilFinished / DateUtils.MINUTE_IN_MILLIS);
}
millisUntilFinished -= (minutes*DateUtils.MINUTE_IN_MILLIS);
if(millisUntilFinished > DateUtils.SECOND_IN_MILLIS)
{
seconds = (int) (millisUntilFinished / DateUtils.SECOND_IN_MILLIS);
}
sDate += " "+String.format("%02d",hours)+":"+String.format("%02d",minutes)+":"+String.format("%02d",seconds);
tv.setText(sDate.trim());
}
#Override
public void onFinish() {
tv.setText("Finished");
}
};
counters.put(tv, cdt);
cdt.start();
return rowView;
}
public void cancelAllTimers()
{
Set<Entry<TextView, CountDownTimer>> s = counters.entrySet();
Iterator it = s.iterator();
while(it.hasNext())
{
try
{
Map.Entry pairs = (Map.Entry)it.next();
CountDownTimer cdt = (CountDownTimer)pairs.getValue();
cdt.cancel();
cdt = null;
}
catch(Exception e){}
}
it=null;
s=null;
counters.clear();
}
}
Please have a look here at my blog where you will find an example on how to achieve this.
One solution is to put the TextView that represents each counter into a HashMap together with it's position in the list as the key.
In getView()
TextView counter = (TextView) v.findViewById(R.id.myTextViewTwo);
if (counter != null) {
counter.setText(myData.getCountAsString());
// add the TextView for the counter to the HashMap.
mCounterList.put(position, counter);
}
Then you can update the counters by using a Handler and where you post a runnable.
private final Runnable mRunnable = new Runnable() {
public void run() {
MyData myData;
TextView textView;
// if counters are active
if (mCountersActive) {
if (mCounterList != null && mDataList != null) {
for (int i=0; i < mDataList.size(); i++) {
myData = mDataList.get(i);
textView = mCounterList.get(i);
if (textView != null) {
if (myData.getCount() >= 0) {
textView.setText(myData.getCountAsString());
myData.reduceCount();
}
}
}
}
// update every second
mHandler.postDelayed(this, 1000);
}
}
};
after checking few ways to do that this is a creative solution i wrote .its simple and works perfectly .
the idea it to check if the Runnable that updates the data is updating the same TextView and if the TextView is related to different view the Runnablewill stop and by this way there will be no extra Thread's in the background so there will be no blinking text's or memory leak.
1 . inside your getView() add each TextView tag with his position .
text = (TextView) view
.findViewById(R.id.dimrix);
text.setTag(position);
2 . create class that implements Runnable so we can pass parameters .
public class mUpdateClockTask implements Runnable {
private TextView tv;
final Handler mClockHandler = new Handler();
String tag;
public mUpdateClockTask(TextView tv,
String tag) {
this.tv = tv;
this.tag = tag;
}
public void run() {
if (tv.getTag().toString().equals(tag)) {
// do what ever you want to happen every second
mClockHandler.postDelayed(this, 1000);
}
}
};
so what happen here is unless the TextView is not equals to the original tag the Runnable will stop .
3 . go back to your getView()
final Handler mClockHandler = new Handler();
mUpdateClockTask clockTask = new mUpdateClockTask(text,
activeDraw, text.getTag().toString());
mClockHandler.post(clockTask);
that's it , work's perfect !
Here is another solution of using ListView with multiple CountDownTimer. Firstly, we create a class MyCustomTimer that holds the CountDownTimer:
public class MyCustomTimer{
public MyCustomTimer() {
}
public void setTimer(TextView tv, long time) {
new CountDownTimer(time, 1000) {
public void onTick(long millisUntilFinished) {
//Set formatted date to your TextView
tv.setText(millisUntilFinished);
}
public void onFinish() {
tv.setText("Done!");
}
}.start();
}
}
Then, initilize the created class in your adapter:
public class MyAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private MyCustomTimer myTimer;
private ArrayList<Item> myItems;
public MyAdapter(Context context, ArrayList<Item> data) {
mInflater = LayoutInflater.from(context);
myTimer= new MyCustomTimer();
myItems = data;
}
//... implementation of other methods
#Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = mInflater.inflate(R.layout.listview_row, null);
TextView tvTimer = (TextView) convertView.findViewById(R.id.textview_timer);
TextView tvName = (TextView) convertView.findViewById(R.id.textview_name);
Item item = data.get(position);
tvName.setText(item.getName());
myTimer.setTimer(tvTimer, item.getTime());
return convertView;
}
}