Android GridLayout get row/column - java

I have a 3x3 GridLayout with buttons. These buttons have a onTileClicked() listener. Now I want to get the position of the clicked button in the grid. Here is my code:
public void onTileClicked(View view) {
Button button = (Button) view;
GridLayout.LayoutParams params = (LayoutParams) button.getLayoutParams();
}
But how can i get the row and column of the clicked button? There is params.columnSpec but I don't know what to do with this...

There is no direct way to get the position of the cell. However, you can calculate the position of the clicked button assuming you know the number of columns and rows in the onItemClick listener:
public void onItemClick(AdapterView parent,
View v, int position, long id)
{
int row_no=position/3;
int col_no=position%3;
.
.
}

Thanks for your answer AmmarCSE, but I solved the broblem by just doing something like this:
public void onTileClicked(View view) {
Button button = (Button) view;
if(button.getId()==R.id.btn00)onTileClicked(0, 0, button);
else if(button.getId()==R.id.btn01)onTileClicked(0, 1, button);
else if(button.getId()==R.id.btn02)onTileClicked(0, 2, button);
else if(button.getId()==R.id.btn10)onTileClicked(1, 0, button);
else if(button.getId()==R.id.btn11)onTileClicked(1, 1, button);
else if(button.getId()==R.id.btn12)onTileClicked(1, 2, button);
else if(button.getId()==R.id.btn20)onTileClicked(2, 0, button);
else if(button.getId()==R.id.btn21)onTileClicked(2, 1, button);
else if(button.getId()==R.id.btn22)onTileClicked(2, 2, button);
}

This may be old, but i also looked for a solution and still did not find it.
I needed to make multiple lines of button depending on users choice for a game. Using ID's was not possible because the number of buttons could vary from 2x2 to 9x9.
My button did not need to have a caption, so here is what i did:
layout = (GridLayout) findViewById(R.id.layoutPrincipal);
context = this;
for(int i=0;i<rowCount;i++){
for(int j=0;j<columnCount;j++){
Button mButon= new Button(this);
mButon.setText(""+i+""+j);
mButon.setTextColor(Color.TRANSPARENT);
mButon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Button button = (Button) v;
//button.getText() allows me to retrieve the coordinates.
button.getText()
}
});
layout.addView(mButon);
}
}
Of course this won't work if you need a text on your button. Maybe you could use a tag?

I am facing the same problem and currently the only solution that makes me comfortable is to modify the class and add whatever information you need.
My example is
class ImageView extends android.widget.ImageView {
public ImageView(Context context) {
super(context);
}
//the position of the plane head.
int xPos,yPos;
public void setPosInGrid(int x,int y) {
xPos=x;yPos=y;
}
public int[] getPosInGrid() {
int[] pos=new int[2];
pos[0]=xPos;
pos[1]=yPos;
return pos;
}
}
And now we can use this additional functionality. We can also rewrite the setLayoutParams and integrate the setPosInGrid() into it.
No modification of other codes is required except when you want an ordinary ImageView just use the full identifier "androi.widget.ImageView".

Related

OnClick in one RecyclerView item affects other items

Edit #1: Through debugging I've discovered that the bug 'disappears'. Basically I set a breakpoint and slowly go through steps of checking each multiChoiceItem and the heights of the other RecyclerView child items do not change. Does this mean it is a drawing/timing related issue?
Edit #2: Also, a new find, if I change the height of Child: 6 it changes for Child: 3 and Child: 0
I apologize for the long question. I've checked other answers regarding the same problem and none of them apply. I've tried solving this myself and just couldn't so I would love some help. If there is anything I can do to make this easier to read, please let me know and I'll get right on it!
With the way my code is written, this technically should be impossible to happen but yet here it is.
The Problem: I have an onClickListener() for a TextView within a RecyclerView item. The onClickListener() calls a multiChoiceItem AlertDialog in the container class of the RecyclerAdapter which then calls notifyDataSet(), after completed, with an addOnLayoutChangeListener() at the end which measures the height after the new RecyclerView is drawn.
Notifying that the data set ended then causes the TextView within the RecyclerView item to change to show the text of each Checked item. Then this height is measured in the addOnLayoutChangeListener() and sent to a ViewModel which measures the height of the same position item of three fragments and sets the items height to the max height so they all look the same height.
The Confusing Part: This problem only occurs for one of the three fragments AND the other effected item heights do not match the other two fragments. Which tells me that this is localized to one fragment (which has its own class)
The Code:
The code is long so I reduced it to what I think was important
The ViewHolder
class TextViewViewHolder extends RecyclerView.ViewHolder {
TextView vhTVTextView;
TextView vhTVMainTextView;
CardView vhTVCardView;
TextViewClickedListener vhTextViewClickedListener;
// Gets current position from 'onBindViewHolder'
int vhPosition = 0;
public TextViewViewHolder(View itemView, TextViewClickedListener textViewClickedListener) {
super(itemView);
this.vhTextViewClickedListener = textViewClickedListener;
this.vhTVCardView = itemView.findViewById(R.id.thoughtCard);
this.vhTVTextView = itemView.findViewById(R.id.thoughtNumber);
this.vhTVMainTextView = itemView.findViewById(R.id.textEntry);
/*
When the main TextView is clicked, it calls a function in the container
'FragTextView' which pops up an AlertDialog. It was chosen to do it in the
container instead of here because the Adapter is so adapt the lists data to the view
and the container is what dictates what the lists data actually is.
*/
vhTVMainTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(vhTextViewClickedListener != null) {
vhTextViewClickedListener.onTextViewClicked(vhPosition);
}
}
});
}
}
onBindViewHolder
#Override
public int getItemViewType(int position) {
/*
If mThoughtEntries is not null, then that means we can find the ViewType we are working
with inside of it. Otherwise, we are mDistortions and we must be working on TYPE_TEXTVIEW
*/
if(mThoughtEntries != null) return mThoughtEntries.get(position).getViewType();
else return Constants.TYPE_TEXTVIEW;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
int adapterPosition = holder.getAdapterPosition();
switch (holder.getItemViewType()) {
case Constants.TYPE_EDITTEXT:
EditTextViewHolder editTextViewHolder = (EditTextViewHolder)holder;
// update MyCustomEditTextListener every time we bind a new item
// so that it knows what item in mDataset to update
editTextViewHolder.mMyCustomEditTextListener.setTWPosition(holder.getAdapterPosition());
//Displaying list item to its correct position
editTextViewHolder.vhETTextView.setText(String.valueOf(adapterPosition + 1));
editTextViewHolder.vhETEditText.setText(mThoughtEntries.get(adapterPosition).getThought());
break;
case Constants.TYPE_TEXTVIEW:
TextViewViewHolder textViewViewHolder = (TextViewViewHolder)holder;
// Send current position to viewHolder so when the text listener is called, it knows
// exactly which position of the Distortions list to change
textViewViewHolder.vhPosition = adapterPosition;
//Displaying list item to its correct position
textViewViewHolder.vhTVTextView.setText(String.valueOf(adapterPosition + 1));
textViewViewHolder.vhTVMainTextView.setText(distortionsToString(mDistortions.get(adapterPosition)));
break;
}
}
AlertDialog in Parent
#Override
public void onTextViewClicked(int position) {
//pass the 'context' here
AlertDialog.Builder alertDialog = new AlertDialog.Builder(getContext());
final int recyclerPosition = position;
/*
Turning the distortions into a list of strings and an array of what should, or should
not, be checked.
*/
final String[] distortionStrings = distortionNameToStringArray(mDistortions.get(position));
final boolean[] checkedDistortions = distortionCheckToBooleanArray(mDistortions.get(position));
alertDialog.setMultiChoiceItems(distortionStrings, checkedDistortions,
new DialogInterface.OnMultiChoiceClickListener() {
#Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
if (isChecked) {
// If the user checked the item, add it to the selected items
mDistortions.get(recyclerPosition).get(which).setChecked(true);
} else {
// Else, if the item is already in the array, remove it
mDistortions.get(recyclerPosition).get(which).setChecked(false);
}
/*
Because the RecyclerView takes a while to draw, if we call the below function
as we normally we would, it would appear to have no effect because it would
be automatically overwritten when the RecyclerView is drawn. So we call this
onLayout change listener to wait til the view is drawn and then we call
the function
*/
mRecyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
mRecyclerView.removeOnLayoutChangeListener(this);
// Send new height to the ViewModel
if(mLayoutManager.findViewByPosition(recyclerPosition) != null) {
// Get view of item measuring
View recyclerChild = mLayoutManager.findViewByPosition(recyclerPosition);
// Get LinearLayout from view
LinearLayout linearLayout = recyclerChild.findViewById(R.id.horizontalLayout);
// This is called to find out how big a view should be. The constraints are to check
// measurement when it is set to 'wrap_content'.
linearLayout.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
// Get height of the specified view
int height = linearLayout.getMeasuredHeight();
// Send to child abstracted class which then calls function from 'SharedEntryFragments'
setViewModelHeight(height, recyclerPosition);
}
}
});
mAdapter.notifyDataSetChanged();
}
});
alertDialog.setPositiveButton("Okay", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// DO SOMETHING HERE
dialog.cancel();
}
});
AlertDialog dialog = alertDialog.create();
dialog.show();
}
The function that makes all the fragment item heights equal
I know this part of the code doesn't affect it because where the views that heights are changed are skipped by if(positionalHeight.get(i) != 0) {} So technically...they should never change!
/*
This is the listener that will set all the RecyclerViews childrens heights. It
listens to getTallestLiveHeight() inside of 'SharedEntryFragments.java' and when
a change occurs, this is called
*/
if(getActivity() != null) {
// The container holds the ViewModel so this must make sure getActivity() is not null
mViewModel = ViewModelProviders.of(getActivity()).get(SharedEntryFragments.class);
/*
Creates the observer which updates the UI. The observer takes the
PositionalHeight class as an input. This class keeps track of which index
of the RecyclerView to change and what height it will be changed to.
*/
final Observer<List<Integer>> maxHeight = new Observer<List<Integer>>() {
#Override
public void onChanged(#Nullable final List<Integer> positionalHeight) {
if (positionalHeight != null) {
// Get the index that we are going to change and its height
//int position = positionalHeight.getPosition();
//int height = positionalHeight.getHeight();
/*
We're going to run through each child of mRecyclerView and change
its height accordingly
*/
int listSize = positionalHeight.size();
for(int i = 0; i < listSize; i++) {
// If height reads zero then skip because it will make our view disappear
if(positionalHeight.get(i) != 0) {
// This is the child item that we will be changing
View recyclerChild = mLayoutManager.findViewByPosition(i);
// Ensure that the child exists before continuing
if (recyclerChild != null) {
// We will be changing the CardView's height
// TODO might have to add a check to detect which viewholder
CardView cardView = recyclerChild.findViewById(R.id.thoughtCard);
// Get the LayoutParams first to ensure everything stays the same
ViewGroup.LayoutParams lparams = cardView.getLayoutParams();
// Get and set height
lparams.height = positionalHeight.get(i);
cardView.setLayoutParams(lparams);
}
}
}
}
}
};
mViewModel.getTallestLiveHeight().observe(this, maxHeight);
}
}
I wish I could provide a better answer for other people but this is what I discovered:
For some reason when I call mAdapter.notifyDataSetChanged(); in the AlertDialog function, every third item in the RecyclerView changed to the equaled height. I decided to change it to mAdapter.notifyItemChanged(recyclerPosition); to save on memory and, coincidentally, the bug has disappeared.
If someone could explain why, I will set that as the accepted answer but as of now, this satisfies the question so I will keep it as an answer.

Android - Make Sure ImageView Stays on Screen

I have a Android project where I have an ImageView that moves to the left every time a button is pressed using
tButton.setOnTouchListener(new RepeatListener(10, 10, new View.OnClickListener()
{
#Override
public void onClick(View view)
{
player1.setX(player1.getX() - 1);
}
}));
I don't want this ImageView to leave the screen, and so I've tried adding another ImageView to the edge, and tried using
tButton.setOnTouchListener(new RepeatListener(10, 10, new View.OnClickListener()
{
#Override
public void onClick(View view)
{
Rect p1 = new Rect();
player1.getHitRect(p1);
Rect sL = new Rect();
lSide.getHitRect(sL);
if(Rect.intersects(p1, sL))
{
player1.setX(player1.getX() + 100);
}
else
{
player1.setX(player1.getX() - 1);
}
}
}));
The problem is that after the two objects interest, the onClick method never seemed to get called again. Is there something that I'm forgetting to do, or is there an easier way to solve this problem?
Thanks
There's an easier way to solve the problem. Use imageView.getLocationOnScreen() to get the x and y position of the view on screen. Then you can check and see if moving it again would cause you to have a negative X (move off the screen to the left) or if X+imageView.getWidth()>screen_width (move off to the right).

Destroy a button that was added programatically multiple times on its click

The title may sound confusing I know, I 'm adding a view everytime I click on a button, composed by a textview and a button. I'm setting every added view an ID with simply view.setID(++i) and every added button (inside the views) an ID simply with button.setID(++n), n starting at 1000, since I won't have more than 1000 added views.
Here's what I got:
public class MainActivity extends AppCompatActivity {
GridLayout gridLayout;
static int i;
static int n = 1000;
private Button theButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gridLayout = (GridLayout)findViewById(R.id.gamehistory);
Button b = (Button)findViewById(R.id.Button01);
b.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
theButton = new Button(MainActivity.this);
TextView theText = new TextView(MainActivity.this);
theText.setGravity(Gravity.CENTER_HORIZONTAL);
LinearLayout theLayout = new LinearLayout(MainActivity.this);
theLayout.setOrientation(LinearLayout.VERTICAL);
theLayout.setBackgroundColor(Color.parseColor("#8BAAC3"));
theLayout.setId(++i);
theButton.setId(++n);
theButton.setText(theButton.getId() + "");
theText.setText(theLayout.getId() + "");
theLayout.addView(theButton);
theLayout.addView(theText);
gridLayout.addView(theLayout);
GridLayout.LayoutParams lp = (GridLayout.LayoutParams) theLayout.getLayoutParams();
lp.setMargins(10, 10, 10, 10);
}
});
What I need is when I click on a button that was created, the correspondent view is destroyed, and the next views take one step back feeling the gap in the parent which is a GridLayout
Add this where you are adding views to GridLayout -
theLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
gridLayout.removeView(theLayout);
}
});
theButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
gridLayout.removeView(theLayout);
}
});
For this , you need to make theLayout final
final LinearLayout theLayout = new LinearLayout(LauncherActivity.this);
The simplest method would be.
View v = gridLayout.findViewById(<some id>);
gridLayout.removeView(v);
However it seems like you may want to consider using a RecyclerView. You can add/remove items from the Adapter and the views will be updated for you.
EDIT
When using A RecyclerView you have to specify two essential components.
RecyclerAdapter - This converts data into views (rows, cards, cells, ect.)
LayoutManger - Most common are LinearLayoutManger and GridLayoutManager which define how the views from the adapter are presented out in relation to one another, and handle scrolling.
There are a few more option additions you can can use if needed.
ItemDecoration - define backgrounds, or overlays for cells. (E.G. draw a gray background for every other view in a list)
ItemTouchHelper - does most of the heavy lifting for swipe (e.g. swipe to delete) and drag (e.g. drag to re-arrange) operations.
I would highly suggest getting familiar with the RecyclerView it should be your goto component when you need to display a list of items on the screen.

Loop through each child element and compare its text in java

I am trying to build an android app that gets some questions from a database and its possible answers (the answers are created dynamically as UI buttons). What I have managed so far is that once the user clicks on an answer, if the answer is right the button's colour becomes green, and if the answer is wrong the colour becomes red. What I want to achieve is in case the answer was wrong, change the button's colour to red and find the button with the correct answer and change it's colour to green. I am currently trying to do this by looping through every child element of the clicked button's parent, and comparing its text with the right answer given from the database. This is what my code looks like:
final TableRow textRow = new TableRow(this);
textRow.addView(questionText, rowParamsQuestions);
layout.addView(textRow, layoutParams);
for(final String option : currentQuestion.getOptions())
{
final Button button = new Button(this);
button.setText(option);
button.setTextSize(16);
button.setBackgroundResource(R.drawable.rounded_shape);
button.setTextColor(Color.WHITE);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Button clicked = (Button) v;
if (!answeredQuestions.containsKey(currentQuestion)) {
answeredQuestions.put(currentQuestion, clicked.getText().toString());
if (clicked.getText().equals(currentQuestion.getAnswer())) {
clicked.setBackgroundResource(R.drawable.right_answer);
} else {
clicked.setBackgroundResource(R.drawable.wrong_answer);
for (int i = 0; i < textRow.getChildCount(); i++) {
View child = textRow.getChildAt(i);
if (child instanceof Button) {
if (((Button) child).getText().equals(currentQuestion.getAnswer())) {
child.setBackgroundResource(R.drawable.right_answer);
}
}
}
}
Thank you very much!

performClick not triggering

I am trying to trigger a click on the Carousel. I want that if I press the forward button, it automatically triggers the click on the carousel and then move forward. The manual click (physical touch) is working but performClick() is not. The code is as follows
//************* Forward Button: Select Objects *************
Button forwardButton = (Button)this.findViewById(R.id.ForwardButton);
forwardButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(NewFieldTrip.this, SelectObjects.class);
//ImagePosition = (int)carousel.getSelectedItemId();
carousel.performClick();
i.putExtra("SelectedScene",ImagePosition);
startActivity(i);
}
});
carousel.setOnItemClickListener(new CarouselAdapter.OnItemClickListener(){
#Override
public void onItemClick(CarouselAdapter<?> parent, View view,
int position, long id) {
Toast.makeText(NewFieldTrip.this, "Select Position=" + position, Toast.LENGTH_SHORT).show();
ImagePosition = position;
}
});
A helping hand would be great :)
EDIT:
public void scrollToChild(int i){
CarouselImageView view = (CarouselImageView)getAdapter().getView(i, null, null);
float angle = view.getCurrentAngle();
if(angle == 0)
return;
if(angle > 180.0f)
angle = 360.0f - angle;
else
angle = -angle;
mFlingRunnable.startUsingDistance(-angle);
}
What type is carousel of? ListView?
So if carousel is ListView then what event are you expected on ListView clicking?
performClick() trigger OnClickListener, which you didn't set to cаrousel. You set OnItemClickListener, so you have to call performItemClick(...). Try it.
UPD:
Try to do folowing:
1) make method Carousel.scrollToChild(int i) public
2)
int itemCount = carousel.getAdapter().getCount();
int item = new Random().nextInt(itemCount);
View view - carousel.getAdapter().getView(item, null, null);
int itemId = carousel.getAdapter().getItemId(item);
carousel.scrollToChild(item);
carousel.performItemClick(view, item, itemId);

Categories

Resources