Interchange positions of two Views within GridLayout (Android) - android

I have programmed a basic gaming App that consists of blocks which can be exchanged by a swipe (similar to CandyCrush). I succeeded in recognizing the swipe gesture but my code that changes the positions of the Views (blocks) within a GridLayout does only work in the emulator but not on a real device (Samsung Galaxy S3).
Also I couldn't find a way of animating the interchange of the two views. I've added android:animateLayoutChanges="true" to the GridLayout XML but it didn't change anything.
Here's my exchanging code:
// change to blocks positions in grid layout
GridLayout.LayoutParams sourceParams = (GridLayout.LayoutParams)this.imageView.getLayoutParams();
GridLayout.LayoutParams targetParams = (GridLayout.LayoutParams)otherBlock.imageView.getLayoutParams();
GridLayout.Spec sourceRowSpec = sourceParams.rowSpec;
GridLayout.Spec sourceColumnSpec = sourceParams.columnSpec;
sourceParams.rowSpec = targetParams.rowSpec;
sourceParams.columnSpec = targetParams.columnSpec;
targetParams.rowSpec = sourceRowSpec;
targetParams.columnSpec = sourceColumnSpec;
this.imageView.setLayoutParams(sourceParams);
otherBlock.imageView.setLayoutParams(targetParams);
gameGridLayout.requestLayout();
And here's a screenshot of the App:

You should have an index table of your layout, and in your drawing method you run through this index to draw each block.
Like that:
private int [][] index;
void onDraw(Canvas canvas) {
for (int i = 0; i < MAX_BLOCK_HEIGHT; i++) {
for (int j = 0; j < MAX_BLOCK_WIDTH; j++) {
// Draw block index[i][j]
}
}
}
void swapBlock(int blockX, int blockY, int relativePos) {
int tmpBlock = index[blockY][blockX];
if (relativePos == AT_LEFT) {
index[blockY][blockX] = index[blockY][blockX - 1];
index[blockY][blockX - 1] = tmpBlock;
}
// And so on...
}
And this index can be helpful all the time, not only for blit.

Related

how to create array of numbers in range 1 to 100 in view class

everyone, Please help me!
I want to show an array of numbers in a shape.
What I did
1) I draw a shape with canvas in View class.
2) I create a single random number.
3) I've searched a lot & find out that should use Drawtext to show a text or number.
4) I've read the documentation of Random, Drawtext, etc.
5) I've tried for loop but it didn't work outside of canvas class & inside, also will repeat that single number infinite times.
my Problem
I don't know how to put that number in an array & how to show array with drawtext in view class.
at the moment, I can show only one random number, but a want to show an array of random numbers.
I'm very new in Android & my English is not so good.
I'll be very grateful if anybody can Help me through this.
Thank You.
Here's the part of my code that I used for creating a single random number (this is outside of class, constructor & onDraw method) :
Random rand = new Random();
int number = rand.nextInt(100) + 1;
String mystring = String.valueOf(number);
& in onDraw method for showing this number i used below code :
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawText(mystring,130,480,black_paintbrushstroke);
}
Please try to use ArrayList to hold your random data, like:
ArrayList<Integer> arrayList = new ArrayList<>(100);
Random r = new Random(System.currentTimeMillis());
for(int i = 0; i < 100; i++) {
arrayList.add(r.nextInt());
}
Then:
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
int yOffset = 10;
int yStartPosition = 480;
for (int i = 0; i < arrayList.size(); i++) {
Integer integer = arrayList.get(i);
canvas.drawText(String.valueOf(integer), 130, yStartPosition+(i*yOffset), black_paintbrushstroke);
}
}
Looks like you are drawing all your items into the same postion (x,y), you need to add some vertical offset to draw new value bellow previous one.
If you want to draw all you numbers as comma-separted string, you need to convert your array into String using StringBuilder:
StringBuilder sb = new StringBuilder();
for(int i = 0; i < arrayList.size(); i++) {
sb.append(arrayList.get(i)).append(",");
}
String myRandomNumbersArray = sb.toString();
sb.setLength(0);

ImageButton Neighboring Validation

I have a 4x4 tile of letters. I wanted to have a validation that only those letters nearest to the tapped/clicked/pressed letter must be clickable and others are set as unclickable. I have my code (see below) for image1. I am having a second thought of how will I enable disabled ImageButton when I tap ImageButton near it.
Example of set:
1 | 5 | 9 | 13
2 | 6 | 10 | 14
3 | 7 | 11 | 15
4 | 8 | 12 | 16
If I clicked/pressed 1 then the 2, 5 and 6 are clickable and the rest are not. Then, I'll click 2 and 1, 3, 5, 6 and 7 must be clickable.
QUESTIONS:
How can I avoid button being clicked again if its already clicked?
How can I enable disabled button and avoiding my Q1?
I am using Android Studio.
Code
if (image1.isPressed()) {
image1.setClickable(false);
image2.setClickable(true);
image3.setClickable(false);
image4.setClickable(false);
image5.setClickable(true);
image6.setClickable(true);
image7.setClickable(false);
image8.setClickable(false);
image9.setClickable(false);
image10.setClickable(false);
image11.setClickable(false);
image12.setClickable(false);
image13.setClickable(false);
image14.setClickable(false);
image15.setClickable(false);
image16.setClickable(false);
}
TIA :)
Avoid writing conditional code. Writing if for each and every image click is a bad practice. So, you can create an arraylist which will always hold the clickable buttons. Based on the clicked button, change this arraylist. Pass this list to a method, which shall enable or disable the clicks for you.
This isn't really about ImageButtons, but rather, what algorithm or methodology would you use to determine which regions are clickable, based on the current state of the system.
We need to separate the logic somewhat. On one side you have your View. Let's just put everything related to updating your grid into a black box, say void updateGrid(List<List< Boolean>> grid). Let's maintain a matrix with entries determining whether or not a button is clickable.
Initially all of the buttons are clickable. You're going to get some notification when a button is clicked, say from a callback void onButtonClick(int i, int j), where i and j give the position of the button. I'm deliberately being vague about this, you'll most likely get an id, and need to resolve the i and j positions yourself. In which case perhaps you'll want to encapsulate the buttonStates and positions in some kind of object - it's up to you.
private <List<List<Boolean>> mGrid;
private static final int GRID_LENGTH = ...;
private static final int GRID_WIDTH = ...;
private static final defaultValue = false;
public void onButtonClick(int i, int j) {
List<List<Boolean>> newGrid = selectGridRegion(i, j);
updateGrid(newGrid);
}
private List<List<Boolean>> selectGridRegion(int i, int j) {
if (!isValidRegion(i, j)) {
return mGrid;
}
ArrayList<ArrayList<Boolean>> grid = makeGrid();
for (int row = 0; row < GRID_LENGTH; row++) {
ArrayList<Boolean> gridRow = grid.get(row);
for (int col = 0; col < GRID_WIDTH; col++) {
gridRow.set(col, isNeighboringRegion(i, j, row, col));
}
}
return grid;
}
private static boolean isNeighboringRegion(int i, int j, int m, int n) {
return (Math.abs(i - m) == 1 && Math.abs(j - n) == 1);
}
private static List<List<Boolean>> makeGrid() {
ArrayList<ArrayList<Boolean>> grid = new ArrayList<ArrayList<Boolean>>();
for (int i = 0; i < GRID_LENGTH; i++) {
ArrayList<Boolean> row = new ArrayList<Boolean>();
for (int j = 0; j < GRID_WIDTH; j++) {
row.add(defaultValue);
}
grid.add(row);
}
return grid;
}
private static boolean isValidRegion(int i, int j) {
boolean isValid = false;
if (0 <= i && i < GRID_LENGTH) {
if (0 <= j && j < GRID_WIDTH) {
isValid = true;
}
}
return isValid;
}
Now, that's a general approach to modeling the grid. All these method except for onButtonClick sound very much like they should be encapsulated in a Grid class! You might want to extend ArrayList, or something similar.

How two Avoid overlapping of ImageViews in Dynamic creation at random positions in android

i am new to android and i am trying to create 50 images dynamically in my activity at random locations using random function. The problem is on the creation of these images they seems to overlap sometimes, can anyone guide me how to avoid those overlapping in dynamic creation, due to my requirement i don't want to do this image view creation via XML.
for (int i = 0; i<30/*R.nextInt(10-4)+4*/; i ++)
{
Random r = new Random();
posX=r.nextInt((displaywidth)-(displaywidth-500))+(displaywidth-500);
posY=r.nextInt((displayheight)-(displayheight-500))+(displayheight-500);
if(pPosX!=posX&&pPosY!=posY)
{
if(posX<=displaywidth&&posY<=displayheight)
{
System.out.println("XX "+posX+" X "+pPosX+" W "+displaywidth);
System.out.println("YY "+posY+" Y "+pPosY+" H "+displayheight);
image[i] = new ImageView(this);
PicID=r.nextInt(pic.length)-1;
if(PicID==-1)
{
PicID++;
}
image[i].setImageResource(pic[PicID]);
image[i].setTag(PicID);
int tag = (Integer) image[i].getTag();
System.out.println("PicTagTask"+tag);
image[i].setLayoutParams(new GridView.LayoutParams(50,50));
image[i].setScaleType(ImageView.ScaleType.CENTER);
image[i].setPadding(1,1,1,1);
image[i].setX(posX);
image[i].setY(posY);
image[i].setAdjustViewBounds(true);
image[i].startAnimation(animation[R.nextInt(4)]/*animScale*/);
image[i].setOnTouchListener(this);
System.out.println("ImageViewID ="+image[i].getId());
mainlayout.addView(image[i]);
pPosX=posX;
pPosY=posY;
passTagImage[i]=PicID;
}`enter code here`

How to programmatically adjust z order of Views

The below code is my attempt to send mMyView to the front or the back of the set of children of mPivotParent so it will be rendered on top or behind the others. Hiding the view will not suffice in my case.
mPivotParent is a FrameLayout.
Looking at mPivotParent.mChildren shows that my code below "works" in that the ordering is being set correctly. Yet it has no impact on the z order. Not only this, but the framerate gets cumulatively slower and slower the more times the repositioning code gets called. There are 4 children total and mPivotParent.mChildrenCount remains at 4 throughout as expected.
I'm targeting API Level 7.
#Override
public boolean onTouchEvent(MotionEvent event) {
Display display = getWindowManager().getDefaultDisplay();
float x = event.getRawX();
float sWidth = (int)display.getWidth();
float xLerpFromCenter = ((x / sWidth) - .5f) * 2.f; // [-1, 1]
mRotateAnimation.mfLerp = xLerpFromCenter;
if(xLerpFromCenter < -0.2f && mPivotParent.getChildAt(0) != mMyView)
{
mPivotParent.removeView(mMyView);
mPivotParent.addView(mMyView, 0);
refreshEverything();
}
else if(xLerpFromCenter > 0.2f && mPivotParent.getChildAt(0) == mMyView)
{
mPivotParent.removeView(mMyView);
mPivotParent.addView(mMyView, mPivotParent.getChildCount() - 1);
refreshEverything();
}
return super.onTouchEvent(event);
}
private void refreshEverything()
{
for(int i = 0; i < mPivotParent.getChildCount(); ++i)
{
mPivotParent.getChildAt(i).invalidate();
mPivotParent.getChildAt(i).requestLayout();
}
mPivotParent.invalidate();
mPivotParent.requestLayout();
}
Partial Solution
Here's a somewhat inefficient hack but it works for my purpose, which is to take the top item and send it to the back, keeping all other items in their same order.
private void putFrontAtBack()
{
for(int i = 0; i < mPivotParent.getChildCount() - 1; ++i)
{
mPivotParent.getChildAt(0).bringToFront();
}
refreshEverything();
}
Note: This doesn't work in the general case of arbitrary re-ordering.
Try this.
private void reorder(int[] order)
{
if(order == null || order.length != mPivotParent.getChildCount()) return;
for(int i = order.length - 1; i >= 0; i--)
{
mPivotParent.getChildAt(order[i]).bringToFront();
}
refreshEverything();
}
This code provides arbitrary reordering. The integer array "order" is used to indicate the new order of each view, where order[n]=x means the new order of childAt(x) is n.

TransitionDrawable doesn't account for padding?

I tried the accepted solution posted here, but it appears to ignore padding. When the second view (in this case a button) displays, it's much smaller than the original which has padding. Is there a workaround for that? Thanks
Yes, TransitionDrawable extends from LayerDrawable which ignores the padding. This is the getPadding() method in the base Android code, which gets rid of anything you specified:
#Override
public boolean getPadding(Rect padding) {
// Arbitrarily get the padding from the first image.
// Technically we should maybe do something more intelligent,
// like take the max padding of all the images.
padding.left = 0;
padding.top = 0;
padding.right = 0;
padding.bottom = 0;
final ChildDrawable[] array = mLayerState.mChildren;
final int N = mLayerState.mNum;
for (int i=0; i<N; i++) {
reapplyPadding(i, array[i]);
padding.left += mPaddingL[i];
padding.top += mPaddingT[i];
padding.right += mPaddingR[i];
padding.bottom += mPaddingB[i];
}
return true;
}
See base Android code here.
To deal with it, I had to first save the padding values before setting the faulty drawable background on my view:
int bottom = theView.getPaddingBottom();
int top = theView.getPaddingTop();
int right = theView.getPaddingRight();
int left = theView.getPaddingLeft();
theView.setBackgroundResource(R.drawable.faulty_drawable);
theView.setPadding(left, top, right, bottom);

Categories

Resources