Android: deleting programmatically added image buttons with ID's - android

hows it going? I'm creating a little training app for a project, its going fine except for a formatting problem im getting. So, ive a csv file with a name and age for a client. an array is created from this, then I've got a scroll View containing a grid layout and i create Image Buttons from the client array. that's all fine. ive got an add client button at the end of this, the button and its activity work fine, but when you come back to the main screen, the buttons are all screwed up (huge, misplaced etc). So i figured i would loop through and delete all the buttons and repopulate the main screen, except, since i programmatically created them, i cant figure out how to find them to delete them. i tried setting their id's to the index of the array, but then i get a null pointer error.
Function where the buttons are created:
public void fillActivity_main(){
if(listPopulated == false) { // check to see if its aready been created
populateClientList();//fill array with client objects
listPopulated = true;
}
//setup asset manager
AssetManager am = getApplicationContext().getAssets();
//Create the "GridLayout Image Board"
GridLayout buttonBoard = (GridLayout) findViewById(R.id.buttonboard);
int idealWidth = buttonBoard.getWidth(); //get width of the board
int idealHeight = buttonBoard.getHeight() / 2;//same
//create the Listeners, this is a place holder for now but will eventually use SetCurrentClient() (or maybe just switch to Start screen, with the current client?)
View.OnClickListener imageClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
System.out.println("CLICK AT: " + v.getId());
Client temp = clientList[v.getId()];
Intent i = new Intent(getApplicationContext(), DisplayClient.class);
System.out.println(temp.getName());
i.putExtra("name", temp.getName());
System.out.println(i.getStringExtra("name"));
i.putExtra("age", Integer.toString(temp.getAge()));
startActivity(i);
}
};
int j = 0; //used the keep track of the id's we set for the buttons
for (int i = 0; i < clientList.length; i++) {
if (clientList[i] != null) {
//creation and ID setting
ImageButton imgbutton = (ImageButton) new ImageButton(this);
imgbutton.setId(i);
//Layout shit
imgbutton.setImageResource(R.mipmap.ic_launcher);
imgbutton.setMinimumWidth(idealWidth);
imgbutton.setMinimumHeight(idealHeight);
imgbutton.setOnClickListener(imageClickListener);
//check and set image
if(clientList[i].getClientImage().equals(" ")) {
try{
imgbutton.set(am.openFd(clientList[i].getClientImage()));}
catch(Exception ex){
ex.toString();
}
Log.d("ClientImageCheck", "No picture found for " + clientList[i].getName());
}
buttonBoard.addView(imgbutton);
j++;
}
}
//create the new Client Button at the end of all the rest.
Button newClientButton = (Button) new Button(this);
newClientButton.setText("+"); // obvious
newClientButton.setLayoutParams(new LinearLayout.LayoutParams(GridLayout.LayoutParams.WRAP_CONTENT, GridLayout.LayoutParams.WRAP_CONTENT));
newClientButton.setWidth(idealWidth);
newClientButton.setHeight(idealHeight);
View.OnClickListener newClientListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(getApplicationContext(), CreateClientForm.class);
startActivityForResult(i, 199);
//System.out.println("Doing good so far, leaving the createclient form bnut still in main");
}
}; // create listener
newClientButton.setOnClickListener(newClientListener); // assign listener
buttonBoard.addView(newClientButton); //add the button the buttonBoard, after all the clients have been added
}
Function where i do the deleting:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//Check which request we're responding to
if (requestCode == 199) {
// Make sure request was successful
if (resultCode == RESULT_OK) {
// The user made a name and crap.
Bundle extras = data.getExtras();
String name = extras.getString("name");
int age = extras.getInt("age");
Client temp = new Client(name, age);
addClientToArray(temp);
System.out.println(name + "attempted add to array");
}
for(int i = 0; i<clientList.length; i++ ){
View v = findViewById(i);
((ViewManager) v.getParent()).removeView(v);
}
fillActivityMain();
}
if i've got the logic right, the 'i' in the loop should be the appropriate id. Granted, the teach has kind of thrown us in the deep end for this project, never taken mobile apps or anything, so all this code is the result of me googling issues as i run into them. I've read the basics for Views, intents, etc, but there must be something i'm missing.
I've tried making the gridLayout that the buttons sit on a class variable so i could call it buttonBoard.removeView(i) or something.
ive also tried `
for(int i = 0; i<clientList.length; i++ ){
ImageButton btn = (ImageButton) findViewByid(i);
((ViewManager) v.getParent()).removeView(btn);
}

Can you add the replacement images at the same time that you delete the existing images? If so, try this:
for(int i = 0; i < buttonBoard.getChildCount(); i++) {
ImageButton tempButton = (ImageButton) buttonBoard.getChildAt(i);
tempButton.setVisibility(View.INVISIBLE);
buttonBoard.addView(yourImageButtonHere, i); //adds a new ImageButton in the same cell you are removing the old button from
buttonBoard.removeView(tempButton);
}
This approach should also prevent the GridLayout from rearranging where the children are. I believe the default behavior if you delete a child view is that the GridLayout will re-order the children so there is not empty cell at the beginning of the grid. I hope that makes sense.

There is so much wrong with this approach.
Mainly you don't have to create the ImageButtons manually and add them to the GridLayout. That is what recycled views such as GridView or RecyclerView are for. In fact you should use those to avoid OutOfMemoryError from having too much images in your layout.
But also you cannot just call setId(i) in the for loop. Android holds many ids already assigned and you can never be sure whether the id is safe. (Unless you use View.generatViewId())
And since you only want to remove all views added to your GridLayout why don't you just call removeAllViews() on the buttonBoard?

Related

Android Development - Create a menu with 3 imageButtons

First of all english is not my first language but i will try my best.
Also... i am pretty sure my title choice was not the best so sorry for that.
Basically what i wanted to do is a menu with three ImageButtons but there is a tricky part (tricky for me at least) since every time i press one button that same button changes image (to a colored version instead of a grayed out image) and the other two change as well from colored version of their respective images to grayed out ones, actually only one of the other two will change since the purpose of this is to be able to activate only one at a time so it would not be possible to have the other two active at the same time.
Notice that this is not a menu on the top right corner but just a set of three ImageButtons on a activity or Fragment.
I already tried a lot of stuff to make that happen but so far no luck but i think i know why though i can't find a workaround for this since i am actually new in android dev.
what i tried was inside the setOnClickListener of any of those buttons such as:
eventsButton.setOnClickListener(
new View.OnClickListener() {
public void onClick(View view) {
ImageButton eventsButton = (ImageButton) view.findViewById(R.id.eventsButton);
eventsButton.setBackgroundResource(R.drawable.events_icon_active);
eventsButton.setClickable(false);
}
}
);
i tried to add the functions to change the other imageButtons as well like:
eventsButton.setOnClickListener(
new View.OnClickListener() {
public void onClick(View view) {
ImageButton eventsButton = (ImageButton) view.findViewById(R.id.eventsButton);
eventsButton.setBackgroundResource(R.drawable.events_icon_inactive);
eventsButton.setClickable(false);
ImageButton contactsButton = (ImageButton) view.findViewById(R.id.contactsButton);
contactsButton.setBackgroundResource(R.drawable.contacts_icon_inactive);
contactsButton.setClickable(true);
ImageButton interestsButton = (ImageButton) view.findViewById(R.id.interestsButton);
interestsButton.setBackgroundResource(R.drawable.interests_icon_inactive);
interestsButton.setClickable(true);
}
}
);
and i repeated that three time, always setting the other buttons clickable and setting their images to the inactive one (the grayed out one), also setting the button i click as no longer clickable.
But from what i gather i cant do any references to any other buttons inside the eventsButton.setOnClickListener like the buttons interestsButton or contactsButton, it will crash the app as soon as i touch any of those three buttons with the following error message:
Attempt to invoke virtual method 'void android.widget.ImageButton.setBackgroundResource(int)' on a null object reference
And it always point to the first line where i make a reference to another button other then the one used to start the setOnClickListener.
If you can just point me in the right direction i would be tremendously grateful.
All the best
You can declare your ImageViews as final outside the scope of the listener and when the onClickListener(View v) is called you can then just call setBackground because they are final and you can reference them from inside the listener.
Something like this:
final ImageView view1 = (ImageView) findViewById(R.id.view1id);
final ImageView view2 = (ImageView) findViewById(R.id.view2id);
view1.setOnClickListener(
new View.OnClickListener() {
public void onClick(View view) {
// do whatever you want to the ImageViews
// view1.setBackground...
}
}
);
eventsButton.setOnClickListener(
new View.OnClickListener() {
public void onClick(View view) {
ImageButton contactsButton = (ImageButton) view.findViewById(R.id.contactsButton);
contactsButton.setBackgroundResource(R.drawable.contacts_icon_inactive);
contactsButton.setClickable(true);
}
}
);
Your problem is in view.findViewById(R.id.contactsButton): view here is the button being clicked (the events one), and by calling view.findViewById(contactsButton) you are implicitly saying that the contact button is a child of view, which is not.
Just use findViewById() (from Activity), getActivity().findViewById() (from Fragments), or better container.findViewById() (if you have a reference to the layout containing the three buttons).
I'm not saying that yours is the most efficient way to deal with a menu, just pointing out your error.
You can first make things simple; I suggest:
you add 3 array (Arraylist might be better) fields in your activity class, one for the buttons, one for the active resources and one for the inactive resources
initialize those arrays in the onCreate method;
define a single onClickListener object and use it for all the buttons; Use a loop in the onClick method, see bellow.
In terms of code, it looks like this:
ImageButton[] buttons;
int[] activeResources;
int[] inactiveResources;
protected void onCreate2(Bundle savedInstanceState) {
View.OnClickListener onClickListener = new View.OnClickListener(){
public void onClick(View view) {
ImageButton clickedButton = (ImageButton) view;
for(int i = 0; i<buttons.length; i++){
ImageButton bt = buttons[i];
if(clickedButton==bt){
bt.setBackgroundResource(inactiveResources[i]);
bt.setClickable(false);
}else{
bt.setBackgroundResource(activeResources[i]);
bt.setClickable(true);
}
}
}
};
buttons = new ImageButton[3];
activeResources = new int[3];
inactiveResources = new int[3];
int idx = 0;
buttons[idx] = (ImageButton) findViewById(R.id.eventsButton);
inactiveResources[idx] = R.drawable.events_icon_inactive;
activeResources[idx] = R.drawable.events_icon_active;
idx = 1;
buttons[idx] = (ImageButton) findViewById(R.id.contactsButton);
inactiveResources[idx] = R.drawable.contacts_icon_inactive;
activeResources[idx] = R.drawable.contacts_icon_active;
idx = 3;
buttons[idx] = (ImageButton) findViewById(R.id.interestsButton);
inactiveResources[idx] = R.drawable.interests_icon_inactive;
activeResources[idx] = R.drawable.interests_icon_active;
for(int i =0; i<buttons.length; i++){
buttons[i].setBackgroundResource(activeResources[i]);
buttons[i].setOnClickListener(onClickListener);
}
}
Do not expect it to run right the way, I am giving only ideas, you have to look and see if it fit for you are looking for.

disable button click for certain rows of buttons

I dynamically create Buttons by entering a word. If I write "met", it appears on the screen - one Button per letter. The same thing happens for the next word I enter, and it appears below the previous word --- as shown in the image above.
When I click on a Button it turns green. My question is, what is the best way to disable the clicking of a row of Buttons. Meaning, if the user clicks on the 'm' in "met" I want the user to only be able to click on the Buttons in "met" and to not be able to click on any of the Buttons in "had", "goes", or "ran"
Here is my code:
EDIT
int size = enter_txt.getText().toString().length();
for (int i=0; i<size; i++){
final Button dynamicButtons = new Button(view.getContext());
dynamicButtons.setLayoutParams(rlp);
dynamicButtons.getLayoutParams().width = 130;
dynamicButtons.getLayoutParams().height = 130;
dynamicButtons.setTag("0");
dynamicButtons.setId(1);
dynamicButtons.setText(edit_text_array[i]);
dynamicButtons.setBackgroundResource(R.drawable.button);
button_list.add(dynamicButtons);
linearLayout2.addView(dynamicButtons, rlp);
dynamicButtons.setOnClickListener(
new View.OnClickListener()
{
public void onClick(View view)
{
int i=0;
LinearLayout ll = (LinearLayout) dynamicButtons.getParent();
for(i=0; i<list_of_ll.size();i++){
if (ll == list_of_ll.get(i)){
list_of_ll.get(i).setId(i);
break;
}
}
if(list_of_ll.get(i).getId()==i)
ButtonOnClick(view);
}
});
}
linearLayout2.setId(0);
linearLayout2.setTag("0");
list_of_ll.add(linearLayout2);
EDIT
I created a List of the LinearLayouts for each row of Buttons. The Buttons turn green if the id of the LinearLayout is set to 1. When I click on a Button I want that LinearLayout to stay at 1 and have all other rows/LinearLayouts set to 0 so they become unclickable.
Currently, every Button I click turns green even if it's in a different row. Can someone please help me solve this issue?
Why you don't set Id in the for loop so that you are able to refer and set the onlicklistener to null like jpcrow already mentioned.
Set Id like:
YourCreatedBtn.setId(i+1);
//Id's setted programmatically don't.
have to be unique... But they should be
a positive number (referring to the
android documentation)
And in your on click method simply set onclicklistener for specified Id's to null. Just a hint, hope it helps
Update regarding Thread-openers Comment
I found two simple ways but i would prefer the one which is not commented out in the buttonIsClicked:
LinearLayout llrow;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
llrow = (LinearLayout) findViewById(R.id.test_layout);
//Adding 5 Buttons
for(int i = 0; i<5; i++) {
Button mybtn = new Button(this);
//set LayoutParams here
mybtn.setId(5);
mybtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
buttonIsClicked(v);
}
});
llrow.addView(mybtn);
}
}
private void buttonIsClicked(View v) {
/*ArrayList<View> myButtons = llrow.getTouchables();
for(int i = 0; i < llrow.getChildCount(); i++){
myButtons.get(i).setOnClickListener(null);
}*/
for(int i = 0; i<llrow.getChildCount(); i++){
llrow.getChildAt(i).setOnClickListener(null);
}
}
It's just a simplified Version of your code, but i'm sure you will get the Content..
What if found out is, that you don't have to set the ID in both cases.. You can easily get all the child over
YourRowLinearLayout.getChildAt(starting from 0 to n-1-Views you added)...
I didn't found a way around the for-loop... But this small-little loop will not break your neck regarding to Performance..
The outcommented-code is the second Approach, finding all the Child over getTouchables which logically leads to an ArrayList and that's exactly the reason why i don't like it. You have to initialize an arraylist...... However, this also won't break your neck regarding to Performance but a penny saved is a penny got! ;) Hope it helps and everything is clear. Both of them work! Please mark as accepted answere if it fits your Needs...
You have to distinguish between the two rows, either add them to different ViewGroups or you can use View.setTag(int key, Object tag)

Different On-clicks for programmatically added buttons

I was looking through some topics but couldn't find exact answer or at least couldn't get it right. What happens in the code is that i create one button for each row in my database and each button is supposed to have an OnClick that sends us to another activity along with some values(each button is supposed to have different value) but in the end it seems like i get the same value for all of my buttons which makes me think that it only creates 1 view for all of the buttons.
Cursor przepis = bazaUzytkownikow.rawQuery("SELECT * FROM przepisy", null);
int liczba_wierszy = przepis.getCount();
przepis.moveToPosition(0);
for (int i = 0; i < (liczba_wierszy/4)+1; i++) {
LinearLayout row = new LinearLayout(this);
row.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
for (int j = 0; j < 4; j++) {
if((przepis.moveToPosition((i*4)+j)!=false))
{
nrPrzepisu=(i*4)+j;
Button btnTag = new Button(this);
btnTag.setLayoutParams(new LayoutParams(115, 60));
btnTag.setText(przepis.getString(przepis.getColumnIndex("nazwa")));
btnTag.setTextSize(10);
btnTag.setId(j + 1 + (i * 4));
btnTag.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View t) {
// TODO Auto-generated method stub
Intent IdzPrzepis = new Intent(getApplicationContext(), DodajPrzepis.class);
IdzPrzepis.putExtra("ID_uzytkownika", ID_uzytkownika);
IdzPrzepis.putExtra("nr_Przepisu", nrPrzepisu);
startActivity(IdzPrzepis);
}
});
row.addView(btnTag);
}
}
layout.addView(row);
}
To make the code more clear for you - bazaUzytkownikow is my database, liczba_wierszy is the number of the rows that i got. I move the cursor to the beginning since it's where i want to start and i proceed to "cut" my data using 2 loops. I am aiming for 4 buttons in 1 row.
The part that i think doesn't work is the OnClick method where i want my button to switch activity and send nrPrzepisu which is basically adding a connection between my button and proper row in the database (In the other activity i want to set text, reading rows from database depends on which button you click).
I checked the other activity and it seems to be reading same nrPrzepisu everytime which usually equals the last value of nrPrzepisu=(i*4)+j when loops finish and it made me think that i somehow need to make different views for each button.
you are passing the same object to each onClick then changing that object with the next iteration. In the end all the onClicks have the same nrPzepisu object and it is returning the value which is whatever is last in this example.
int nrPrzepisu = (i*4) + j;
This way you aren't passing the same object into all the onClicks.

Some alternative to hundreds of buttons

Im going to write some android app, which will basically consists of two activities. So first should have a lot of buttons (100+) and on click on any of them I will just get some special id and move to second activity. But is there any alternative to declare that hundreds of buttons and copy/paste one piece of code to every of them setting almost same onClickLister? Is there any special construction? Thanks
Edit: every of buttons are actually indexed from 1 to n. And on the click second activity will be launched and get that index to show it. I cant basically use any spinner or smth else, because there will be 3 rows of clickable things and each of them carring different images
Edit 2: so, to give you an idea, im going to do some table of buttons like in Angry Birds menu when you actually choosing the level you want to play. So, on click you will get id of button and start second activity
Call the method to add buttons
private void addButton(){
LinearLayout view = (LinearLayout) findViewById(R.id.linear_layout_id_here);
Button btn = null;
int w = 50;
int h = 25;
for(int i=1; i<100; i++) {
btn = new Button(this);
btn.setLayoutParams(new LayoutParams(w,h));
btn.setText("button " +i);
btn.setTag(""+i);
btn.setOnClickListener(onClickBtn);
view.addView(btn);
btn = null;
}
}
Call this method for handling onclick on button
private View.OnClickListener onClickBtn = new View.OnClickListener() {
public void onClick(View view) {
final int tag = Integer.parseInt(view.getTag().toString());
switch (tag) {
case 1:
// Do stuff
break;
case 2:
// Do stuff
break;
default:
break;
}
}
};
You should use a ListView.
ListViews are great for handling a lot of items at the same time. They are also natural for the user. Additionally, you use only one click listener - OnItemClickListener.
There's a useful example on how to work with ListViews in the Android Referenence.
You may add buttons in code, something like this:
#Override
public void onCreate(Bundle savedInstanceState) {
/*your code here*/
GroupView gw =findViewById(R.id.pnlButtonscontainer); //find the panel to add the buttons
for(int i=0; i<100; i++) {
Button b = new Button(this);
b.setLayoutParameters(new LayoutParameters(w,h));
b.settext = i+"";
b.setOnClickListener(new OnClickListener(){
});
}
}
I coded directly into browser, so some syntax error may appear in my code, but this is the point, a way, not the only one, to add 100 buttons.

Android: Listener on array items

I have created a For Loop which sets each ImageView an ID and a ClickListener. When the click is pressed, the OnClickListner either doesn't seem to be able to find the ID, or isn't called at all.
public int[] myResources = {R.id.bblock1, R.id.bblock2, R.id.bblock3, R.id.bblock4, R.id.bblock5, R.id.bblock6, R.id.bblock7, R.id.bblock8};
ImageView[] bblock = new ImageView[7];
for(int i = 0; i==myResources.length; i++) {
bblock[i]=(ImageView)layout.findViewById(myResources[i]);
bblock[i].setOnClickListener(ActivityCheck);
}
View.OnClickListener ActivityCheck = new View.OnClickListener(){
public void onClick (View v){
if( v.getId() == (R.id.bblock1)){
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://twitter.com/#!/itz_jonno"));
startActivity(browserIntent);
}
^^ The OnClickListener is not able to to find the view.
Although, WITHOUT using the array and loop the code below does work:
ImageView bblock1=(ImageView)layout.findViewById(R.id.bblock1);
bbblock1.setOnClickListener(ActivityCheck);
Any ideas why the array wont work, but the standard code will?
Change i==myResources.length into i < myResources.length.
The current loop will exit immediately unless 0 == myResources.length.
(The whole event listener thing was a nice decoy though.)
Besides problem with a loop, your Listener will work only for R.id.block1 , it will egnore events for all other views. What exactly you are trying to achieve?
for(int i = 0; i==myResources.length; i++) {
bblock[i]=(ImageView)layout.findViewById(myResources[i]);
bblock[i].setOnClickListener(
new View.OnClickListener(){
public void onClick (View v){
if( write u r checking condition here ){
start u r suitable activity here.
}
);
}

Categories

Resources