New to android and programming in general, so learning concepts as I go.
I have a LinearLayout to which I am adding edittexts dynamically.
I need to be able to get the Index AND Id of any edittext when it is selected or in focus.
I have tried looping through a child count to check for selected like so:
int count = llmain.getChildCount();
for (int i=0; i< count; ++i) {
if ((llmain.getChildAt(i).isSelected()) == true){
//Do Stuff
}
but I have no idea if its even close, and that would be only for index.....
Help would be greatly appreciated!
Thanks
EDIT: still dont have a reliable way to achieve this. The example below with the
if(v instanceOf EditText) {
id = v.getId();
index = ll.indexOfChild(v);
returns a -1 for index and a ten digit number for the id, however, i am assigning an id on creation. ?The odd thing is that the code inside the "if" is running, so it at least thinks it has the focused view. Now, if I change the "instanceof" to a null check like
if (v != null){
id = v.getId();
index = llmain.indexOfChild(v);
AND I add a setFocusableInTouchMode(true), I get a proper return, however, it then acts like i called clearFocus(), because none of the EditTexts are focused. Here is my full proof of concept code that returns the correct values but no longer lets the EditTexts actually have focus.
#Override
public boolean dispatchKeyEvent(KeyEvent event) {
if ((event.getAction() == KeyEvent.ACTION_DOWN) &&(event.getKeyCode() == 66)) // KeyEvent.* lists all the key codes u pressed
{
View myView = linflater.inflate(R.layout.action, null);
myView.setId(pos);
pos++;
myView.setFocusableInTouchMode(true);
llmain.addView(myView);
myView.requestFocus();
View v = llmain.findFocus();
if (v != null){
id = v.getId();
index = llmain.indexOfChild(v);
Context context = getApplicationContext();
CharSequence text = "index is:" + index + "id is:" + id;
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
}
return false;
}
This returns the correct values unless I comment the setFocusableInTouchMode line, then it goes back to the odd -1 for index and ten digit for ID. What am I doing wrong? going to have to be a good (and working) answer to get over half of my rep.....
So nobody has a solution for this? its still driving me nuts!
thanks again
My initial thought would be to set the ID equal to the index when adding an EditText, assuming only EditTexts will be in this particular layout:
LinearLayout llMain = (LinearLayout)findViewById(R.id.llmain);
EditText editText = new EditText(this);
//0-based index, so get the number of current views, and use it for the next
editText.setId(llMain.getChildCount());
llMain.addView(editText);
Then, to retrieve the info, put a check into a listener of some sort (onTouch, onFocus, something like that):
#Override
public void onTouch(View v, MotionEvent ev) {
int indexAndId = v.getId();
}
Give this a shot:
LinearLayout ll = (LinearLayout)findViewById(R.id.ll);
int index, id;
//finds the currently focused View within the ViewGroup
View v = ll.findFocus();
if(v instanceOf EditText) {
id = v.getId();
index = ll.indexOfChild(v);
}
That seems like it would work to me, have you tested it and it isn't working?
llmain.getChildAt(i).getId(); will return the views ID to you.
Related
I have a editText and a search button, Now it just shows the first results.
But I want to be able to press the search button again and then show the next indexOf. How can I do this ?
public void searchMethod(final String search) {
int i = mTextCurrentArticle.toLowerCase().indexOf(search.toLowerCase().trim());
if (i == -1) {
mEditTextSearch.setTextColor(Color.RED);
// String cap = search.substring(0, 1).toUpperCase() + search.substring(1);
//i = mTextCurrentArticle.indexOf(cap);
} else {
mEditTextSearch.setTextColor(Color.BLACK);
}
// int line = mTextViewCurrentArticle.getLayout().getLineForOffset(i);
Layout layout = mTextViewCurrentArticle.getLayout();
mScrollView.scrollTo(0, layout.getLineTop(layout.getLineForOffset(i)));
You can use the overloaded version of indexOf which takes a starting index for the search and pass the original index like this:
int i = mTextCurrentArticle.toLowerCase().indexOf(search.toLowerCase().trim());
int nextPosition = mTextCurrentArticle.toLowerCase().indexOf(search.toLowerCase().trim(), i+1);
EDIT:
If you need to cycle through subsequent positions then just make the index i a global variable and initialize it to -1
then inside your searchMethod use
i = mTextCurrentArticle.toLowerCase().indexOf(search.toLowerCase().trim(), i+1);
This will ensure that it returns the first position when called for the first time and then gives the next position when called subsequently
I have 4*7 table and I want to handle every Edit text there , when the user clicks on the 5th edit text for long enough so that the menu pops up he can choose what color he wants.
the problem is how should I do this, I have 4*7 edittexts so I should call registerForContextMenu(View) 28 times even if I did this How can I handle it in onContextItemSelected(menuItem) ?
I don't want to put the whole fragment's view since everytime the user clicks for too long on any position the menu will pop up , so I don't want that.
Edit:
I tried this in the onCreateContextMenu:
if(v.getId() == R.id.table_item1){
Position = 1;
}
and check for the position in the onContextItemSelected , is there a better way/ faster way than this ? since I'll need to put 28 if there :(
You could name your EditTexts edit1, edit2, edit3 etc.
Then you could do a loop
for(int i=1; i<=28; ++i) {
int resId = getResources.getIdentifier("edit" + i, "id", getPackageName());
EditText edt = (EditText)findViewById(resId);
edt.registerForContextMenu(...)
}
and the same in other places where you have to iterate over your EditTexts
There is an really weird thing happening with my listview. I am creating an ListView with buttons and an editText.
It's disposed like this: [Button] [EditText] [Button], The buttons works like an "incrementer" and "decrementer" updating the numerical value of EditText in 1 unit per click.
The problem is, when I click in an button, almost every time an editText of another list view element is changed (the editText of the clicked item is also changed). And if I click in a button of this erroneous changed item, it also changes the editText of the first one. They basically have the same reference of buttons and editText, although they have textViews with data, and this data is different between they.
To accomplish that I created and custom adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
convertView = mInflater.inflate(R.layout.lastproduct_row, null);
holder = new ViewHolder();
holder.btnAddQtd = (Button) convertView.findViewById(R.lastproduct_row.btn_add_qtd);
holder.btnSubQtd = (Button) convertView.findViewById(R.lastproduct_row.btn_sub_qtd);
holder.etQuantidade = (EditText) convertView.findViewById(R.lastproduct_row.et_quantidade);
TextView tv;
holder.tvList = new TextView[PRODUCTROW_INT_KEY.length];
for(int i = 0; i < PRODUCTROW_INT_KEY.length; i++) {
tv = (TextView) convertView.findViewById(PRODUCTROW_INT_KEY[i]);
holder.tvList[i] = tv;
}
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
HashMap<String, String> hm = productsList.get(position);
String key = hm.get(CODIGO_KEY);
for(int i = 0; i < PRODUCTROW_INT_KEY.length; i++) {
holder.tvList[i].setText(hm.get(PRODUCTROW_STR_KEY[i]));
}
holder.btnAddQtd.setTag(key+QTD_FLAG+ADD_ACTION);
holder.btnSubQtd.setTag(key+QTD_FLAG+SUB_ACTION);
holder.btnAddQtd.setOnClickListener(handle);
holder.btnSubQtd.setOnClickListener(handle);
if(novosEstoques.containsKey(key)) {
holder.etQuantidade.setText(MyParseFunctions.parseCentesimal(novosEstoques.get(key).getQuantidade()));
}
return convertView;
}
class ViewHolder {
private TextView []tvList;
private Button btnAddQtd, btnSubQtd;
private Button btnAddQtVol, btnSubQtVol;
private EditText etQuantidade, etQtVolume;
}
I added onClick listenners to the buttons, setting their tags with my listView element ID (concatenated with another informations). Then in my event listener I just get the button parent View (an LinearLayout) and get the EditText from that using getViewAt():
#Override
public void onClick(View v) {
String tag = (String) v.getTag();
if(tag.contains(QTD_FLAG)) {
String []info = ((String) v.getTag()).split(QTD_FLAG);
float qtd;
LinearLayout ll = (LinearLayout) v.getParent();
ll.setBackgroundColor(Color.rgb(0, 128, 30));
EditText et = (EditText) ll.getChildAt(2);
qtd = Float.parseFloat(et.getText().toString().replace(",", "."));
if(info[1].equals(ADD_ACTION)) {
qtd++;
}
else if(info[1].equals(SUB_ACTION)) {
if(qtd > 0)
qtd--;
}
Log.d("TESTE", "MODIFICAR KEY = "+info[0]);
et.setText(qtd+"");
}
}
I'm using an setBackgroundColor in this example to confirm that the LinearLayout instance is duplicated in the lisView. When I click an Button, it's painted in 2 different list view item.
Anyone can point me what could be doing this? I have found people with an duplicated ListView item, I don know if that is my case, cause I have TextView's inside my ListView, and they are not equal, only the LinearLayout portion with buttons and editText is "shared".
I make some changes in my getView method and it's working now! It seems that every time the getView method is called i have not guarantee at all that my editTexts will be filled properly and I didn't realize that. So every getView call I make I set the editText value, if the user edit an ET value, I store it in a HashMap to restore in getView, if there is no entry in HashMap for the given editText, then I set it to the default value (zero):
...
if(convertView == null) {
holder = new ViewHolder();
holder.btnAddQtd = (Button) convertView.findViewById(R.lastproduct_row.btn_add_qtd);
holder.btnSubQtd = (Button) convertView.findViewById(R.lastproduct_row.btn_sub_qtd);
holder.etQuantidade = (EditText) convertView.findViewById(R.lastproduct_row.et_quantidade);
//Now it is easier to get etQuantidade reference in button
//click handle, I just have to do:
// public onClick(View v) {
// EditText etButtonAssociated = (EditText) v.getTag();
// ...
// }
holder.btnAddQtd.setTag(holder.etQuantidade);
holder.btnSubQtd.setTag(holder.etQuantidade);
holder.btnAddQtd.setOnClickListener(handle);
holder.btnSubQtd.setOnClickListener(handle);
...
}
else {
...
}
holder.etQuantidade.setTag(key);
if(novosEstoques.containsKey(key)) {
holder.etQuantidade.setText(MyParseFunctions.parseCentesimal(novosEstoques.get(key).getQuantidade()));
}
else {
holder.etQuantidade.setText("0");
}
return convertView;
Israel,
After looking over your code, I was wondering about an implementation decision you have made. Since each Button is "bound" to a particular EditText, have you considered setting the Tag of those Buttons to the EditText? The Tag may be any Object including a UI element. This is especially useful for dynamic UI elements, such as a runtime populated list.
Since this is handled in your Adapter you wouldn't have to worry about duplicate Parents and such. Additionally, you could avoid having to worry about "finding" the control in your onClick() because you would have it (It's the tag). I'm not sure exactly what your project needs are, but this seems like a potentially viable solution, unless you need those Buttons to accomplish other tasks.
Note of Caution
Just make sure that you erase the Tags' references to the EditText when you are done. Otherwise, you run the risk of leaking some memory.
FuzzicalLogic
I've created an array of 5 clickable textviews using a loop, have set their parameters (size, colour, background image, to be clickable etc) and have set an onClickListener and the array is called "myArrayofTVs". Their ids have been set using the loop int (i). I have another predefined array that hold text string, and other textviews are present on the layout. Later on in the onClick method, and as all the buttons/clickable textviews do something very similar, I'd like to be able to do something like:
#Override
public void onClick(View v) {
if(v == myArrayofTVs[i]) { //using 'i' here doesn't seem to work
tv1.setText(myArray2[i]);
tv2.setText(myArray2[i+1];}
etc
etc}
I've tried various differnt ways such as using switch case statements (don't really want to use these as there will be a lot of repeated code and I'll have to add a new case statement each time I want to add new textview/buttons in the future). Is there anyway of using one statement that will handle all the buttons/clickable textviews based on the variable id given or will I have to use a separate case/tag/id statement for each one?
Many thanks in advance!
Add the views to your ViewGroup and use getChildAt(int index) and getChildCount() to create a loop. You can loop all children/views in the viewgroup and you could check with
if(child instanceof TextView)
if they are of the correct type. Then you could cast the views back to a TextView/Button/View and do the thing you want to do.
But it sounds like you want a list of something. So i would suggest using a ListView with a adapter behind it.
I really think you should use the id provided by Android instead of trying to compare objects. The reason your code wouldn't work, if it had a sufficient for loop around it, is somewhat mysterious, but I would try to parallel the switch statements you see in examples as much as possible by comparing ID's and not objects.
for( int i = 0; i < myArrayofTvs.length; i++ )
if(v.getId() == myArrayofTVs[i].getId()) {
tv1.setText(myArray2[i]);
tv2.setText(myArray2[i+1];
}
}
Also obviously you'll want to avoid an array out of bounds error in that second inner statement.
What I did was programmatically inflate my custom layout and used an onClickListener on that button from the custom layout inflated. Then to interact with a specific item I got the parent view of the view being clicked eg. your button and then used that view to change attributes of the view. This is a snippet of my code. The onClick of the alertDialog is where I go about changing values of the newly inflated view.
// if an edit button of numbers row is clicked that number will be edited
if (view.getId() == R.id.NumberRowEditButton)
{
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("Contact edit");
alert.setMessage("Edit Number");
// Set an EditText view to get user input
final EditText input = new EditText(this);
input.setSingleLine();
alert.setView(input);
alert.setPositiveButton("Ok", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int whichButton)
{
// get input
Editable value = input.getText();
if(value.length() > 4){
View tempView = (View) view.getParent();
TextView tempTV = (TextView) tempView.findViewById(R.id.numberRowTextView);
String number = tempTV.getText().toString();
tempTV.setText(value.toString());
}
else
{
// ...warn user to make number longer
final Toast msgs = Toast.makeText(ContactEdit.this, "Number must be over 4 digits.", Toast.LENGTH_SHORT);
msgs.setGravity(Gravity.CENTER, msgs.getXOffset() / 2, msgs.getYOffset() / 2);
msgs.show();
}
}
});
alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int whichButton)
{
// cancel the dialog
dialog.cancel();
}
});
alert.show();
}
Hopefully this might help you.
listView=(ListView)findViewById(R.id.list);
listView.setOnItemClickListener(new OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> parent, View viewItem, int position, long arg3) {
if(!itemClicked)
{
viewItem = parent.getChildAt(position);
((Button)viewItem.findViewById(R.id.gov)).setVisibility(View.VISIBLE);
((Button)viewItem.findViewById(R.id.re)).setVisibility(View.VISIBLE);
viewItem.invalidate();
itemClicked=true;
clickedItemPos=position;
TextView text = (TextView)viewItem.findViewById(R.id.item);
ime = text.getText();
}
else
{
viewItem=parent.getChildAt(clickedItemPos);
((Button)viewItem.findViewById(R.id.go)).setVisibility(View.INVISIBLE);
((Button)viewItem.findViewById(R.id.re)).setVisibility(View.INVISIBLE);
viewItem = parent.getChildAt(position);
((Button)viewItem.findViewById(R.id.go)).setVisibility(View.VISIBLE);
((Button)viewItem.findViewById(R.id.re)).setVisibility(View.VISIBLE);
viewItem.invalidate();
clickedItemPos=position;
TextView text = (TextView)viewItem.findViewById(R.id.item);
ime = text.getText();
}
final int[] coordAndCat = FavoriteCoord(ime.toString());
Nullpointer exception happens when I have more elements and when you scroll and click the last item on the listview. How to workaround this?
Note: I'm trying to display 2 buttons in every item that is being clicked. This code works for all clicks on items besides the last one (if there are many elements in the list and you need to scroll)
clickedItemPos isn't defined after else unless clickedItemPos=position has been called earlier. What line does the error happen on?
listView=(ListView)findViewById(R.id.list);
listView.setOnItemClickListener(new OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> parent, View viewItem, int position, long arg3) {
if(!itemClicked)
{
viewItem = parent.getChildAt(position);
//start >> This might be where your problem is (R.id.gov)
((Button)viewItem.findViewById(R.id.gov)).setVisibility(View.VISIBLE);
//end <<
((Button)viewItem.findViewById(R.id.re)).setVisibility(View.VISIBLE);
viewItem.invalidate();
itemClicked=true;
clickedItemPos=position;
TextView text = (TextView)viewItem.findViewById(R.id.item);
ime = text.getText();
}
else
{
viewItem=parent.getChildAt(clickedItemPos);
((Button)viewItem.findViewById(R.id.go)).setVisibility(View.INVISIBLE);
((Button)viewItem.findViewById(R.id.re)).setVisibility(View.INVISIBLE);
viewItem = parent.getChildAt(position);
((Button)viewItem.findViewById(R.id.go)).setVisibility(View.VISIBLE);
((Button)viewItem.findViewById(R.id.re)).setVisibility(View.VISIBLE);
viewItem.invalidate();
clickedItemPos=position;
TextView text = (TextView)viewItem.findViewById(R.id.item);
ime = text.getText();
}
final int[] coordAndCat = FavoriteCoord(ime.toString());
I think you said R.id.gov instead of R.id.go unless R.id.gov is truly an id you have. That is the only thing I saw 'wrong', please show a stacktrace for the error next time, it would help in debugging =)
EDIT
is clickedItemPos initialized somewhere else? because if it isn't this line: viewItem=parent.getChildAt(clickedItemPos); in your else statement would probably be null if nothing was selected before. I understand you want to use this in order to check for previously clicked items, but if nothing was clicked before, this will never get set to a value and therefore be null try doing this in that else statement to avoid the possible null variable:
else
{
if(clickedItemPos != null){ //only add this if there was a previous clicked item
viewItem=parent.getChildAt(clickedItemPos);
((Button)viewItem.findViewById(R.id.go)).setVisibility(View.INVISIBLE);
((Button)viewItem.findViewById(R.id.re)).setVisibility(View.INVISIBLE);
viewItem = parent.getChildAt(position);
((Button)viewItem.findViewById(R.id.go)).setVisibility(View.VISIBLE);
((Button)viewItem.findViewById(R.id.re)).setVisibility(View.VISIBLE);
viewItem.invalidate();
clickedItemPos=position;
TextView text = (TextView)viewItem.findViewById(R.id.item);
ime = text.getText();
}else{
viewItem = parent.getChildAt(position);
((Button)viewItem.findViewById(R.id.go)).setVisibility(View.VISIBLE);
((Button)viewItem.findViewById(R.id.re)).setVisibility(View.VISIBLE);
viewItem.invalidate();
clickedItemPos=position;
TextView text = (TextView)viewItem.findViewById(R.id.item);
ime = text.getText();
}
}
hopefully that will fix it. In your stack trace it says line 91 in your class is the line causing the error, what variable is located on that line? that is usually a good indicator of what variable is causing the null pointer, but since we don't know the line numbers for your code it is hard to guess where the issue is.
Yes there is repeated code in that solution but you could always make a method to put the repeated code in and call that instead if you want.
Good Luck, hope this helps.