I am trying to add image button to my custom SimpleCursorAdapter for my ListView in my project but I have got one problem with repeating and totally random value of one field.
This is a code of it:
public class MyCursorAdapter extends SimpleCursorAdapter {
public MyCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
super.bindView(view, context, cursor);
final Context t = context;
final Cursor c = cursor;
ImageButton delimageButton = (ImageButton)view.findViewById(R.id.deletebutton);
delimageButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
Toast.makeText(t,
"Delete ID: " + c.getInt(c.getColumnIndex(MyDBAdapter.KEY_ID)), Toast.LENGTH_SHORT).show();
}
});
if(cursor.getLong(cursor.getColumnIndex(MyDBAdapter.KEY_OWNID))>0)
{
TextView own = (TextView)view.findViewById(R.id.ownInfo);
own.setText("OWN");
}
else
{
TextView own = (TextView)view.findViewById(R.id.ownInfo);
own.setText("");
}
}
}
Now, when I press delimageButton what I get is some random ID of one record (row) of ListView that is in current view (I can see it, but it is not the correct id) e.g. if you can see like 5 rows on screen and you press on one of buttons you will get id of other row (one of those 5) but not this one which you pressed (in most of the cases). I remember that there was some trick with this own TextView, but I don't see how it can be putted in here.
So, can you please advice me how can I make it to show correct ID?
I'll be glad for help.
EDIT
There is a whole code responsible for setting a ListView together with calling MyCursorAdapter:
private void refreshList() {
mySQLiteAdapter = new MyDBAdapter(this);
mySQLiteAdapter.open();
String[] columns = { MyDBAdapter.KEY_TITLE, MyDBAdapter.KEY_GENRE,
MyDBAdapter.KEY_OWNID, MyDBAdapter.KEY_ID };
Cursor contentRead = mySQLiteAdapter.getAllEntries(false, columns,
null, null, null, null, MyDBAdapter.KEY_TITLE, null);
startManagingCursor(contentRead);
Log.d(TAG, Integer.toString(contentRead.getCount()));
MyCursorAdapter adapterCursor = new MyCursorAdapter(this,
R.layout.my_row, contentRead, columns, new int[] {
R.id.rowTitle, R.id.detail });
this.setListAdapter(adapterCursor);
mySQLiteAdapter.close();
}
To clarify, the activity is ListActivity.
Your OnClickListener is getting the id from the cursor when it is clicked, not when it is constructed. Meanwhile, the listview is changing the cursor position as you scroll around.
I think if you look carefully, you'll find the Toast is displaying the id of the last item that was loaded into view rather than the item that contains the button you clicked.
You can solve this by getting the id when you construct the click listener, like this:
delimageButton.setOnClickListener(new OnClickListener() {
private int id = c.getInt(c.getColumnIndex(MyDBAdapter.KEY_ID));
#Override
public void onClick(View arg0) {
Toast.makeText(t,
"Delete ID: " + id, Toast.LENGTH_SHORT).show();
}
});
Related
I am new to android development, in fact its my first application. I have created a dynamic layout in my project based on Json. Each object includes an "id" key and some more string keys. every object in my json should be transformed to a cardview inside a recylcerview and each cardview has a button.
My problem is handling these dynamic buttons. Is it possible to determine which button was clicked?
View.id is an integer, and you shouldn't set arbitrary values to it if the View is generated dinamically, what you can use though is View.tag. So you can assign the id defined in the JSON to tag and then check the tag value when the View is clicked. E.g.
val view1 = View(context)
view1.tag = "id from JSON 1"
view1.setOnClickListener(this::onViewClicked)
val view2 = View(context)
view2.tag = "id from JSON 2"
view2.setOnClickListener(this::onViewClicked)
// ...
private fun onViewClicked(view: View){
val jsonId = view.tag as? String
// ...
}
If your min sdk level at least 17, another option would be to generate ids dinamically with View.generateViewId() and store them in a Map together with your JSON ids
Use a Tag to differentiate the buttons. Add OnClickListener to all Button and set different String tag to button.
button.setOnClickListener(this);
Add ClickListener
#Override
public void onClick(View view){
String tag = (String)view.getTag();
if(tag.equals(tag1)){
// action here
}
//.......
.....
}
Yeah pretty straight, all you have to do is to give them a unique id
I assume you must have a JSON array for dynamic buttons creation.
sample code
public Button createButton(Context context,String text,int buttonNo){
//here set the properties
Button bt = new Button(context);
bt.setText(text);
bt.setId(buttonNo);
bt.setTag(buttonNo);
bt.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view){
//handle the button click, unique id , you can use to differentiate
int id = view.getId();
}
});
return bt;
}
call this above method in for loop or as many times you want to create.
you can also set a tag as mentioned above to store more information.
Finally I put the OnClickListener in onBindViewHolder event inside my customAdapter class as below:
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> implements View.OnClickListener {
private Context context;
private List<MyData> my_data;
public CustomAdapter(Context context, List<MyData> my_data) {
this.context = context;
this.my_data = my_data;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.card,parent,false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.match_date.setText(my_data.get(position).getMatch_date());
holder.home_name.setText(my_data.get(position).getHome_name());
holder.away_name.setText(my_data.get(position).getAway_name());
holder.button.setId(my_data.get(position).getId());
holder.button.setTag(my_data.get(position).getStringId());
holder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int intHomeGoals = Integer.parseInt(holder.edtHomeGoals.getText().toString());
int intAwayGoals = Integer.parseInt(holder.edtAwayGoals.getText().toString());
if (intHomeGoals == intAwayGoals)
{
Toast.makeText(context, "00000", Toast.LENGTH_SHORT).show();
}
}
});
Glide.with(context).load(my_data.get(position).getHome_logo()).into(holder.home_logo);
Glide.with(context).load(my_data.get(position).getAway_logo()).into(holder.away_logo);
}
I always cant delete first two sometimes three records. They are on Listview, when you press element you will see delete button on another layout. On Log im getting correct index for every element.
So here is my code:
Main Activity:
viewOfT.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent i=new Intent(MainActivity.this, popupWindow.class);
i.putExtra("index",id);
startActivity(i);
}
});
}
public void populateListView() {
Cursor data = db.getData();
ArrayList<String> listData = new ArrayList<>();
while(data.moveToNext()){
k.setId(data.getInt(0));
k.setTask(data.getString(1));
listData.add("- " + k.getTask());
}
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1,
listData);
viewOfT.setAdapter(arrayAdapter);
arrayAdapter.notifyDataSetChanged();
viewOfT.invalidateViews();
Delete button in other activity:
del.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
Bundle bundle=getIntent().getExtras();
long value=bundle.getLong("index");
db.deleteRecord(value);
finish(); }
});
And SQLHelper:
public void deleteRecord(long id) {
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_NAME, KEY_ID + "=" + id, null);
close();
}
1) Delete populateListView method
2) Add the following as class variables:-
SimpleCursorAdpater sca;
Cursor data;
3) in the onCreate method add :-
data = getData();
sca = new SimpleCursorAdapter(
this, // Context same as for array adapter
android.R.layout.simple_list_item_1, // layout to be used
data, // <<<<<<<< the cursor to be used for the list
new String[]{"columnname_to_be_displayed"}, // <<<<<<<< cursor column to display name
new int[android.R.id.text1], // the view into which the cursor data will be placed
0 // a flag 0 is fine
);
viewOfT.setAdapter(arrayAdapter);
4) Add a new method to override the 'onResume' method (or alter it if already overridden) :-
#Override
protected void onResume() {
super.onResume();
data = getData();
sca.swapCursor(); // can you sca.notifyDatasetChanged()
}
As you are calling another activity to delete the task, onResume will be called when returning from the other activity so the data is again retrieved from the database (deleted row will not exist) and the adpater is told to refresh the data.
You should ideally also override the onDestroy() method to close the cursor (data.close();)
Important Consideration
A cursor column named _id must exist for CursorAdapters (that's how the SimpleCursorAdapter knows what pass to the onItemClickListener).
if KEY_ID does not equate to _id; you either need to change KEY_ID to _id or amend the getData() method to include the _id column (which should be the value of the row's identifier) e.g. assuming a very basic query:
public Cursor getData() {
return db.query(TABLE_NAME,"rowid AS _id, *",null,null,null,null,null);
} // Note! will add an extra column so beware if using column offsets
or perhaps :-
public Cursor getData() {
return db.query(TABLE_NAME,KEY_ID + " AS _id, *",null,null,null,null,null);
} // Note! will add an extra column so beware if using column offsets
A Note on Column offsets
In your code you have :-
k.setId(data.getInt(0));
k.setTask(data.getString(1));
0 and 1 are column offsets (and could change e.g. the two alternative getData() methods). As such it's generally better to take advantage of the Cursor method getColumnIndex(columnname) e.g. the above could be :-
k.setId(data.getInt(data.getColumnIndex(KEY_ID));
k.setTask(data.getString(data.getColumnIndex("TASK_COLUMN")));
Note! not that you will need to create an Array as the SimpleCursorAdpater takes the cursor as the source. (KEY_ID would likely have to be prefixed with the DatabaseHelper Class).
I have a listview that displays all the items in my database. I am trying to create a button that will change the data displayed to only show items that match today's date.
What is the best way to change the query that is being run in the app based on a button push and update the listview?
I've played with setting a flag in the onclick() method paired with if-else statements that held the query call, but it did not seem to switch which was being called.
The flag is the boolean filterToday. set in the onClickListener of todayButton.
public class MainActivity extends AppCompatActivity {
public final static String KEY_EXTRA_CONTACT_ID = "KEY_EXTRA_CONTACT_ID";
private ListView listView;
DBHelper dbHelper;
boolean filterToday;
Cursor cursor;
String [] columns;
int [] widgets;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button addButton = (Button) findViewById(R.id.addNew);
addButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this, AddTaskActivity.class);
intent.putExtra(KEY_EXTRA_CONTACT_ID, 0);
startActivity(intent);
}
});
Button todayButton = (Button) findViewById(R.id.today);
todayButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
filterToday = true;
}
});
dbHelper = new DBHelper(this);
if(filterToday == true){
Calendar cal = Calendar.getInstance();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
String date = df.format(cal.getTime());
Toast.makeText(this,date,Toast.LENGTH_LONG).show();
cursor = dbHelper.getTodaysTasks(date);
}
else{
cursor = dbHelper.getAllTasks();
}
columns = new String[] {
DBHelper.TASK_COLUMN_NAME,
DBHelper.TASK_COLUMN_TYPE,
DBHelper.TASK_COLUMN_DATE
};
widgets = new int[] {
R.id.taskName,
R.id.taskType,
R.id.taskDate
};
SimpleCursorAdapter cursorAdapter = new SimpleCursorAdapter(this, R.layout.task_info,
cursor, columns, widgets, 0);
listView = (ListView)findViewById(R.id.listView1);
listView.setAdapter(cursorAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> listView, View view,
int position, long id) {
Cursor itemCursor = (Cursor) MainActivity.this.listView.getItemAtPosition(position);
int taskID = itemCursor.getInt(itemCursor.getColumnIndex(DBHelper.TASK_COLUMN_ID));
Intent intent = new Intent(getApplicationContext(), AddTaskActivity.class);
intent.putExtra(KEY_EXTRA_CONTACT_ID, taskID);
startActivity(intent);
}
});
}
}
These are the two queries I am trying to switch between:
public Cursor getAllTasks() {
SQLiteDatabase db = this.getReadableDatabase();
Cursor res = db.rawQuery( "SELECT * FROM " + TASK_TABLE_NAME, null );
return res;
}
public Cursor getTodaysTasks(String date){
SQLiteDatabase db = this.getReadableDatabase();
Cursor res = db.rawQuery("SELECT * FROM " + TASK_TABLE_NAME + " WHERE " +
TASK_COLUMN_DATE + " =?", new String[]{date});
return res;
}
OK so the first thing to notice is that your update filterToday = true; inside the onclick listener, the if(filterToday == true){...} outside doesn't know that. That part is only executed once in the oncreate()
If you'd like to perform that action I suggest a small change.. like below
Create a seperate function outside for the data loading
private void LoadMyData() {
if(filterToday == true){
Calendar cal = Calendar.getInstance();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
String date = df.format(cal.getTime());
//you might need to update this line
//Toast.makeText(this,date,Toast.LENGTH_LONG).show();
//assume dbHelper is ready at this point
cursor = dbHelper.getTodaysTasks(date);
}
else{
cursor = dbHelper.getAllTasks();
}
}
Call this function inside onclick listner
filterToday = false;//initialize to false if necessary
todayButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
filterToday = !filterToday ;//by doing it like this you can switch back and forth. add/check extra logic if necessary
LoadMyData();
}
});
LoadMyData(); //Call this outside in the oncreate to load the data initially
I'm not suggesting a code improvement here. Just changes to what you already have. Hope it helps.
There were two issues that I ran into, the first was comparing a string to to the SQL string[], which meant while the comparison was working, it did not behave as I expected. Once fixing this issue was updating the ListView object. This was handled by creating a new adapter and attaching it to ListView, which then updates itself.
Per this answer: Refresh Current Fragment (ListView Data) remaining in the same activity
The other option was to call notifyDataSetChanged() on the adapter, but this did not work well with the way I had my program set up.
I have a database with team names and team numbers(both stored as strings - the numbers sometimes have letters in them)
I want it so when you click the "Open" button, it'll bring up a popup that will have all of the teams' names and numbers, and if you click one, it will close the popup and set both editText's to the name & number.
Dialog d = new Dialog(this);
ScoutingFormData info = new ScoutingFormData(this);
info.open();
ScrollView scr = info.getData();
info.close();
d.addContentView(scr, null);
d.show();
That's from the main program, when you click "Open", this happens. ScoutingFormData is my SQLite database, and here's getData:
public ScrollView getData() {
String[] columns=new String[]{KEY_ROWID,KEY_NAME,KEY_NUM};
Cursor c = ourDatabase.query(DATABASE_TABLE, columns, null, null, null, null, null);
ScrollView result=new ScrollView(null);
int iID = c.getColumnIndex(KEY_ROWID);
int iName = c.getColumnIndex(KEY_NAME);
int iNum = c.getColumnIndex(KEY_NUM);
LinearLayout vertlay = new LinearLayout(null);
vertlay.setOrientation(LinearLayout.VERTICAL);
for (c.moveToFirst(); !c.isAfterLast();c.moveToNext()){
TextView tv=new TextView(null);
tv.setText(c.getString(iNum)+" "+c.getString(iName));
vertlay.addView(tv);
}
result.addView(vertlay);
return result;
}
So this builds the dialog, but how do I make it so the main program(the top code) will know when one of these TextViews have been clicked?
Side question: when making a View(like LinearLayout vertlay = new LinearLayout(null);), what context am I supposed to be using? I don't fully understand what a 'context' is, so I'm really at a loss of what to replace "null" with.
How do I make it so the main program(the top code) will know when one of these TextViews have been clicked?
Simply make tv clickable and add an OnClickListener.
Side question: what context am I supposed to be using?
You hinted that getData() is in your database adapter class ScoutingFormData, save a reference to the Context passed to the Constructor and reuse it later:
private Context mContext;
public ScoutingFormData(Context context) {
mContext = context;
}
All together:
TextView tv=new TextView(mContext);
tv.setClickable(true);
tv.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
// Do something
}
});
is it possible to create a View which is driven by a SimpleCursorAdapter. The content from this view is ever time a entry from DB.
The View (dataView) looks like:
txtData1
txtData2
txtData3
btnPrev btnNext
I read around and tryd to setup this behavior. Hope its make sens:
public class mActivity extends Activity {
public Context me = this;
public SimpleCursorAdapter mAdapter = null;
public Cursor mCursor = null;
private OnClickListener btnStart_onClick = new OnClickListener() {
public void onClick(View v) {
setContentView(R.layout.dataView);
mCursor = mDB.rawQuery("SELECT * FROM Data", null);
startManagingCursor(mCursor);
mAdapter = new SimpleCursorAdapter(
me,
R.layout.dataView,
mCursor,
new String[] {"Data1", "Data2", "Data3"},
new int[] {R.id.txtData1 , R.id.txtData2, R.id.txtData3});
mAdapter.setViewBinder(VIEW_BINDER);
mCursor.moveToFirst();
}
};
static final ViewBinder VIEW_BINDER = new ViewBinder() {
public boolean setViewValue(View view, Cursor cursor, int columnIndex)
{
switch (view.getId())
{
case R.id.txtData1:
TextView txt = (TextView) view;
if (txt != null)
{
int index = cursor.getColumnIndex("Data1");
txt.setText(cursor.getString(index));
}
return true;
case R.id.txtData2:
TextView txt = (TextView) view;
if (txt != null)
{
int index = cursor.getColumnIndex("Data2");
txt.setText(cursor.getString(index));
}
return true;
case R.id.txtData3:
TextView txt = (TextView) view;
if (txt != null)
{
int index = cursor.getColumnIndex("Data3");
txt.setText(cursor.getString(index));
}
return true;
default:
return false;
}
}
};
}
When I run from the btnStart_onClick I dont get Data in my Textboxes :-(
Can somebody help? Can it work like this?
Next question: how can I use the Prev or Next Buttons? Possible this is the only thing I miss to "load" the first data...
EDIT: I extended my example with the global mCursor and the call to mCursor.moveToFirst()
On my app I also tested with the next / prev buttons and the function mCursor.moveToNext() and mCursor.moveToPrevious()
But its not change :-(
As far as I can tell, there are a lot of what I think are conceptual/organizational/syntactical problems with your code. First of all, an adapter is usually exploited by a view such as ListView or Spinner, that gets populated with the data retrieved by the adapter via the cursor (or whatever data structure is backing it). However, I don't see this pattern in your code, and I'm left wondering what use an adapter would have in your case.
Second, you perform a whole SELECT * query in your click listener, i.e. you retrieve all your 1000 records for each click on... well, on what, exactly? You define the click listener, but never set it onto anything - just as you define the adapter, but you don't bind it to anything. The code that sets up the adapter, with the database query and the binder should really be placed outside the listener.
Last, I believe you mocked variable names a bit before posting the code, because in the following snippet:
TextView txt = (TextView) view;
if (txt != null)
{
int index = cursor.getColumnIndex("Data1");
String txt = cursor.getString(index);
txt.setText(txt);
}
I could hardly see how the compiler is intended to distinguish the two txt variables on the last line of the if body.