In my application I'm register ContextMenu on Listview and I want to get clicked Listview item by context menu. For example if I have two row in list view with this structure:
public class StructReceiveSms{
public int userID;
public String username;
}
my adapter can be show username in listview. now i'm in below code can define conext menu on list view:
public class FragmentSmsReceiveMaster extends Fragment {
private static final Boolean DEBUG = true;
public ArrayAdapter adapter;
private ArrayList<StructReceiveSms> receiveSmsArray;
.
.
.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
.
.
.
smsView = (ListView) view.findViewById(R.id.listView);
smsView.setAdapter(adapter);
registerForContextMenu(smsView);
.
.
.
}
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
String[] menuItems = getResources().getStringArray(R.array.SmsMasterContextMenu);
for (int i = 0; i < menuItems.length; i++) {
menu.add(Menu.NONE, i, i, menuItems[i]);
}
}
#Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
int menuItemIndex = item.getItemId();
String listItemName = adapter.getItem(info.position) + "";
/* GET CLICKED LISTVIEW ITEM AFTER CHOOSE CONTEXTMENU MENU ITEMS */
Toast.makeText(G.currentActivity, listItemName, Toast.LENGTH_SHORT).show();
return true;
}
}
Now after click on context menu items I can get which clicked by user by menuItemIndex but I can not get which Listview's items in onContextItemSelected function. for example ater opening context menu on first item I can get userID and username and show it. How to do this, thanks
Since your Adapter's data list consists of StructReceiveSms objects, the adapter.getItem(info.position) call in onContextItemSelected() will return the list item the context menu was opened for, but it will need to be cast to the StructReceiveSms type. From this, you can get the userID and username you want.
public boolean onContextItemSelected(MenuItem item)
{
...
StructReceiveSms listItem = (StructReceiveSms) adapter.getItem(info.position);
String selectedName = listItem.username;
int selectedId = listItem.userID;
...
}
This is assuming you've not overridden the getItem() method of the Adapter to return something else, but I imagine you would've shown that if you had.
Related
I have populated listview from database using ArrayAdapter, i want to get the database-row id of the list item when i click the the item. I have searched many hours, but i couldn't get the answer because i am beginner.
here is my main.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView=findViewById(R.id.quote_list);
registerForContextMenu(listView);
DatabaseAccess datac= DatabaseAccess.getInstance(getApplicationContext());
datac.open();
final ArrayList<String> thelist = new ArrayList<>();
Cursor data=datac.getquotes();
while(data.moveToNext()){
thelist.add(data.getString(2));
ListAdapter listAdapter = new ArrayAdapter<>(this,R.layout.textcenter,R.id.textitem,thelist);
listView.setAdapter(listAdapter);
}
}
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater menuInflater=getMenuInflater();
menuInflater.inflate(R.menu.my_contextual_menu,menu);
}
#Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info =(AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
return super.onContextItemSelected(item);
}
}
Try in this way,
1. When you click on the item of list view, you will get the id.
2. get the string value from that index and call a method where send collected string as a parameter and fire a query in that method like
return db.rawQuery("select row_id from your_tableName where string_columnName="+your_passed_string+";",null);
Collect row_id in your cursor and convert it into a int value then show it on screen.
Done.
I'm currently trying to implement a context menu for my app, there is a list of tasks and when the user long-clicks on one, a context menu should appear giving the user some options depending on the item he selected.
Implementing the menu itself wasn't too hard and my implementation gives the result I expect :
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
if(v.getId() == R.id.list){ //check if the click comes from the listview
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)menuInfo;
menuChoosenTask = taskAdapter.getItem(info.position); //retrive the choosen task
Log.e("CreateMenu", "Choose item with name " + menuChoosenTask.getTaskName());
menu.setHeaderTitle(menuChoosenTask.getTaskName());
int options = 3;
if(menuChoosenTask.getTypeOfTask() == WeeklyTask.TYPE_TASK_WORK) options = 4;
for(int i = 0; i < options; i++){
menu.add(Menu.NONE, i, i, menuOptions[i]);
}
}
}
But for handling user clicks, the trouble comes and weird things happen :
#Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
final int menuItemIndex = item.getItemId();
menuChoosenTask = taskAdapter.getItem(info.position);
int choosenTaskId = menuChoosenTask.getId();
String selectedOption = menuOptions[menuItemIndex];
if(selectedOption.equals(getString(R.string.cm_option_edit))){
//DO STUFF
}
if(selectedOption.equals(getString(R.string.cm_option_delete_for_everyday))){
//DO STUFF
}
if(selectedOption.equals(getString(R.string.cm_option_delete_for_this_day)){
//DO STUFF
}
return true;
}
The problems happens when I try to retrieve the menuChoosenTask, for some reason the taskAdapter may reduce it's size, that is, from what i've seen in the debugger : When creating the menu taskAdapter has size 4 and info.position is 3 so no problem, but when I click, info.position is still 3 but now taskAdapter has size 2.
So I first tried to work around this by setting menuChoosenTask as a global variable and just set it's state when the menu is created and re-use it when the user selects an option, but for some reasons the variable loses it's state when an option is selected and becomes null.
My question is : How can I retrieve the object and why do these weird things happen?
EDIT :
I managed to narrow down the problem a bit : The 2 functions you see are in a class called DayFragment and one of it's variable is set by the children class when called, so basically what happens is that the first method (menu creation) is called from a child (let's say the second, and from what I've seen: the one on my screen) but the second method (option selection) is called from another one , here is the full code of the class plus one of its subclasses :
DayFragment.java:
public abstract class DayFragment extends Fragment {
protected int DAY_ID = 0;
protected List<WeeklyTask> tasks;
protected TaskAdapter taskAdapter;
protected MainViewModel mainViewModel;
protected AppDatabase mDB;
protected String[] menuOptions = {"Edit", "Delete for this day", "Delete for everyday", "To-Do's"};
protected WeeklyTask menuChoosenTask;
public DayFragment(){}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.tasks_list, container, false);
setDayId();
tasks = new ArrayList<>();
taskAdapter = new TaskAdapter(getActivity(), tasks);
mDB = AppDatabase.getInstance(getContext());
ListView listView = rootView.findViewById(R.id.list);
listView.setAdapter(taskAdapter);
registerForContextMenu(listView);
mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
setupFAB( (FloatingActionButton) rootView.findViewById(R.id.fab));
setupViewModel();
return rootView;
}
public abstract void setupFAB(FloatingActionButton fab);
protected abstract void setDayId();
public void setupViewModel() {
mainViewModel.getTasks().removeObservers(this);
mainViewModel.getTasks().observe(this, new Observer<List<WeeklyTask>>() {
#Override
public void onChanged(#Nullable List<WeeklyTask> weeklyTasks) {
taskAdapter.clear();
int id = 0;
WeeklyTask task;
for(int i = 0; i < weeklyTasks.size(); i++){
task = weeklyTasks.get(i);
if(task.getDays()[DAY_ID]){ // IF should be shown this day
taskAdapter.insert(task, id);
id++;
}
}
taskAdapter.notifyDataSetChanged();
}
});
}
public boolean atLeastOneTrue(boolean[] arr){
boolean out = false;
for(int i = 0; i < arr.length; i++){
out = out | arr[i];
}
return out;
}
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
if(v.getId() == R.id.list){ //check if the click comes from the listview
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)menuInfo;
menuChoosenTask = taskAdapter.getItem(info.position);
Log.e("CreateMenu", "Choose item with name " + menuChoosenTask.getTaskName() + " for day " + DAY_ID);
menu.setHeaderTitle(menuChoosenTask.getTaskName());
int options = 3;
if(menuChoosenTask.getTypeOfTask() == WeeklyTask.TYPE_TASK_WORK) options = 4;
for(int i = 0; i < options; i++){
menu.add(Menu.NONE, i, i, menuOptions[i]);
}
}
}
#Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
Log.e("menuClicked", "menu for day " + DAY_ID + " clicked on item no " + info.position);
final int menuItemIndex = item.getItemId();
menuChoosenTask = taskAdapter.getItem(info.position);
int choosenTaskId = menuChoosenTask.getId();
String selectedOption = menuOptions[menuItemIndex];
if(selectedOption.equals(getString(R.string.cm_option_edit))){
//DOSTUFF
}
if(selectedOption.equals(getString(R.string.cm_option_delete_for_everyday))){
//DOSTUFF
}
if(selectedOption.equals(getString(R.string.cm_option_delete_for_this_day))){
//DOSTUFF
}
return true;
}
}
One of its children classes (there are 7 of them) :
public class MondayFragment extends DayFragment {
public static final int DAY_ID = 0;
public void setDayId(){
super.DAY_ID = DAY_ID;
}
}
All these fragments are shown in a PagerAdapter (coupled with a view pager) so I 'kinda' get why this happens but I have no idea how to prevent it.
Ok, well I just spent an hour and a half googling stuff and I finally found a solution :
Wrong fragment in ViewPager receives onContextItemSelected call
As I didn't fully understand what went wrong it took me really long to find it. So my Bad and sorry for all the trouble
I'm using a FirebaseRecyclerView to show a list of 'Charts' (defined in the app) in a Fragment in my app. The items in the list can be long-clicked to pop up a menu with options.
The difficulty I have is that when the user clicks on an item in the pop up menu, I can't get the id of the item that was clicked. I've included the relevant parts of ChartListFragment below, and marked where I'm having a problem.
public abstract class ChartListFragment extends Fragment {
private FirebaseRecyclerAdapter<Chart, ChartViewHolder> mAdapter;
private RecyclerView mRecycler;
public ChartListFragment() {}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View rootView = inflater.inflate(
R.layout.fragment_charts_list, container, false);
mRecycler = (RecyclerView) rootView.findViewById(R.id.charts_list);
mRecycler.setHasFixedSize(true);
return rootView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
LinearLayoutManager mManager = new LinearLayoutManager(getActivity());
mManager.setReverseLayout(true);
mManager.setStackFromEnd(true);
mRecycler.setLayoutManager(mManager);
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();
Query chartsQuery = getQuery(mDatabase);
mAdapter = new FirebaseRecyclerAdapter<Chart, ChartViewHolder>(Chart.class, R.layout.item_chart,
ChartViewHolder.class, chartsQuery) {
#Override
protected void populateViewHolder(final ChartViewHolder viewHolder, final Chart model, final int position) {
final DatabaseReference chartRef = getRef(position);
final String chartKey = chartRef.getKey();
registerForContextMenu(viewHolder.itemView);
viewHolder.bindToPost(model);
}
};
mRecycler.setAdapter(mAdapter);
}
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
if (v.getId()==R.id.chart_item) {
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.menu_long_press_chart_name, menu);
}
}
#Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
// AT THIS POINT I THINK I SHOULD BE ABLE TO GET THE POSITION OF THE ITEM
// THAT WAS CLICKED BY DOING SOMETHING LIKE
// int chartPos = info.position;
// Chart chartClicked = mAdapter.getItem(chartPos);
// BUT info IS NULL, SO info.position CAUSES A CRASH
switch(item.getItemId()) {
case R.id.edit:
// Show Edit Activity
return true;
case R.id.delete:
// Show confirmation message
return true;
default:
return super.onContextItemSelected(item);
}
}
}
My solution has a lot of moving parts, but overall the concept is pretty simple: use an Intent to pass the data you want. You can't rely on the ContextMenu.ContextMenuInfo menuInfo parameter, because this is only present when your context menu is attached to a ListView or GridView.
The first step is you need to get all information you will eventually want (this could be position or it could be something more direct) into your ViewHolder. You need this because you can call RecyclerView.getChildViewHolder() in onCreateContextMenu in order to access the data associated with whichever item you clicked on.
The next step is to attach this data to the context menu using an Intent. In your onCreateContextMenu() implementation, after you inflate your menu, you can write something like this:
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
...
MyViewHolder holder = (MyViewHolder) recycler.getChildViewHolder(v);
int position = holder.position; // or any custom data you want, set up in onBindViewHolder()
Intent data = new Intent();
data.putExtra("position", holder.position);
menu.findItem(R.id.your_item_here).setIntent(data);
menu.findItem(R.id.your_other_item_here).setIntent(data);
}
Finally, in your onContextItemSelected() implementation, you can access this Intent and get your data out of it:
#Override
public boolean onContextItemSelected(MenuItem item) {
int position = item.getIntent().getIntExtra("position", -1);
// do something with position
return true;
}
So, with the below code i get the value of listview item i clicked and copy it to clipboard, so the user can paste it later.
listView.setOnItemClickListener(
new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position, long arg3) {
TextView textView = (TextView) view.findViewById(R.id.txt);
registerForContextMenu(textView);
}
}
);
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
//user has long pressed your TextView
menu.add(Menu.NONE, 0, Menu.NONE, "Copy");
//cast the received View to TextView so that you can get its text
TextView yourTextView = (TextView) v;
copiedMSG = yourTextView.getText().toString();
//System.out.println("Message Copied = : " + copiedMSG);
}
// This is executed when the user selects an option
#Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
switch (item.getItemId()) {
case 0:
ClipboardManager cm = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
cm.setText(copiedMSG);
Toast.makeText(getActivity(), "Copied", Toast.LENGTH_SHORT).show();
return true;
default:
return super.onContextItemSelected(item);
}
}
This works just fine but the problem is that the context menu appears only at the second time i click an item.The first time i click an item nothing at all happens.
This is happening because the Context Menu is not registered for the TextView until the first click (this happens in your OnItemClickListener).
If you want the view to be registered for a Context Menu initially, you should do it in the getView() function of your list's adapter.
I'm newbie in Android and i learning context menu but after surfing about context menu i have little bit confusion in Adapter and Inflater. I saw 1 program with using adapter and 1 using Inflater. So, please help me how/when to use Adapter and Inflater.
Here is an example using inflater...
public class MainActivity extends ListActivity {
private String selectedName = "";
private String[] nameList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
nameList = getResources().getStringArray(R.array.name_list);
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, nameList));
registerForContextMenu(getListView());
}
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
getMenuInflater().inflate(R.menu.context_menu, menu);
}
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo adapInfo = (AdapterContextMenuInfo) item
.getMenuInfo();
selectedName = nameList[(int) adapInfo.id];
switch (item.getItemId()) {
case R.id.view:
Toast.makeText(MainActivity.this,
"You have pressed View Context Menu for " + selectedName,
Toast.LENGTH_LONG).show();
return true;
case R.id.save:
Toast.makeText(MainActivity.this,
"You have pressed Save Context Menu for " + selectedName,
Toast.LENGTH_LONG).show();
return true;
case R.id.edit:
Toast.makeText(MainActivity.this,
"You have pressed Edit Context Menu for " + selectedName,
Toast.LENGTH_LONG).show();
return true;
case R.id.delete:
Toast.makeText(MainActivity.this,
"You have pressed Delete Context Menu for " + selectedName,
Toast.LENGTH_LONG).show();
return true;
}
return false;
}
}
Another example using adapter:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Countries = getResources().getStringArray(R.array.Game);
ListView list = (ListView) findViewById(R.id.list);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
R.layout.listitem, Countries);
list.setAdapter(adapter);
registerForContextMenu(list);
}
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
if (v.getId() == R.id.list) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
menu.setHeaderTitle(Countries[info.position]);
String[] menuItems = getResources().getStringArray(
R.array.contextmenu);
for (int i = 0; i < menuItems.length; i++) {
menu.add(Menu.NONE, i, i, menuItems[i]);
}
}
}
public boolean onContextItemSelected(MenuItem item) {
// TODO Auto-generated method stub
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item
.getMenuInfo();
int menuItemIndex = item.getItemId();
String[] menuItems = getResources().getStringArray(R.array.contextmenu);
String[] menuItems1 = getResources().getStringArray(R.array.game);
String menuItemName = menuItems[menuItemIndex];
String listItemName = menuItems1[info.position];
// selectedName = nameList[(int) info.id];
TextView text = (TextView) findViewById(R.id.textView1);
text.setText(String.format("Selected %s for item %s", menuItemName,
listItemName));
return true;
}
These types serve different purposes.
The MenuInflator converts XML files into a Menu object representing the on-screen layout of the menu. In the first example, R.menu.context_menu refers to an associated XML file at res/menu/context_menu.xml that defines the choices that will appear in the menu. See Menu Resource for the format of XML menu resources. Here's a simple example:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/open" android:title="Open"/>
<item android:id="#+id/info" android:title="More Info"/>
<item android:id="#+id/delete" android:title="Delete"/>
</menu>
The AdapterContextMenuInfo provides extra information when a context menu is brought up for a list, grid, etc. It allows you to determine which item the user selected (long pressed). Notice that both of your examples use this.