I'm a beginner in android development and developing a simple notepad application where in I've a Navigation Drawer. Everything is ok, I am able to see navigation drawer and it works fine. now I'm stuck with how to update the counter value in drawer. Nav Drawer displays the categories of the notes and their counts.
How can I update the counter values in the Navigation Drawer when a note is either deleted or added (Notes are saved in db with category value - So I can get the count by doing a simple query ). I've searched thoroughly on internet but couldn't find any example to do it.
Any help would be appreciated.
You can add an extra method like loadData() in your adapter class and write your codes inside this method. Just get your current data(no. of notes/trash) from database and set it to specific navigation drawer item and finally call notifyDataSetChanged() to refresh the dataset.
public class NavDrawerListAdapter extends BaseAdapter {
public NavDrawerListAdapter(Context context, ArrayList<NavDrawerItem> navDrawerItems)
{
this.context = context;
this.navDrawerItems = navDrawerItems;
}
#Override
public int getCount()
{
return navDrawerItems.size();
}
#Override
public Object getItem(int position) {
return navDrawerItems.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Write your code
return convertView;
}
public void loadData()
{
DatabaseHelper db = new DatabaseHelper(context);
// Notes and trash counts
int countNotes = db.getAllActiveNotes().size();
int countTrash = db.getAllTrashNotes().size();
String strCountNotes = String.valueOf(countNotes);
String strCountTrash = String.valueOf(countTrash);
// Set counter value to Navigation drawer items
navDrawerItems.get(0).setCount(strCountNotes);
navDrawerItems.get(1).setCount(strCountTrash);
notifyDataSetChanged();
}
}
Finally, call loadData() from method onDrawerOpened() where navigation drawer implemented most probably in your main FragmentActivity class. As a result, every time when you open the navigation drawer you will see the updated counter value on drawer items.
public void onDrawerOpened(View drawerView) {
// Update notes and trash counter
adapter.loadData();
getActionBar().setTitle(mDrawerTitle);
invalidateOptionsMenu();
}
Hope this will help~
Navigation Drawer is getting items from adapter (since you have custom items, I assume you have your own custom adapter). So once you do any action with the data your adapter serving, you should just push new data to your adapter and call notifyDataSetChanged().
EDIT How this adapter can looks like:
public class CustomAdapter extends BaseAdapter {
private ArrayList<Object> data;
#Override
public int getCount() {
//do stuff
}
#Override
public Object getItem(int position) {
//do stuff
}
#Override
public long getItemId(int position) {
//do stuff
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//do stuff
}
public void updateData(Cursor cursor){
data = //fetch data from Cursor
notifyDataSetChanged();
}
}
Related
I am calling an api that returns json. I parse the json into an arraylist of objects. I have a class that i created to randomly select an element from the list and remove it and display it in the viewpager. I have the viewpager and pageradapter set up and asyncloader to create the dataset. I get a runtime exception with the following. If i change the getCount() to return a static number then it works as i want it to but it doesnt accomplish my requirements that i listed below.
The application's PagerAdapter changed the adapter's contents without calling PagerAdapter#notifyDataSetChanged! Expected adapter item count: 14, found: 12 Pager id
I totally understand why it is happening. The following code is why.
random element class: this method provides the random object i want to display in the viewpager at the same time removing from the list so i do not retrieve it again.
public T spin() throws IllegalStateException, NullPointerException {
if (list == null)
throw new NullPointerException("A list has not been set.");
if (list.size() > 0) {
int i = rand.nextInt(list.size());
T b = list.remove(i);
return b;
} else {
throw new IllegalStateException("There are no more elements left. Please query more.") ;
}
}
This is my inner pageradapter class - r is the object of the randomelement class:
private class RPagerAdapter extends PagerAdapter {
private Context context;
public RPagerAdapter(Context context) {
this.context = context;
}
#Override
public int getCount() {
return r.size();
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
ViewHolder mViewHolder = new ViewHolder(context);
mViewHolder.setItem(r.spin());
container.addView(mViewHolder);
return mViewHolder;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
What im trying to accomplish is the following:
The user can only swipe forward (to the left) thus the spin method will be invoked correctly. (priority)
When the the spin method returns an exception the loader is called to load more data. (not a priority right now)
The user cannot be allowed to swipe right because the functionality of the app is for the user to only see a random element once (which the randomelement class accomplishes) so i need the viewpager to understand to only move in one direction meaning a new random element is displayed.
Please let me know if there is a different way that i can implement the above or point me in the right android apis to use.
A call to the PagerAdapter method startUpdate(ViewGroup) indicates
that the contents of the ViewPager are about to change. One or more
calls to instantiateItem(ViewGroup, int) and/or destroyItem(ViewGroup,
int, Object) will follow, and the end of an update will be signaled by
a call to finishUpdate(ViewGroup).
Therefore, you should only perform your update in the list in finishUpdate(ViewGroup)
forget about your whole spin structure, try something like this, pseudo code only:
private class RPagerAdapter extends PagerAdapter {
private Context context;
private List<T> items;
public RPagerAdapter(Context context, List<T> items) {
this.context = context;
this.items = items;
}
#Override
public int getCount() {
return items.size();
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
ViewHolder mViewHolder = new ViewHolder(context);
mViewHolder.setItem(items.get(position));
container.addView(mViewHolder);
container.setTag(String.valueOf(position));
return mViewHolder;
}
#Override
public void finishUpdate(ViewGroup container) {
int position = Integer.parseInt(container.getTag());
//remove all items before position
//add items at the end if you want
notifyDataSetChanged();
viewPager.setCurrentItem(0, false);
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
background
When choosing an item from a listView, I change its data and call notifyDataSetChanged.
The problem
Since it's the same listView, when I click the item, the effect stays for the view that will be used after the notifyDataSetChanged.
This is especially noticeable on Android Lollipop, where the ripple can continue after the listView gets refreshed with new data.
The code
Here's a sample code showing the problem:
public class MainActivity extends ActionBarActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView=(ListView)findViewById(R.id.listView);
listView.setAdapter(new BaseAdapter()
{
int pressCount=0;
#Override
public int getCount()
{
return 100;
}
#Override
public Object getItem(int position)
{
return null;
}
#Override
public long getItemId(int position)
{
return 0;
}
#Override
public View getView(int position,View convertView,ViewGroup parent)
{
View rootView=convertView;
if(rootView==null)
{
rootView=LayoutInflater.from(MainActivity.this).inflate(android.R.layout.simple_list_item_1,parent,false);
rootView.setBackgroundResource(getResIdFromAttribute(MainActivity.this,android.R.attr.selectableItemBackground));
rootView.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
pressCount++;
notifyDataSetChanged();
}
});
}
TextView tv=(TextView)rootView;
tv.setText("text:"+(pressCount+position));
return rootView;
}
});
}
public static int getResIdFromAttribute(final Activity activity,final int attr)
{
if(attr==0)
return 0;
final TypedValue typedvalueattr=new TypedValue();
activity.getTheme().resolveAttribute(attr,typedvalueattr,true);
return typedvalueattr.resourceId;
}
}
The question
How can I temporarily stop the selection effect till the next time anything is clicked on the listView (but also resume allowing it for the next time the user clicks an item) ?
OK, I've found the answer. It seems it's a known issue, and the solution is quite simple (shown here) :
ViewCompat.jumpDrawablesToCurrentState(view);
Weird thing is, it works for me only when I call it via Handler.post(...) .
Wonder why (as the view is already during animation), and if there's a better solution.
Summary: I need a way to trigger my calculate() function within my main activity when an item is added or removed from my ListView
Background:
My android application fills a listview with list items. A list item contains a textview and an imagebutton (delete) that removes the item from the list on click. I use a custom adapter to keep track of changes in the list. This all works fine.
In my main acticity, some calculations take place based on the values in the list in a function called calulate(). I want to call this function whenever an item is added or deleted from the list. However, I don't know if this is possible and how to implement such a function.
I noticed that it is possible to add an observer using registerDataSetObserver() that will be notified when notifyDataSetChanged() is called. However, I'm not sure if this is what I need and how to implement this. Any help or suggestions are more than welcome.
Here is my CustomListAdapter:
public class CustomListAdapter extends BaseAdapter {
static final String TAG = "CustomListAdapter";
private Context context;
ArrayList <String> listArray;
LayoutInflater inflater;
public CustomListAdapter(Context context, List <String> inputArray) {
super();
this.context = context;
this.listArray = (ArrayList<String>) inputArray;
}
#Override
public int getCount() {
return listArray.size(); // total number of elements in the list
}
#Override
public String getItem(int i) {
return listArray.get(i); // single item in the list
}
#Override
public long getItemId(int i) {
return i; // index number
}
#Override
public View getView(int position, View convertView, final ViewGroup parent) {
View V = convertView;
if(V == null) {
LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
V = vi.inflate(R.layout.selected_drug_list_item, null);
}
//place text in textview
String listItem = listArray.get(position);
TextView textView = (TextView) V.findViewById(R.id.selectedDrugName);
textView.setText(listItem);
ImageButton deleteSelectedDrugButton = (ImageButton) V.findViewById(R.id.deleteSelectedDrugButton);
deleteSelectedDrugButton.setTag(position);
//Listener for the delete button. Deletes item from list.
deleteSelectedDrugButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
//re
Integer index = (Integer) view.getTag();
listArray.remove(index.intValue());
notifyDataSetChanged();
}
});
return V;
}
public void add(String input) {
listArray.add(input);
notifyDataSetChanged();
Log.v(TAG, input + " added to list");
}
public void remove(String input){
listArray.remove(input);
notifyDataSetChanged();
Log.v(TAG, input + " added to list");
}
}
Here is how my ListView is initialized in my onCreate() method.
selectionListView = (ListView) findViewById(R.id.selectionListView);
selectionAdapter = new CustomListAdapter(this,myListItems);
selectionListView.setAdapter(selectionAdapter);
If any other code fragment is required, I'll happily provide it.
You may create Interfece that will be implemented by Your Main Activity and passed to Adapter (eg. in constructor)
public interface SomeInterface
{
public void foo();
}
Add SomeInterface object in Your Adapter
SomeInterface responder=null;
public CustomListAdapter(Context context, List <String> inputArray, SomeInterface responder) {
super();
this.context = context;
this.listArray = (ArrayList<String>) inputArray;
this.responder=responder;
}
public void add(String input) {
listArray.add(input);
notifyDataSetChanged();
Log.v(TAG, input + " added to list");
responder.foo();
}
public void remove(String input){
listArray.remove(input);
notifyDataSetChanged();
Log.v(TAG, input + " added to list");
responder.foo();
}
and implements SomeInterface in Your MainActivity
public class MainActivity extends Activity implements SomeInterface
{
...
public void foo()
{
//do whatever
}
private initializeAdapter()
{
CustomListAdapter adapter=new Adapter(this, someArray, this);
}
}
You can create a callback interface with a simple method, like stuffHappened(). Then, let your activity implement that interface. Now you can add a constructor argument which has as type the callback interface, pass the activity in, keep it as a member variable on the adapter and call the stuffHappened() method whenever you need to send feedback to your activity.
I have an Activity that hosts multiple fragments using the actionbar's tab functionality. One of those fragments contains a ListView. Upon this tab being selected, I'd like to select a certain item.
To do this programmatically, I use the following code (where calls is the ListView)
private void selectItem(int position)
{
long itemId = calls.GetItemIdAtPosition(position);
calls.PerformItemClick(calls, position, itemId);
}
If this ListView has been rendered, and I'm calling this, no problem. However, if I call it from onResume, then the code executes but nothing is selected in the end. I figure this is because at the point where I'm calling selectItem, not all items of the ListView have been rendered yet. If however I start off a background thread, sleep for a couple hundred milliseconds, then run the same code (in the ui thread of course), everything is fine, but this is an ugly hack.
Now you might be wondering, "why isn't he using calls.setSelection"? The thing is, I'm using a custom layout that performs expansion - so I need to actually click on the item I want selected (which in turn triggers the layout expansion for the item selected). However, I can call the code that is performed on PerformItemClick directly, the results will be the same (the layout expansion isn't performed).
Isn't there any way for me to catch the "Listview has finished rendering all viewable items" point in time, and then execute my selectItem call at that point? In ASP.NET, I have an event on every UI item telling me when it is done rendering, so I do item selection at that point but I haven't found anything.
Regards
Stephan
Here's the Adapter I'm using
public class ActiveCallsAdapter: ObservableAdapter<Call>
{
public ActiveCallsAdapter(Activity activity, ObservableCollection<Call> calls)
: base(activity, calls)
{
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var item = items[position];
var view = (convertView ?? context.LayoutInflater.Inflate(Resource.Layout.Call, parent, false)) as LinearLayout;
//View view = convertView;
//if (view == null) // no view to re-use, create new
// view = context.LayoutInflater.Inflate(Resource.Layout.Call, null);
SetTextView(view, Resource.Id.CallerName, item.CallerName);
SetTextView(view, Resource.Id.CallerNumber, item.CallerNumber);
SetTextView(view, Resource.Id.CallStatus, item.State.ToString());
SetTextView(view, Resource.Id.CallDuration, item.Duration);
return view;
}
public void Update(LinearLayout view, Call item)
{
SetTextView(view, Resource.Id.CallerName, item.CallerName);
SetTextView(view, Resource.Id.CallerNumber, item.CallerNumber);
string identifier = "callState_" + item.State.ToString();
int resourceId = Application.Context.Resources.GetIdentifier(identifier, "string", Application.Context.PackageName);
string callStateString = item.State.ToString();
if (resourceId != 0)
{
try
{
callStateString = Application.Context.Resources.GetString(resourceId);
}
catch (Exception e)
{
AndroidLogModel.Model.AddLogMessage("ActiveCallsAdapter", "Unable to find call state string with resource id " + resourceId + " state string: " + identifier, 3);
}
}
SetTextView(view, Resource.Id.CallStatus, callStateString);
//SetTextView(view, Resource.Id.CallDuration, item.Duration);
}
public void UpdateDuration(LinearLayout view, Call item)
{
SetTextView(view, Resource.Id.CallDuration, item.Duration);
}
}
And the base class of that adapter
public class ObservableAdapter<T>: BaseAdapter<T>
{
protected readonly Activity context;
protected readonly ObservableCollection<T> items;
public ObservableAdapter(Activity context, ObservableCollection<T> collection)
{
this.context = context;
this.items = collection;
//this.collection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(collection_CollectionChanged);
this.items.CollectionChanged += (sender, e) => NotifyDataSetChanged();
}
void collection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
NotifyDataSetChanged();
}
public override T this[int position]
{
get { return items[position]; }
}
public override int Count
{
get { return items.Count; }
}
public override long GetItemId(int position)
{
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var item = items[position];
var view = (convertView ?? context.LayoutInflater.Inflate(Resource.Layout.Call, parent, false)) as LinearLayout;
// configure view here
return view;
}
protected void SetTextView(LinearLayout view, int id, string text)
{
var textView = view.FindViewById<TextView>(id);
if (textView != null)
textView.SetText(text, TextView.BufferType.Normal);
}
}
My Mono skills are limited so I don't know if I fully understood your adapter, anyway I've adapted some old code and made an adapter that expands a single item when click, also it will move the ListView in onResume to a desired position:
private static class CustomAdapter extends BaseAdapter {
// the data
private ArrayList<String> mData;
// an int pointing to a position that has an expanded layout,
// for simplicity I assume that you expand only one item(otherwise use
// an array or list)
private int mExpandedPosition = -1; // -1 meaning no expanded item
private LayoutInflater mInflater;
public CustomAdapter(Context context, ArrayList<String> items) {
mInflater = LayoutInflater.from(context);
mData = items;
}
public void setExpandedPosition(int position) {
// if the position equals mExpandedPosition then we have a click on
// the same row so simply toggle the row to be gone again
if (position == mExpandedPosition) {
mExpandedPosition = -1;
} else {
// else change position of the row that was expanded
mExpandedPosition = position;
}
// notify the adapter
notifyDataSetChanged();
}
#Override
public int getCount() {
return mData.size();
}
#Override
public String getItem(int position) {
return mData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.ad_expandedelement,
parent, false);
}
((TextView) convertView.findViewById(R.id.textView1))
.setText(getItem(position));
// see if there is an expanded position and if we are at that
// position
if (mExpandedPosition != -1 && mExpandedPosition == position) {
// if yes simply expand the layout
convertView.findViewById(R.id.button1).setVisibility(
View.VISIBLE);
} else {
// this is required, we must revert any possible changes
// otherwise the recycling mechanism will hurt us
convertView.findViewById(R.id.button1).setVisibility(View.GONE);
}
return convertView;
}
}
The onListItemClick will simply be:
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// set the expanded(or collapsed if it's a click on the same row that
// was previously expanded) row in the adapter
((CustomAdapter) getListView().getAdapter())
.setExpandedPosition(position);
}
and in onResume will have:
#Override
protected void onResume() {
super.onResume();
// set the position to the desired element
((CustomAdapter) getListView().getAdapter()).setExpandedPosition(15);
// set the selection to that element so we can actually see it
// this isn't required but has the advantage that it will move the
// ListView to the desired
// position if not visible
getListView().setSelection(15);
}
The R.layout.ad_expandedelement is a simple vertical LinearLayout with a TextView and an initially hidden(visibility set to gone) Button. For this Button I change the visibility to simulate expanding/collapsing a row in the ListView. You should be able to understand my code, if you want I can post on github the full sample.
While I'm not sure of the exact equivalent in C#/Mono, the Android framework provides a callback on Activity called onWindowFocusChanged() that indicates the period when the Window associated with a given Activity is visible to the user. You may have better luck waiting to call your selection method until that time, as the ListView should be measured and laid out by that point. In Java, it would be something like this:
#Override
public void onWindowFocusChanged (boolean hasFocus) {
if (hasFocus) {
selectItem(position);
}
}
You may need to have a bit more logic in there, this callback is directly associated with window focus and isn't a true lifecycle method. I can get called multiple times if you are displaying Dialogs or doing other similar operations.
I have a listview and a button in my layout file. I'am adding items to listview on click of that button. The listview should be empty when the activity is started but it should grow by adding the items to it.
This is my code inside onCreate() :
list = (ListView)findViewById(R.id.inverterListView);
adapter = new ArrayAdapter<String>(InverterList.this, R.layout.inverters_list_row, R.id.inverterNumberTextViewInPanelListRow);
list.setAdapter(adapter);
And here iam adding the items to listview onclick of a button.
adapter.add(inverterNo);
adapter.notifyDataSetChanged();
This works fine. Can anyone guide me to delete custom listview item ? Thanks in advance.
If you know the position of the item you can do this:
Object item = adapter.getItem(position);
adapter.remove(item);
adapter.notifyDataSetChanged();
You may write your own adapter extends BaseAdapter and implement all you need methods.
It is example of my adapter:
public class PeopleUserAdapter extends BaseAdapter
{
private List<User> users;
private int viewResourceId;
private Context context;
public PeopleUserAdapter(Context context, int viewResourceId)
{
this.context = context;
this.viewResourceId = viewResourceId;
this.users = new ArrayList<User>();
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
UserItemHolder holder;
if (convertView == null)
{
convertView = LayoutInflater.from(context).inflate(viewResourceId, parent, false);
holder = new UserItemHolder(convertView);
}
else holder = (UserItemHolder) convertView.getTag();
User user = getItem(position);
holder.name.setText("#" + user.getLogin());
return convertView;
}
#Override
public int getCount()
{
return users.size();
}
#Override
public User getItem(int position)
{
return users.get(position);
}
#Override
public long getItemId(int position)
{
return getItem(position).hashCode();
}
public void clear()
{
users.clear();
}
public void addAll(Collection<User> users)
{
this.users.addAll(users);
notifyDataSetChanged();
}
public void replace(Collection<User> users)
{
clear();
addAll(users);
}
public static PeopleUserAdapter init(Context context)
{
return new PeopleUserAdapter(context, R.layout.item_user);
}
}
adapter.remove(item) .. and then call adapter.notifyDataSetChanged();
In case you are using a custom adapter (for a custom layout listview), you will want to do this:
When your Adapter is something like:
public class YourAdapterName extends ArrayAdapter<yourObject>
then the code for deleting the selected ListView Item will be:
ListView yourListView = (ListView) findViewById(R.id.listviewid);
YourAdapterName adapter;
adapter = (YourAdapterName) yourListView.getAdapter();
yourObject theitem = adapter.getItem(position);
adapter.remove(theitem);
adapte.notifyDataSetChanged();
This is assuming you are inside an event that gives you access to the current position inside the listview. like:
public boolean onItemLongClick(AdapterView<?> parent, View strings,int position, long id)
or
public void onItemClick(AdapterView<?> arg0, View v, int position, long id)
Otherwise you will need to obtain that position some other way, like storing it (onItemClick or onItemLongClick) in a textView with Visibility.GONE, and retrieve it when clicking the button (this is silly, you can use all kinds of storage options, like global variables, database and such).
Make sure you have overridden the remove method on your custom adapter
For example if this is your add method:
#Override
public void add(String[] object) {
scoreList.add(object);
super.add(object);
}
then your remove method would look something like this:
#Override
public void remove(String[] object) {
scoreList.remove(object);
super.remove(object);
}
call the below two lines::
adapter.remove(inverterNo);
adapter.notifyDataSetChanged();
where inverterNo is your item
It easy; you only to need is: add a method public in your personalize adapter some this:
public void remove(int position) {
itemsMovieModelFiltered.remove(position);
notifyDataSetChanged();
}
Remenber, this method you must add in your personalize adapter.
Then, call this method from other
adapte=new PersonalizeListAdapter(getActivity().getApplicationContext(),
movieModelList);
adapte.remove(position);