So, I know that there are plenty of similar questions on Stack; however, none of them managed to solve my issue.
I have a ListActivity activity for displaying a list of usernames;
I created a UserObj class to collect information about each user, i.e., UserObj instance (username, password, etc..).
Now I am showing successfully the username within my ListView and I also managed to show a floating ContextMenu with some items (Delete,ChangeUsername,ChangePassword,ChangeEmail) upon long-clicking on a particular username.
Below I post my ListActivity code:
public class ShowAccountList extends ListActivity {
List<UserObj> Allusers = new ArrayList<>(); // this is required to retrieve users from a local database
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_account_list);
final ListView UserList = (ListView)findViewById(android.R.id.list);
UserList.setLongClickable(true);
/*For completeness: here I fetch users from DB*/
DataBaseUserHelper dbusers = new DataBaseUserHelper(getApplicationContext());
dbusers.CreateTables();
Allusers = dbusers.getAllUsers();
dbusers.closeDB();
/*Let's build a 'username' list to display*/
List<String> namelist = new ArrayList<String>();
for(int i=0 ; i< Allusers.size() ; i++){
String uname = Allusers.get(i).username;
namelist.add(uname);
}
final ArrayAdapter<String> nameadapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,namelist);
UserList.setAdapter(nameadapter);
registerForContextMenu(UserList);
UserList.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
openContextMenu(parent);
/*This outputs the CORRECT position*/
System.out.println("Position: " + position + ", id: " + id);
return true;
}
});
}
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo){
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.user_context_menu, menu);
}
#Override
public boolean onContextItemSelected(MenuItem item){
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
int selectedId = info.position; // HERE the problem shows up
// switch statement for debugging purposes
switch (item.getItemId()){
case R.id.DeleteUser:
System.out.println("DELETE USER");
return true;
case R.id.ChangeName:
System.out.println("CHANGE USERNAME");
return true;
case R.id.ChangeEmail:
System.out.println("CHANGE EMAIL");
return true;
case R.id.ChangePassword:
System.out.println("CHANGE PASSWORD");
return true;
default:
return super.onContextItemSelected(item);
}
}
}
However, what I need to do, obviously, is to actually trigger the execution of the 'delete user', 'change email', etc... tasks for the selected user; what I supposed was that the position of the corresponding list item would be required in order to know which user should have undergone these modifications.
So, following the suggestions of the majority of the questions (and of the corresponding answers) on this topic, I casted item.getMenuInfo() to AdapterContextMenuInfo within onContextItemSelected and then I tried to retrieve the position with:
int selectedId = info.position;
The problem is that when I select one menu item (e.g., the one corresponding to 'delete') the ANR dialog shows up and the following exception is reported:
java.lang.NullPointerException: Attempt to read from field 'int android.widget.AdapterView$AdapterContextMenuInfo.position' on a null object reference
What I can understand is that info.position is always null. But why?
What could be the problem in my code?
As you can see from the code above, I tried to see what happened when clicking each single menu item by printing a test string (e.g., "DELETE USER") and actually if I remove int selectedId = info.position; no crash occurs...
Thanks in advance
EDIT
After some diagnosis, I figured out that the menuInfo passed to onCreateContextMenu is NULL. So, I got a little bit closer to the origin of the problem.
Any idea why I am getting a NULL menuInfo?
I faced similar problem. My solution was to register view for context menu inside the adapter using activity.registerForContextMenu(view)
The position of the item or even UserObj can be set as tag in the view.
You can get the tag in the onCreateContextMenu() call and corresponding action could be invoked.
In the best way, you should create custom adapter, then implement custom listener in activity when event occur (onClick, onLongClick...)
About your problem, I think you was wrong when implement #rabhis idea.
Try my solution that I have implemented in my app:
(In my code, I used custom adapter with recycleview, so try to merge to your code)
Class implement:
public class ListHistoryAdapter extends RecyclerView.Adapter<ListHistoryAdapter.HistoryViewHolder> implements View.OnCreateContextMenuListener {
In public void onBindViewHolder method:
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// your code
}
});
holder.itemView.setOnCreateContextMenuListener(this);
holder.itemView.setTag(position);
Override in adapter:
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
MenuItem deleteItem = menu.add(Menu.NONE, 1, 1, "Delete");
final int pos = (int) v.getTag();
deleteItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
// your code
return true;
}
});
}
Related
I had a listview to which I've kept a popup menu.So I could get popup menu on long clicking an item of listview.My popup menu methods are as follows:
void delete()
{
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(ViewDetails.this, i+"", Toast.LENGTH_SHORT).show();
return true;
}
});
}
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)
{
super.onCreateContextMenu(menu,v,menuInfo);
menu.setHeaderTitle("Click to delete");
menu.add(0,v.getId(),0,"Delete");
}
#Override
public boolean onContextItemSelected(MenuItem item)
{
if(item.getTitle()=="Delete")
{
delete();
}
return true;
}
My problem is that I'm unable to execute the code in onItemLongClick method. Actually what I want to do is deleting the List item on pressing the delete option that comes from the popup menu...Someone please help me...Thanks in advance
I feel the following link should able to solve your problem
how to implement a long click listener on a listview
and one more thing , are you calling your #delete() method to register listview long press listener ?
I have got two activities.
My goal is: After longClick on any ListView position in activity 2, I want some String to be added to ListView in activity 1. From activity 2 I'm going back to activity 1 by pressing back button. Each ListView has got different adapter.
I tried with Bundle (put extra), but it makes app crash.
Fragment of code from activity 1:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.start_layout);
mDrawerList = (ListView)findViewById(R.id.navList);
mDrawerLayout = (DrawerLayout)findViewById(R.id.drawer_layout);
mActivityTitle = getTitle().toString();
addDrawerItems();
setupDrawer();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
}
private void addDrawerItems() { //It adds text ect. to listView - It works so I cut out the rest of code
ar.add("anything");
ar.add(lv.try);
mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, ar);
mDrawerList.setAdapter(mAdapter);
mDrawerList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
});
}
Simply adding like ar.add("anything"); works. The lv.try contains by default text "First text". I tried to update this variable in activity 2 onItemLongClick fragment of code:
mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
//What to type here?
return false;
}
});
Putting simply try = "Second text"; will not update my try variable. I tried also with Bundle (extras) but without success. Any ideas?
I don't know if I understood the question. Anyway, have you tried to override onBackPressed?
In the second activity you create a boolean variabile and inizialize it as false, then change its value when you perform a long click:
mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
longClick = true;
return false;
}
});
Remember, longClick has to be a class variable!
Outside onCreate:
#Override
public void onBackPressed() {
Intent intent = new Intent(SecondActivityName.this, FirstActivity.class);
intent.putExtra("longClick", longClick);
finish();
startActivity(setIntent);
}
And in the first Activity:
Intent intent = getIntent();
if (intent.getBooleanExtra("longClick", false)) {
addDrawerItems();
}
What does add position mean ?
In my opinion add position means add one element to a structure (array, list, etc).
So basically what I mean is that you can have an array in your CustomApplicatioClass or even a Static array(someone probably would not agree with me) and onItemLongClick() add the element you want in the structure.
Then when you load the Activity with the ListView you can do your ListView stuff using the updated structure
hope it helps
(Sorry for my mistakes in spelling)
I have a problem in the combination of some things:
I use a GridView to download show content from a database. onClick a file is downloaded which can be viewed later. Its about 20MB. This download is done by a service.
Every item in the gridView contains a progressbar in the layout. this progressbar is just shown if the item is donwloading in the background via the service.
The Service sends a broadcast about the download Progress, the Intent contains the ID of the item to find it in data of the Adapter for the gridview.
A BroadcastReceiver ist registered in my Activity to get the Progress Update (remember, simultaneous downloads are possible) which calls a function "setProgress" in the gridview-adapter
public void setProgress(long id, int progress)
{
for (int i = 0;i < list.size();i++)
{
if (list.get(i).getId() == id)
{
list.get(i).setProgress(progress);
notifyDataSetChanged();
}
}
}
To this point everything works fine.
Additionally im using QuickAction implementation from http:// www. londatiga.net/it/how-to-create-quickaction-dialog-in-android/ (Spaces because i cannot post more than two hyperlinks)
Now comes the problem:
Sometimes, i guess when notifydatasetchanged is called and the user taps on an item, the quickaction is shown on a wrong position.
To make this clearer here are two pictures:
The first one is what should happen (in this case a click on the first item of the gridview)
http://dl.dropbox.com/u/9031500/expected.png
The second picture shows what happens sometimes (only when some downloads are running, thats why i guess its because of the "notifydatasetchanged" and the rebuild of the views). This was also a click on the first item, unfortunately the quick-action is shown to the fourth item:
http://dl.dropbox.com/u/9031500/wrong.png
This is my implementation in my activity for the call of the quick-action:
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
showQuickAction(view, position);
}
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
showQuickAction(view, position);
return true;
}
private void showQuickAction(View view, int position)
{
RelativeLayout facsimile = (RelativeLayout) view.findViewById(R.id.lib_item_image_layout);
Log.i(LOGTAG, "Position:"+position);
currentPaper = TZReader.paperDao.load(libraryAdapter.getItemId(position));
Log.i(LOGTAG,"Set CurrentPaper:"+currentPaper.getTitelWithDate());
ActionItem downloadItem = new ActionItem(ACTION_ITEM_DOWNLOAD, "Download", getResources().getDrawable(R.drawable.ic_action_download));
ActionItem readItem = new ActionItem(ACTION_ITEM_READ, "Lesen", getResources().getDrawable(R.drawable.ic_action_read));
ActionItem deleteItem = new ActionItem(ACTION_ITEM_DELETE, "Löschen", getResources().getDrawable(R.drawable.ic_action_delete));
ActionItem cancelItem = new ActionItem(ACTION_ITEM_CANCEL, "Abbrechen", getResources().getDrawable(R.drawable.ic_action_cancel));
QuickAction mQuickAction = new QuickAction(this, QuickAction.HORIZONTAL);
switch (currentPaper.getState())
{
case Paper.DOWNLOADED_READABLE:
mQuickAction.addActionItem(readItem);
mQuickAction.addActionItem(deleteItem);
break;
case Paper.DOWNLOADED_BUT_UPDATE:
mQuickAction.addActionItem(downloadItem);
mQuickAction.addActionItem(deleteItem);
break;
case Paper.IS_DOWNLOADING:
mQuickAction.addActionItem(cancelItem);
break;
case Paper.NOT_DOWNLOADED:
mQuickAction.addActionItem(downloadItem);
break;
}
//Set listener for action item clicked
mQuickAction.setOnActionItemClickListener(new QuickAction.OnActionItemClickListener() {
#Override
public void onItemClick(QuickAction source, int pos, int actionId) {
//here we can filter which action item was clicked with pos or actionId parameter
switch(actionId)
{
case ACTION_ITEM_DOWNLOAD:
Intent downloadIntent = new Intent(getApplicationContext(), DownloadService.class);
downloadIntent.putExtra(DownloadService.PARAMETER_PAPER_DB_ID_LONG, currentPaper.getId());
startService(downloadIntent);
break;
case ACTION_ITEM_READ:
break;
case ACTION_ITEM_DELETE:
DeleteAlertDialogFragment newFragment = DeleteAlertDialogFragment.newInstance(currentPaper.getId());
newFragment.setStyle(SherlockDialogFragment.STYLE_NO_TITLE,0);
newFragment.show(getSupportFragmentManager(), "dialog");
break;
case ACTION_ITEM_CANCEL:
break;
}
}
});
mQuickAction.show(facsimile);
}
Maybe someone has any idea or hints for me how i can handle this problem!
Thanks a million in advance!
I found a solution for my problem.
The solution is that i implement my own ProgressBar, which contains now a BroadcastListerner and set the progress on each item. So i can change the value without need to call "notifydatasetchanged". perfect for my needs. Im still not sur if this is a good solution, but it works well.
Here is the code for the Progressbar:
public class ListeningProgressBar extends ProgressBar {
public ListeningProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
final IntentFilter intentDownloadProgressFilter = new IntentFilter(DownloadService.DOWNLOAD_PROGRESS_BROADCAST);
private long paperId = 0;
private boolean isReceiverRegistered = false;
#Override
protected void onAttachedToWindow() {
getContext().getApplicationContext().registerReceiver(downloadProgressBroadcastReceiver,intentDownloadProgressFilter);
isReceiverRegistered = true;
super.onAttachedToWindow();
}
#Override
protected void onDetachedFromWindow() {
if (isReceiverRegistered)
{
getContext().getApplicationContext().unregisterReceiver(downloadProgressBroadcastReceiver);
isReceiverRegistered = false;
}
super.onDetachedFromWindow();
}
private BroadcastReceiver downloadProgressBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
long id = intent.getLongExtra(DownloadService.PARAMETER_PAPER_DB_ID_LONG, -1);
int progressValue = intent.getIntExtra(DownloadService.PARAMETER_PAPER_PROGRESS_INT, 0);
if (paperId == id)
{
setProgress(progressValue);
}
}
};
public long getPaperId() {
return paperId;
}
public void setPaperId(long paperId) {
this.paperId = paperId;
}
}
I just use it as a normal custom view in my XML Layout.
In the Adapter i set the id of my content to give the receiver the data just to setprogress if its the right content.
At the end, my problem is solved, the progress is updated without the need to call notifydatasetchanged. Yeah!
I am making an android application that needs to use a ListView. Once a user presses a menubutton, it pops up a popupwindow containing a TextView, EditText and two Buttons, "Ok" and "Cancel". Once the user presses "Ok", the text inside the EditText should be added to the ListView. And the cancel Button is obvious. I also want to be able to long press on a ListView item to open a popupwindow containing a delete Button. How can I make this possible? I am using this code so far:
public class NotesActivity extends ListActivity {
/** Called when the activity is first created. */
Button AddItemToListView;
static final String[] COUNTRIES = new String[] {
"Matte på A1 med Ole", "Engelsk på klasserommet", "Film på A1 etter friminuttet"
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.notes);
setListAdapter((ListAdapter) new ArrayAdapter<String>(this, R.layout.list_item, COUNTRIES));
ListView lv = getListView();
lv.setTextFilterEnabled(true);
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// When clicked, show a toast with the TextView text
Toast.makeText(getApplicationContext(), "Note: " + ((TextView) view).getText(),
Toast.LENGTH_SHORT).show();
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu meny) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.listviewmenubuttons, meny);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case R.id.AddItemToListView:
Toast.makeText(NotesActivity.this,
"Add note button pressed", Toast.LENGTH_SHORT)
.show();
break;
}
return true;
}
}
Try doing some research on dialogs if you want to add feature after accessing listview.setonlongclick(). Here is a link on android dialog developers.
Since you are using an ArrayAdapter, when the user taps on add, you must add the new item to your array (change from an array to an List to make it easier). Then you should call notifyDataSetChanged() from the ArrayAdapter.
For deleting is the same, but you remove the item from the List. The call to notifyDataSetChanged() is to tell the ListView that it needs to redraw itself.
I have a ListView that I am trying to use with a checkable item list. I am calling the toggle() method on my list item class in the ArrayAdaptor, but the checkbox is not being ticked in the UI. However, I can confirm that the correct items are being selected in the UI, and that the "isChecked()" status reports back correctly--just the UI doesn't change at all. Are there any special methods I need to call to update the checkbox graphic for the UI?
To put it another way--how do I programmatically tell the UI that a checkbox should show up as "checked"? It seems this should be a very simple process, but I've been having a lot of trouble finding this information.
Code is as follows:
For the item data class in the ArrayAdaptor:
public class SelectedItemData extends CheckedTextView {
public String _item_name;
public String getItemName()
{
return _item_name;
}
public void setItemName(String in_name)
{
_item_name = in_name;
}
// methods
public SelectedItemData(Context context) {
super(context);
init();
}
public void init()
{
this._item_name = "UNSET";
this.setChecked(false);
}
#Override
public String toString()
{
return _item_name;
}
}
In the Activity class (located within the onCreate method):
_selectedItemsListView = (ListView) findViewById(R.id.selected_items_listview);
_selectedItemsListView.setItemsCanFocus(false);
_selectedItemsListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
_selectedItemsListView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> listview, View view, int position, long id) {
#SuppressWarnings("unchecked")
ArrayAdapter<SelectedItemData> itemsAdapter = (ArrayAdapter<SelectedItemData>)_selectedItemsListView.getAdapter();
SelectedItemData selectedItem = itemsAdapter.getItem(position);
selectedItem.toggle();
Toast.makeText(view.getContext(), "Item " + selectedItem.getItemName() + " Selected!", Toast.LENGTH_SHORT).show();
Log.d(TAG, "Is Item Checked? " + selectedItem.isChecked());
_selectedItemsListView.setAdapter(itemsAdapter);
}
});
Any guidance on how to enable the UI to properly display that one of the items have been selected/checked would be great. Thanks!
You have to update your adapter with the new item and set it to the listview. (itemsAdapter.setItem(item,position))