Hey everyone So I have created a grid on stage using the code below. Everything works fine and the grid is created. Only problem is when i change the columns and rows value to a higher value the grids position changed offset to the center of the stage. I want it to be when i change the values and more columns etc are added the Grid stays dead center.
Also it seems that the grid expands to the positive x axis from the top left hand corner of the grid.
What can I do to fix this?
public function geniusMatchEngine()
{
//Instantiate Variables
nSquares = 0;
//Movie clips
_gridContainer = new MovieClip();
stage.addChild(_gridContainer);
createGrid();
//Add Listeners
stage.addEventListener(Event.ENTER_FRAME, logicHandler);
}
private function logicHandler(e:Event):void
{
}
private function createGrid():void
{
for (var gy:int = 0; gy < rows; gy++)
{
for (var gx:int = 0; gx < columns; gx ++)
{
var _grid:mcGrid = new mcGrid();
_grid.x = (stage.stageWidth / 2) + _grid.width * gx;
_grid.y = (stage.stageHeight / 2) + _grid.height * gy;
_gridContainer.addChild(_grid);
_grid.addEventListener(MouseEvent.CLICK, tapGrid);
}
}
}
private function tapGrid(e:MouseEvent):void
{
trace("CLICKED");
e.currentTarget.destroy(); //Destroy current target
}
private function centerGrid(gCenter:MovieClip):void
{
gCenter.x = (stage.width / 2) - (gCenter.width / 2); // center horizontally.
gCenter.y = (stage.height / 2) - (gCenter.height / 2); // center vertically.
}
To center something on the stage do:
private function centerThis(s:<Type>):void{
s.x = stage.width / 2 - s.width / 2; // center horizontally.
s.y = stage.height / 2 - s.height / 2; // center vertically.
}
Then with your grid you can do:
centerThis(_grid);
Note: make sure to replace <Type> with whatever makes sense. In this case it could be mcGrid but to make it more generic so you can use this function on any movie clip or Sprite use MovieClip or Sprite.
update
Since you seem a little confused about scope, I'll use your specific case as an example.
//Variables
private var rows:int = 2;
private var columns:int = 2;
for (var gy:int = 0; gy < rows; gy++)
{
for (var gx:int = 0; gx < columns; gx ++)
{
var _grid:mcGrid = new mcGrid();
_grid.x = (stage.stageWidth / 2) - _grid.width / 2;
_grid.y = (stage.stageHeight / 2) - _grid.height / 2;
stage.addChild(_grid);
}
}
I thought the _mcGrid was a prefabricated grid. I'm now seeing that it is probably just one cell in the grid (maybe change the class name to _mcGridCell... less confusing). Forget trying to place the cells relative to the stage center. Add them to a parent Sprite (or MovieClip if you prefer) and then center that Sprite (movieClip) with my centerThis formula. This is the beauty of display object containers.
update
Ok. When you center the grid container, everything in the container goes with it. So no need to also try to center the cells in the grid as you have done.
grid.x = (stage.stageWidth / 2) + _grid.width * gx;
should just be
grid.x = _grid.width * gx;
Related
I have a Page which contains an AbsoluteLayout with a PinchZoomContainer (like the one from microsoft docs). In this Container is an image that fills the entire screen. When the user tapes on the image, another image (a pin) is positioned at the tappostion. So far this works. My problem is, that i couldn't figure out, how to calculate the position of the added image (user tapped) when the user zooms in and out.
I want the added image to stay at the tapped position, no matter if the user zooms in or out.
When i put the image in a grid the calculation gets done automatically.
Is there a better way to achieve that? I've chosen AbsoluteLayout because i need to put the image at the tapped position.
The following code is used for zooming. Here I can't figure out how to do the calculation for the added image. The image gets added to the AbsoluteLayout on runtime.
Normal scale
Zoomed in
void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
if (e.Status == GestureStatus.Started)
{
// Store the current scale factor applied to the wrapped user interface element,
// and zero the components for the center point of the translate transform.
startScale = Content.Scale;
Content.AnchorX = 0;
Content.AnchorY = 0;
startTransX = pin.TranslationX;
startTranxY = pin.TranslationY;
}
if (e.Status == GestureStatus.Running)
{
// Calculate the scale factor to be applied.
currentScale = currentScale + (e.Scale - 1) * startScale;
currentScale = Math.Max(1, currentScale);
// The ScaleOrigin is in relative coordinates to the wrapped user interface element,
// so get the X pixel coordinate.
double renderedX = Content.X + xOffset;
double deltaX = renderedX / Width;
double deltaWidth = Width / (Content.Width * startScale);
double originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;
// The ScaleOrigin is in relative coordinates to the wrapped user interface element,
// so get the Y pixel coordinate.
double renderedY = Content.Y + yOffset;
double deltaY = renderedY / Height;
double deltaHeight = Height / (Content.Height * startScale);
double originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight;
// Calculate the transformed element pixel coordinates.
double targetX = xOffset - (originX * Content.Width) * (currentScale - startScale);
double targetY = yOffset - (originY * Content.Height) * (currentScale - startScale);
// Apply translation based on the change in origin.
// needs to use Xamarin.Forms.Internals or Extension
Content.TranslationX = targetX.Clamp(-Content.Width * (currentScale - 1), 0);
Content.TranslationY = targetY.Clamp(-Content.Width * (currentScale - 1), 0);
x = Content.TranslationX;
y = Content.TranslationY;
// Apply scale factor
Content.Scale = currentScale;
}
if (e.Status == GestureStatus.Completed)
{
// Store the translation delta's of the wrapped user interface element.
xOffset = Content.TranslationX;
yOffset = Content.TranslationY;
x = Content.TranslationX;
y = Content.TranslationY;
}
}
For study purpose I am trying to write an Android app that can load pictures in any size (e.g. 8000x8000px). To do so I have to preprocess a picture into smaller pieces and load only the pieces that need to be displayed into the cells of a GridLayout view group.
I am able follow the instruction and make this view scroll (i.e. panning). Now I am trying to add zooming to this GridLayout view, but it doesn't seem to be as straightforward. Basically the view keeps shrink to (0, 0) instead of the focus point where multi-touch happens. The zooning behavior I need is something like Google Map. However my implementation does not really work. It always zoom to the upper left conner (0,0), because for zoom in/out, only the size scale is changed.
My code looks like
private final class onScaleListener implements OnScaleListener {
#Override
public void onScale(float scaleFactor, float focusX, float focusY)
{
for (int x = 0; x < GRID_DENSITY; x++)
{
Spec columnSpec = GridLayout.spec(x);
for (int y = 0; y < GRID_DENSITY; y++)
{
Spec rowSpec = GridLayout.spec(y);
LayoutParams l = new LayoutParams(rowSpec, columnSpec);
l.height = (int) (_gridHeight * scaleFactor);
l.width = (int) (_gridWidth * scaleFactor);
_gridLayout.updateViewLayout(_imageViewMatrix[x][y], l);
}
}
}
}
#Override
public boolean onScale(ScaleGestureDetector arg0)
{
_scaleFactor *= _scaleGestureDetector.getScaleFactor();
// Don't let the object get too small or too large.
_scaleFactor = Math.max(0.1f, Math.min(_scaleFactor, 5.0f));
_onScaleListener.onScale(_scaleFactor, _scaleGestureDetector.getFocusX(), _scaleGestureDetector.getFocusY());
return true;
}
Thanks!
Im working on my circular list view. I implement almost all functionality, but i have some problem. When my activity starts my listView looks like: https://dl.dropbox.com/u/44444463/Screenshot_2013-02-28-14-37-17.png
But it is not right. It should looks like: https://dl.dropbox.com/u/44444463/Screenshot_2013-02-28-14-37-44.png
Here is my problem.
When activity starts its look like in first screenshot. If i touch any element in listView, or scroll it (if there is a lot of elements) it baceme like in screenshot2. Exactly what i need.
How can i take image like in scrennshot 2 in activity start?
I overrited method #Override public void onScroll(..) in OnScrollListener of my listView, where i aplly all paddings:
// int x = 30;
for (int i = 0; i < visibleItemCount; ++i)
{
View v = listView.getChildAt(i);
if (v != null)
{
LinearLayout rl = (LinearLayout) v.findViewById(R.id.rl);
float sin = (float) (v.getTop() + v.getHeight() / 2 - centerOfCircle) / radius;
double angle = Math.asin(sin);
int x = (int) (-dXRow + radius * Math.cos(angle));
if (x < 0)
x = 0;
Log.d("MN", "setPadding: " + x);
rl.setPadding(x, 0, 0, 0);
}
}
Really sorry for my Engish
You should set starting peddings in the getView() method of your listView's adapter. And then recompute them onScroll() as you need.
OnScroll method isnt invoked when the listView appear, only when you scroll it.
I need to develop an app in which buttons are hexagons and all of them are placed next to each other creating a grid. Given my little experience in Android, I wonder if GridView is the best approach for this. If that is the case, how could I place the hexagons next to each other?
I have this by now
Using this layout in main.xml:
<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/gridview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="0dp"
android:verticalSpacing="0dp"
android:horizontalSpacing="0dp"
android:numColumns="4"
android:columnWidth="0dp"
android:stretchMode="columnWidth"
android:gravity="top"
/>
And this is what I am trying to get:
I would need some help to place hexagons tied to each other in a fixed structure. I've been playing around with the layout values with no sucess. Would TableView be a better approach?
Thanks a lot
Here is some code I used in an app (it's called 'Connect3, if you'd like to play it :) ). It is a custom layout class that draws hexagonal images in a grid. The grid can be triangular or a tilted rectangle.
The code calculates the bounds (in pixels relative to the origin of the hexgrid) of each imageview and then calls imageView.layout(left,top,right,bottom) to set the calculated bounds. The calculations aren't that hard. The main parameter is the radius of the hexagon. From that, the total hight, total width, effective hight and effective width (the height/width of the imageview respectively the distance between the top/left bounds of two consecutive views). Then it comes down to some simple for loops to draw them.
To make the views clickable, just set an onClickListener when you create them. (I made it a class member, because it made things easier).
The onMeasure functions just calculates the total width and height of the view and calls setMeasuredDimension with those values.
The images used for all this are just the single hexagons as you see them right below the actionbar. Note that the images are squares.
#Override
protected void onLayout(final boolean changed, final int l, final int t, final int r, final int b) {
Log.d(TAG, "board.onlayout called with size "+mSize+" l: "+l+" r: "+r+" t: "+t+" b: "+b);
//If the dimensions of the board haven't changed, a redraw isn't necessary. Just update the images of the views instead by calling invalidate().
if (!changed && !mSizeInvalidated) {
invalidate();
return;
}
int childCount = getChildCount();
//Calculate some useful parameters.
float radius = getResources().getDimension(R.dimen.radius);
float verticalMargin = -radius / 4;
float horizontalMargin = ((float) Math.sqrt(3) / 2 - 1) * radius;
float height = 2 * radius;
float width = height;
float effectiveHeight = height + 2 * verticalMargin;
float effectiveWidth = width + 2 * horizontalMargin;
float totalHeight=(radius * (3 * mSize + 1)) / 2;
float totalWidth;
switch (mGameType) {
case Connect3Turn.GAME_TYPE_HEX:
totalWidth = (((float) mSize * 3 - 1)/ 2) * ((float) Math.sqrt(3)) * radius;
break;
case Connect3Turn.GAME_TYPE_Y:
default:
totalWidth = mSize * ((float) Math.sqrt(3)) * radius;
}
LayoutParams layoutParams = new LayoutParams((int) width, (int) height);
//Code to calculate the offsets for horizontal and vertical centering (this is an option in the .xml file)
//The GAME_TYPE_HEX creates a tilted rectangular board and GAME_TYPE_Y creates a triangular board.
float x_offset_row;
switch (mGameType) {
case Connect3Turn.GAME_TYPE_Y:
x_offset_row=(mSize - 1) * effectiveWidth / 2 + horizontalMargin;
break;
case Connect3Turn.GAME_TYPE_HEX:
default:
x_offset_row=0;
}
switch (mCenterHorizontal) {
//the left side of the grid should be at non-negative coordinates.
case 1: {
x_offset_row += Math.max(0,(r-l-totalWidth)/2);
break;
}
case 2: {x_offset_row += Math.max(0,(r-l-totalWidth));
break;
}
case 0:
default: {
break;
}
}
//calculate the y_offset for vertical centering.
float y_offset = 0;
switch (mCenterVertical) {
case 1: {
y_offset = Math.max(0, (b - t - totalHeight) / 2);
break;
}
case 2: {
y_offset = Math.max(0, (b - t -totalHeight));
break;
}
}
int cell = 0;
for (int row = 0; row < mSize; ++row) {
float x_offset = x_offset_row;
int rowLength;
//The row length depends on the board-type we want to draw.
switch (mGameType){
case Connect3Turn.GAME_TYPE_HEX:
rowLength=mSize;
break;
case Connect3Turn.GAME_TYPE_Y:
default:
rowLength=row+1;
}
Log.d(TAG, "Drawing row "+row+" with "+rowLength+" cells.");
for (int col = 0; col < rowLength; ++col) {
ImageView v;
if (cell < childCount) {
v = (ImageView) getChildAt(cell);
} else {
v = new ImageView(super.getContext());
v.setLayoutParams(layoutParams);
v.setOnClickListener(onClickListener);
addViewInLayout(v, cell, v.getLayoutParams(), true);
}
//Set the image (color) of the cell and put its index in a tag, so we can retrieve the number of the clicked cell in the onClickListener.
v.setImageResource(mImageIds[mImages[cell]]);
v.setTag(cell);
//Set the bounds of the image, which will automatically be cropped in the available space.
v.layout((int) x_offset, (int) y_offset, (int) (x_offset + width), (int) (y_offset + height));
x_offset += effectiveWidth;
++cell;
}
y_offset += effectiveHeight;
//The offset of the next row, relative to this one, again depends on the game type.
switch(mGameType){
case Connect3Turn.GAME_TYPE_Y:
x_offset_row -= effectiveWidth / 2;
break;
case Connect3Turn.GAME_TYPE_HEX:
x_offset_row += effectiveWidth / 2;
}
}
//We updated all views, so it is not invalidated anymore.
mSizeInvalidated=false;
}
You can always work with it like if it was a normal grid. But instead of drawing a square, you draw an hexagon.
http://img811.imageshack.us/img811/9229/uyje.png
But then, you move the odd lines half the width of the hexagon in a way that they will fit with the pair lines.
http://img822.imageshack.us/img822/2298/e5cq.png
I've a custom view which draws a running graph - some magnitude versus time. Now I want to implement a custom scroll bar for this so that I can view past data which are offscreen. The data is available to me. I just need the %offset selection by the user.
Any help/suggestions on implementation would be v helpful.
Code Snippet from my Custom view's onDraw method
public void onDraw(Canvas canvas) {
int totalpts = data.size();
scale = getWidth() / (float) maxpoints;
List<Data> display = new ArrayList<Data>();
int initial = 1;
if (totalpts > maxpoints) {
initial = totalpts - maxpoints;
display = data.subList(initial, data.size() - 1);
} else {
display = data;
}
int size = display.size();
Data start = null;
float x1 = 0, x2 = 0, x = 0;
if (size > 1) {
x1 = getWidth();
start = display.get(display.size() - 1);
for (int i = display.size() - 1; i >= 0; i--) {
Data stop = display.get(i);
x = x1;
x1 -= (stop.x * scale / 1000);
canvas.drawLine(x, start.Y, x1, stop.Y, paint1);
start = stop;
}
}
}
Try putting your custom control inside a HorizonatalScrollView (assuming you want it to scroll horizontally), use ScrollView otherwise), setting the width of your control to "WRAP_CONTENT", and the HoizontalScrollView to "FILL_PARENT". Without seeing the code for your custom view, it's difficult to know whether you might need to do some tinkering with the width calculation to get this working.