I have a fairly simply Fragment that adds a handful of colored ImageViews to a RelativeLayout. There are more images than can fit on screen, so I implemented some custom scrolling.
However, When I scroll around, I see that there is an approximately 90dp white border overlapping part of the content right where the edges of the screen are before I scroll.
It is obvious that the ImageViews are still being created and drawn properly, but they are being covered up.
How do I get rid of this?
I have tried:
Changing both the RelativeLayout and FrameLayout to WRAP_CONTENT, FILL_PARENT, MATCH_PARENT, and a few combinations of those.
Setting the padding and margins of both layouts to 0dp.
Example:
Fragment:
public class MyFrag extends Fragment implements OnTouchListener {
int currentX;
int currentY;
RelativeLayout container;
final int[] colors = {Color.BLACK, Color.RED, Color.BLUE};
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup fragContainer, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_myfrag, null);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
container = (RelativeLayout) getView().findViewById(R.id.container);
container.setOnTouchListener(this);
// Temp- Add a bunch of images to test scrolling
for(int i=0; i<1500; i+=100) {
for (int j=0; j<1500; j+=100) {
int color = colors[(i+j)%3];
ImageView image = new ImageView(getActivity());
image.setScaleType(ImageView.ScaleType.CENTER);
image.setBackgroundColor(color);
LayoutParams lp = new RelativeLayout.LayoutParams(100, 100);
lp.setMargins(i, j, 0, 0);
image.setLayoutParams(lp);
container.addView(image);
}
}
}
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
currentX = (int) event.getRawX();
currentY = (int) event.getRawY();
break;
}
case MotionEvent.ACTION_MOVE: {
int x2 = (int) event.getRawX();
int y2 = (int) event.getRawY();
container.scrollBy(currentX - x2 , currentY - y2);
currentX = x2;
currentY = y2;
break;
}
case MotionEvent.ACTION_UP: {
break;
}
}
return true;
}
}
XML:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context=".FloorPlanFrag">
<RelativeLayout
android:id="#+id/container"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</FrameLayout>
While looking through the RelativeLayout source, I noticed that onMeasure() calls applyHorizontalSizeRules(LayoutParams childParams, int myWidth) and applyVerticalSizeRules(LayoutParams childParams, int myHeight).
In applyHorizontalSizeRules I found that for the myWidth and myHeight params:
// -1 indicated a "soft requirement" in that direction. For example:
// left=10, right=-1 means the view must start at 10, but can go as far as it wants to the right
The myWidth parameter is initialized to -1, and then changed based on the MeasureSpec's mode for onMeasure()'s parameters.
So I created my own View that extends RelativeLayout, and overrode onMeasure() to set the mode to 'unspecified':
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int newWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED);
int newHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.UNSPECIFIED);
super.onMeasure(newWidthSpec, newHeightSpec);
}
Works like a charm!
Related
I have problem with imageView, after I add layoutParams and set width and height my image goes in TOP LEFT corner after compiling it.How can I fix it...Thank you in andvance.
I'm new in android studio so I'am not sure if I was working everything properly for now and I was started making some small game and this happened.
Here is my XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintGuide_end="100dp"
android:background="#drawable/spacebackground"
tools:context=".MainActivity">
<ImageView
android:id="#+id/plane"
android:layout_width="wrap_content"
android:layout_height="150dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_marginLeft="0dp"
android:layout_marginRight="119dp"
android:layout_marginBottom="29dp"
android:layout_x="116dp"
android:layout_y="343dp"
android:src="#drawable/warplane" />
</RelativeLayout>
And here is my code
package com.example.marko.warmachine;
public class MainActivity extends Activity {
//variable
private ViewGroup mainLayout;
private ImageView image;
private int xDelta;
private int yDelta;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainLayout = (RelativeLayout) findViewById(R.id.main);
image = (ImageView) findViewById(R.id.plane);
plane.setOnTouchListener(onTouchListener());
}
private View.OnTouchListener onTouchListener() {
return new View.OnTouchListener() {
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouch(View view, MotionEvent event) {
final int x = (int) event.getRawX();
final int y = (int) event.getRawY();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams)
view.getLayoutParams();
xDelta = x - lParams.leftMargin;
yDelta = y - lParams.topMargin;
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view
.getLayoutParams();
layoutParams.leftMargin = x - xDelta;
layoutParams.topMargin = y - yDelta;
layoutParams.rightMargin = 0;
layoutParams.bottomMargin = 0;
view.setLayoutParams(layoutParams);
break;
}
mainLayout.invalidate();
return true;
}
};
}
I EDITET code so this is what I get but still not work.
You should not initialize layoutParams from scratch. Get a previously defined attributes from image view instance itself:
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) image.getLayoutParams();
I am working on a project where I have a view, which, once clicked, instantiates a class, passing the view to the constructor, which creates 4 anchor points on to the view. This is done using the following:
customView = new CustomView(MainActivity.this, viewCounter,
customView.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
Resizer resizer = new Resizer(MainActivity.this, MainActivity.this, container, customView, lblStatus);
}
});
The resizer class is as follows:
public Resizer(Context context, AppCompatActivity activity, ViewGroup container, ViewGroup viewToBeResized, TextView lblStatus)
{
this.context = context;
this.activity = activity;
this.container = container;
this.viewToBeResized = viewToBeResized;
this.lblStatus = lblStatus;
createAnchorPoints();
}
private void createAnchorPoints()
{
Drawable circle = ContextCompat.getDrawable(context, R.drawable.anchor);
int circleSize = dpToPx(CIRCLE_SIZE_DP);
Anchor topLeftAnchor = new Anchor(context, viewToBeResized, Anchor.ResizeMode.TOP_LEFT, lblStatus);
topLeftAnchor.setImageDrawable(circle);
RelativeLayout.LayoutParams topLeftParms = new RelativeLayout.LayoutParams(circleSize, circleSize);
topLeftParms.addRule(RelativeLayout.ALIGN_PARENT_START, viewToBeResized.getId());
topLeftParms.addRule(RelativeLayout.ALIGN_PARENT_TOP, viewToBeResized.getId());
viewToBeResized.addView(topLeftAnchor, topLeftParms);
Anchor topRightAnchor = new Anchor(context, viewToBeResized, Anchor.ResizeMode.TOP_RIGHT, lblStatus);
topRightAnchor.setImageDrawable(circle);
RelativeLayout.LayoutParams topRightParms = new RelativeLayout.LayoutParams(circleSize, circleSize);
topRightParms.addRule(RelativeLayout.ALIGN_PARENT_END, viewToBeResized.getId());
topRightParms.addRule(RelativeLayout.ALIGN_PARENT_TOP, viewToBeResized.getId());
viewToBeResized.addView(topRightAnchor, topRightParms);
Anchor bottomLeftAnchor = new Anchor(context, viewToBeResized, Anchor.ResizeMode.BOTTOM_RIGHT, lblStatus);
bottomLeftAnchor.setImageDrawable(circle);
RelativeLayout.LayoutParams bottomLeftParms = new RelativeLayout.LayoutParams(circleSize, circleSize);
bottomLeftParms.addRule(RelativeLayout.ALIGN_PARENT_START, viewToBeResized.getId());
bottomLeftParms.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, viewToBeResized.getId());
viewToBeResized.addView(bottomLeftAnchor, bottomLeftParms);
Anchor bottomRightAnchor = new Anchor(context, viewToBeResized, Anchor.ResizeMode.BOTTOM_RIGHT, lblStatus);
bottomRightAnchor.setImageDrawable(circle);
RelativeLayout.LayoutParams bottomRightParms = new RelativeLayout.LayoutParams(circleSize, circleSize);
bottomRightParms.addRule(RelativeLayout.ALIGN_PARENT_END, viewToBeResized.getId());
bottomRightParms.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, viewToBeResized.getId());
viewToBeResized.addView(bottomRightAnchor, bottomRightParms);
}
In the anchor class that gets created at each corner, a touch listener is used. What I am trying to do is as the user drags the anchor view, the main view, that is passed into the anchor, will resize in the direction the user dragged.
Below is my touch listener
public class AnchorTouchListener implements View.OnTouchListener
{
private int _xDelta;
private int _yDelta;
private View viewToResize;
private TextView lblStatus;
private Anchor.ResizeMode resizeMode;
public AnchorTouchListener(View viewToResize, TextView lblStatus, Anchor.ResizeMode resizeMode)
{
this.viewToResize = viewToResize;
this.lblStatus = lblStatus;
this.resizeMode = resizeMode;
}
#Override
public boolean onTouch(View view, MotionEvent event)
{
final int X = (int) event.getRawX();
final int Y = (int) event.getRawY();
Log.d("Anchor", "Updating X & Y");
int diff = 0;
switch (event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
lblStatus.setText("Moving down");
RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
_xDelta = X - lParams.leftMargin;
_yDelta = Y - lParams.topMargin;
break;
case MotionEvent.ACTION_UP:
lblStatus.setText("Drag finished");
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
lblStatus.setText("Moving around");
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
layoutParams.leftMargin = X - _xDelta;
layoutParams.topMargin = Y - _yDelta;
layoutParams.rightMargin = _xDelta - X;
layoutParams.bottomMargin = _yDelta - Y;
view.setLayoutParams(layoutParams);
//viewToResize.animate().scaleX(0.6f);
if (resizeMode == Anchor.ResizeMode.BOTTOM_RIGHT)
{
diff = diff - X - _xDelta;
Log.d("Anchor Touch", "Diff: " + diff);
if (diff > 0)
{
((RelativeLayout.LayoutParams) viewToResize.getLayoutParams()).width = viewToResize.getLayoutParams().width + Math.abs(diff);
}
else
{
((RelativeLayout.LayoutParams)viewToResize.getLayoutParams()).width = viewToResize.getLayoutParams().width - Math.abs(diff);
}
}
break;
}
return true;
}
}
It is kind of working, except its not moving smoothly with the anchor, the view being resized seems to grow quicker than what is being dragged and is very erratic at how it resize and shrinks.
Is there a better way for doing what I am trying to achieve or can anyone see what I might be doing wrong.
UPDATE
Added video to show what I am trying to achieve and what the problem is.
Since the anchors are positioned with a RelativeLayout, there is no need to write code to move the anchors. Simply resize the grey box and the anchors will be positioned correctly upon layout. The size of the grey box can be determined by capturing initial conditions of the pointer placement and the initial size of the box to achieve the following.
I have only implemented the bottom right anchor and I have taken some liberties with your implementation, but the concept is still valid for your code and the other anchor points.
AnchorTouchListener.java
public class AnchorTouchListener implements View.OnTouchListener {
private int _xDelta;
private int _yDelta;
private View viewToResize;
private TextView lblStatus;
// private Anchor.ResizeMode resizeMode;
public AnchorTouchListener(View viewToResize, TextView lblStatus/*, Anchor.ResizeMode resizeMode*/) {
this.viewToResize = viewToResize;
this.lblStatus = lblStatus;
// this.resizeMode = resizeMode;
}
private int initialHeight;
private int initialWidth;
private int initialX;
private int initialY;
#Override
public boolean onTouch(View view, MotionEvent event) {
final int X = (int) event.getRawX();
final int Y = (int) event.getRawY();
Log.d("Anchor", "Updating X & Y");
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
lblStatus.setText("Action down");
// Capture initial conditions of the view to resize.
initialHeight = viewToResize.getHeight();
initialWidth = viewToResize.getWidth();
// Capture initial touch point.
initialX = X;
initialY = Y;
break;
case MotionEvent.ACTION_UP:
lblStatus.setText("Drag finished");
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
lblStatus.setText("Moving around");
RelativeLayout.LayoutParams lp =
(RelativeLayout.LayoutParams) viewToResize.getLayoutParams();
// Compute how far we have moved in the X/Y directions.
_xDelta = X - initialX;
_yDelta = Y - initialY;
// Adjust the size of the targeted view. Note that we don't have to position
// the resize handle since it will be positioned correctly due to the layout.
lp.width = initialWidth + _xDelta;
lp.height = initialHeight + _yDelta;
viewToResize.setLayoutParams(lp);
break;
}
return true;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Drawable circle = ContextCompat.getDrawable(this, R.drawable.circle);
ImageView imageView = new ImageView(this);
imageView.setImageDrawable(circle);
int circleSize = dpToPx(CIRCLE_SIZE_DP);
RelativeLayout viewToBeResized = findViewById(R.id.customView);
ImageView bottomRightAnchor = new ImageView(this);
bottomRightAnchor.setImageDrawable(circle);
RelativeLayout.LayoutParams bottomRightParms =
new RelativeLayout.LayoutParams(circleSize, circleSize);
bottomRightParms.addRule(RelativeLayout.ALIGN_PARENT_END, viewToBeResized.getId());
bottomRightParms.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, viewToBeResized.getId());
viewToBeResized.addView(bottomRightAnchor, bottomRightParms);
bottomRightAnchor.setOnTouchListener(
new AnchorTouchListener(viewToBeResized, ((TextView) findViewById(R.id.status))));
}
private int dpToPx(int dp) {
return (int) (dp * getResources().getDisplayMetrics().density);
}
private static final int CIRCLE_SIZE_DP = 20;
}
activity_main.xml
<RelativeLayout
android:id="#+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<RelativeLayout
android:id="#+id/customView"
android:layout_width="150dp"
android:layout_height="150dp"
android:background="#android:color/holo_green_light" />
<TextView
android:id="#+id/status"
android:layout_width="wrap_content"
tools:text="Status"
android:layout_height="wrap_content" />
</RelativeLayout>
RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
In above line of code in your AnchorTouchListener class you are getting the params of the view endpoint you created earlier.
Getting the correct LayourParams should solve the problem.
Background
I have a custom ViewGroup subclass that rotates and mirrors its child view. The purpose for this is to correctly display traditional Mongolian text.
I could put anything is this ViewGroup, but for my current project I am putting an EditText in it. (I was never successful in just rotating and mirroring the EditText directly. However, wrapping it in this custom view group does work.)
Problem
My problem is that when I try to resize the ViewGroup programmatically, its child view is not getting resized properly along with it. I would like the EditText to match the size of the parent ViewGroup so that it appears to be a single view.
MCVE
I made a new project to show the problem. The button increases the width of the ViewGroup (shown in red). The images show the project start (with everything working fine) and two width increments. The EditText is white and is not getting resized even though the width and height are set to match_parent
The full project code is below.
MongolViewGroup.java (Custom ViewGroup that rotates and mirrors its content)
public class MongolViewGroup extends ViewGroup {
private int angle = 90;
private final Matrix rotateMatrix = new Matrix();
private final Rect viewRectRotated = new Rect();
private final RectF tempRectF1 = new RectF();
private final RectF tempRectF2 = new RectF();
private final float[] viewTouchPoint = new float[2];
private final float[] childTouchPoint = new float[2];
private boolean angleChanged = true;
public MongolViewGroup(Context context) {
this(context, null);
}
public MongolViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
}
public View getView() {
return getChildAt(0);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final View view = getView();
if (view != null) {
measureChild(view, heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(resolveSize(view.getMeasuredHeight(), widthMeasureSpec),
resolveSize(view.getMeasuredWidth(), heightMeasureSpec));
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (angleChanged) {
final RectF layoutRect = tempRectF1;
final RectF layoutRectRotated = tempRectF2;
layoutRect.set(0, 0, right - left, bottom - top);
rotateMatrix.setRotate(angle, layoutRect.centerX(), layoutRect.centerY());
rotateMatrix.postScale(-1, 1);
rotateMatrix.mapRect(layoutRectRotated, layoutRect);
layoutRectRotated.round(viewRectRotated);
angleChanged = false;
}
final View view = getView();
if (view != null) {
view.layout(viewRectRotated.left, viewRectRotated.top, viewRectRotated.right,
viewRectRotated.bottom);
}
}
#Override
protected void dispatchDraw(Canvas canvas) {
canvas.save();
canvas.rotate(-angle, getWidth() / 2f, getHeight() / 2f);
canvas.scale(-1, 1);
super.dispatchDraw(canvas);
canvas.restore();
}
#Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
invalidate();
return super.invalidateChildInParent(location, dirty);
}
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
viewTouchPoint[0] = event.getX();
viewTouchPoint[1] = event.getY();
rotateMatrix.mapPoints(childTouchPoint, viewTouchPoint);
event.setLocation(childTouchPoint[0], childTouchPoint[1]);
boolean result = super.dispatchTouchEvent(event);
event.setLocation(viewTouchPoint[0], viewTouchPoint[1]);
return result;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
MongolViewGroup viewGroup;
EditText editText;
int newWidth = 300;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewGroup = (MongolViewGroup) findViewById(R.id.viewGroup);
editText = (EditText) findViewById(R.id.editText);
}
public void buttonClicked(View view) {
newWidth += 200;
ViewGroup.LayoutParams params = viewGroup.getLayoutParams();
params.width=newWidth;
viewGroup.setLayoutParams(params);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.example.mongolviewgrouptest.MainActivity">
<com.example.mongolviewgrouptest.MongolViewGroup
android:id="#+id/viewGroup"
android:layout_width="100dp"
android:layout_height="200dp"
android:background="#color/colorAccent">
<EditText
android:id="#+id/editText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#android:color/black"
android:background="#android:color/white"/>
</com.example.mongolviewgrouptest.MongolViewGroup>
<Button
android:text="Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/button"
android:onClick="buttonClicked"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"/>
</RelativeLayout>
You're not recalculating viewRectRotated for your EditText when the
ViewGroup's onLayout(...) method is called again.
Since angleChanged is set to false (and never changes) after your ViewGroups first layout, then the part that calculates the left, right, top and bottom values for your EditText
is skipped any time after the first time when your ViewGroup
requestsLayout (when you change its height or width).
As such, your EditText is still laid out with the same left,right,top
and bottom values it was initially laid out with.
Do away with the angleChanged and it should work just fine. Like so:
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final RectF layoutRect = tempRectF1;
final RectF layoutRectRotated = tempRectF2;
layoutRect.set(0, 0, right - left, bottom - top);
rotateMatrix.setRotate(angle, layoutRect.centerX(), layoutRect.centerY());
rotateMatrix.postScale(-1, 1);
rotateMatrix.mapRect(layoutRectRotated, layoutRect);
layoutRectRotated.round(viewRectRotated);
final View view = getView();
if (view != null) {
view.layout(viewRectRotated.left, viewRectRotated.top, viewRectRotated.right,
viewRectRotated.bottom);
}
}
I've tested this and it works just fine this way.
If you need angleChanged for any reason, then just make sure it's changed back to true inside your ViewGroup's onMeasure method so that viewRectRotated is recalculated again. However I wouldn't recommend that.
I've just started programming for Android. I've searched for my problem a lot, but the advises didn't help me. I want the same images appear on screen in the touch coordinates. That's what I've done:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View main_view = (View)findViewById(R.id.main_view);
main_view.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
ImageView image = new ImageView(getApplicationContext());
//ImageView image = (ImageView)findViewById(R.id.broken);
image.setImageResource(R.drawable.broken);
image.setX(event.getX() + image.getWidth() / 2);
image.setY(event.getY() - image.getHeight() / 2);
LinearLayout top_layout = (LinearLayout) findViewById(R.id.top_layout);
LayoutParams p = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
image.setLayoutParams(p);
top_layout.addView(image);
return true;
}
});
}
Everything seems right to me, but when touching the screen, nothing happens. Where is the obvious mistake I've made? Thanks in advance.
You can't do that in a LinearLayout.
Let's use a FrameLayout instead.
activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/framelayout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
MainActivity.java
public class MainActivity extends Activity {
private FrameLayout mLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLayout = (FrameLayout) findViewById(R.id.framelayout);
mLayout.setOnTouchListener(mListener);
}
private OnTouchListener mListener = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
return true;
case MotionEvent.ACTION_UP:
// decode the resource to get width and height
Options opts = new Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher, opts);
int imageWidth = opts.outWidth;
int imageHeight = opts.outHeight;
// set the imageview's top and left margins
FrameLayout.LayoutParams lp = new LayoutParams(imageWidth, imageHeight);
lp.leftMargin = (int) (event.getX() - (imageWidth / 2));
lp.topMargin = (int) (event.getY() - (imageHeight / 2));
ImageView image = new ImageView(MainActivity.this);
image.setImageResource(R.drawable.ic_launcher);
mLayout.addView(image, lp);
return false;
}
return false;
}
};
}
setX and setY don't set the images position. It basically scrolls the image in place. The position is controlled by its parent view. Since its parent is a linear layout, it will always be placed below or to the right of the thing above it in the layout. If you want to place it somewhere exactly, you need to put it in a parent that supports that, such as the deprecated AbsoluteLayout.
public class MAINActivity extends TabActivity {
host.addTab(host.newTabSpec("Settings")
.setIndicator("Settings", getResources().getDrawable(R.drawable.icon_user))
.setContent(new Intent(this, TwoDScrollView.class)));
........
}
public class TwoDScrollView extends Activity {
private RelativeLayout container;
private int currentX;
private int currentY;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.design);
container = (RelativeLayout)findViewById(R.id.container);
int top = 0;
int left = 0;
ImageView image1 = (ImageView)findViewById(R.id.imageView1);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(left, top, 0, 0);
container.addView(image1, layoutParams);
ImageView image2 = (ImageView)findViewById(R.id.imageView2);
left+= 100;
layoutParams.setMargins(left, top, 0, 0);
container.addView(image2, layoutParams);
ImageView image3 = (ImageView)findViewById(R.id.imageView3);
left= 0;
top+= 100;
layoutParams.setMargins(left, top, 0, 0);
container.addView(image3, layoutParams);
ImageView image4 = (ImageView)findViewById(R.id.imageView4);
left+= 100;
layoutParams.setMargins(left, top, 0, 0);
container.addView(image4, layoutParams);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
currentX = (int) event.getRawX();
currentY = (int) event.getRawY();
break;
}
case MotionEvent.ACTION_MOVE: {
int x2 = (int) event.getRawX();
int y2 = (int) event.getRawY();
container.scrollBy(currentX - x2 , currentY - y2);
currentX = x2;
currentY = y2;
break;
}
case MotionEvent.ACTION_UP: {
break;
}
}
return true;
}
}
When I trying to add the TwoDScrollView Class to the tabbars I got an error of IllegalStateException. The specified child already has a parent. You must removeView() on the child's parent first. What does all those means?
What's happening is that you are implicitly inflating your layout by calling setContentView which creates a bunch of views including four ImageViews apparently in a RelativeLayout. Then you're doing findViewById to find these ImageViews and the RelativeLayout, and after changing their margins programmatically, you add them back to the same layout which already contained them. This is why you get an exception. Those ImageViews already have a parent (the RelativeLayout) and you're trying to give them a new parent (even if it is that same RelativeLayout) without first removing them from their parent. You don't need to call addView at all in this case.