Increase LinearLayout width from either right or left - android

I have a LinearLayout which is centered on the screen. It has a width less than the screen width. There are two buttons: Right-Arrow and Left-Arrow.
When the user presses the relevant button, the layout should increase its width from the relevant side. The other side should keep its position there.
Right now setting the width increases the layout from both sides equally. The layout needs to be initially centered and it has to expand from either side by user's input. (Use case is to find the width of relevant part of an image whose right and left sides have unequal borders, so the user has to mark them using my technique).
I am using following to increase width but it has the behaviour described above.
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams)
llCropOverlay.getLayoutParams();
params.width = params.width + 1;
PS: This functionality was implemented in Tasker app since its early days; so it is possible.
EDIT:
Here is the layout.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:gravity="top"
android:layout_gravity="top"
android:id="#+id/iv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="visible" />
<LinearLayout
android:id="#+id/llRightLeft"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center">
<Button
android:id="#+id/bLeft"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="LEFT" />
<Button
android:id="#+id/bRight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="RIGHT" />
</LinearLayout>
<LinearLayout
android:layout_gravity="center_horizontal"
android:id="#+id/llCropOverlay"
android:layout_width="match_parent"
android:layout_height="70dp"
android:background="#color/colorCropOverlay"
android:orientation="vertical" />
</FrameLayout>
</LinearLayout>
The last LinearLayout (llCropOverlay) should be resized. Note that I am programatically changing the width to 300 before using resizing the buttons so I can test if the buttons are working.

I have found an almost perfect solution (there is sometimes a problem with one pixel which is annoying - any suggestions will be appreciated).
For this, we need some variables set up. Firstly, the LinearLayout called llCropOverlay must be found and identified.
Here is its xml:
<LinearLayout
android:layout_gravity="center_horizontal"
android:id="#+id/llCropOverlay"
android:layout_width="200dp"
android:layout_height="150dp"
android:background="#color/colorCropOverlay"
android:orientation="vertical" />
Now before allowing user to interact we need to find the original position of the llCropOverlay. So use this in OnCreate():
llCropOverlay.post(new Runnable() {
#Override
public void run() {
orgX = llCropOverlay.getX();
}
});
Now set up all the buttons and set a setOnTouchListener() on these buttons. Then when the listener is called, pass the touched button in the following method. Use a Handler and postDelayed() to keep calling this method till the button is pressed. Or call it once to resize by one pixel row/column.
void handleTouchOrClick(View view) {
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams)
llCropOverlay.getLayoutParams();
switch (view.getId()) {
case R.id.bUp:
params.height = params.height - 1;
break;
case R.id.bDown:
params.height = params.height + 1;
break;
case R.id.bRight:
params.width = params.width + 1;
llCropOverlay.post(new Runnable() {
#Override
public void run() {
llCropOverlay.setX(orgX);
}
});
break;
case R.id.bRightContract:
params.width = params.width - 1;
llCropOverlay.post(new Runnable() {
#Override
public void run() {
llCropOverlay.setX(orgX);
}
});
break;
case R.id.bLeft:
params.width = params.width + 1;
orgX--;
llCropOverlay.post(new Runnable() {
#Override
public void run() {
llCropOverlay.setX(orgX);
}
});
break;
case R.id.bLeftContract:
params.width = params.width - 1;
orgX++;
llCropOverlay.post(new Runnable() {
#Override
public void run() {
llCropOverlay.setX(orgX);
}
});
break;
}
llCropOverlay.setLayoutParams(params);
}
Now here's how we actually resize the image:
For ease of users I am cropping it in two steps.
Crop from sides:
ViewGroup.MarginLayoutParams params =
(ViewGroup.MarginLayoutParams) llCropOverlay.getLayoutParams();
float eventX = params.width;
float eventY = 0;
float[] eventXY = new float[]{eventX, eventY};
Matrix invertMatrix = new Matrix();
imageView.getImageMatrix().invert(invertMatrix);
invertMatrix.mapPoints(eventXY);
int x = Integer.valueOf((int) eventXY[0]);
int y = Integer.valueOf((int) eventXY[1]);
int height = params.height;
while (height * 3 > originalBitmap.getHeight()) {
height = height - 10;
}
croppedBitmapByWidth = Bitmap.createBitmap(originalBitmap, (int) orgX, 0,
x, height);
imageView.setImageBitmap(croppedBitmapByWidth);
crop from bottom:
float eventX2 = 0;
float eventY2 = params.height;
float[] eventXY2 = new float[]{eventX2, eventY2};
Matrix invertMatrix2 = new Matrix();
imageView.getImageMatrix().invert(invertMatrix2);
invertMatrix2.mapPoints(eventXY2);
int x2 = Integer.valueOf((int) eventXY2[0]);
int y2 = Integer.valueOf((int) eventXY2[1]);
croppedBitmapByHeight = Bitmap.createBitmap(croppedBitmapByWidth, 0, 0,
croppedBitmapByWidth.getWidth(), y2);
imageView.setImageBitmap(croppedBitmapByHeight);

Related

How to scale 3 images to fit screen width

These is the result that I am after:
Basically I want to scale the 3 images so that they have the same height and all together fill the screen width. The original images will all have same height.
Can this be done using layout, without width calculations from code?
Just use Layout Weights.
In the main layout, or the layout which contains the ImageViews, put
android:weightSum="10"
and then in the individual ImageViews, put layout_weights as shown below, or upto your requirements.
This basically means the width of the images will be 25%, 55% and 20% respectively.
You can use a linear layout with weight attribute specified as shown below:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="0dp"
android:src="#drawable/bg_canvas"
android:layout_height="300dp"
android:layout_weight="0.33"/>
<ImageView
android:layout_width="0dp"
android:src="#drawable/bg_canvas"
android:layout_height="300dp"
android:layout_weight="0.33"/>
<ImageView
android:layout_width="0dp"
android:layout_height="300dp"
android:src="#drawable/bg_canvas"
android:layout_weight="0.33"/>
</LinearLayout>
Comment below if you need any further info
try this:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="3"
android:orientation="horizontal">
<ImageView
android:id="#+id/imageView1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
.../>
<ImageView
android:id="#+id/imageView1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
.../>
<ImageView
android:id="#+id/imageView1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
.../>
</LinearLayout>
the "magic" is in the weight component. you define a total weight of 3 in the layout and your image views should take a third of it, so the value is 1.
For my case the images needed to be updated at runtime, so none of the answers were exact fit.
I ended up extending LinearLayout and writing a small routine that unifies all images heights and make sure that all images together fill the LinearLayout width. In case someone is trying to achieve the same, my code looks like this:
public class MyImgLayout extends LinearLayout
{
public MyImgLayout(Context context)
{
super(context);
}
public void setup(ArrayList<String> images)
{
this.setOrientation(LinearLayout.HORIZONTAL);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);
this.setLayoutParams(layoutParams); //set 0 height until we calculate it in onMeasure
for (String image : images) {
ImageView ivArticle = new ImageView(getContext());
setImageFromName(image, ivArticle); //this where you set the image
this.addView(ivArticle);
}
}
private void scaleImages()
{
if(getMeasuredHeight() == 0 && getMeasuredWidth() > 0) {
if (isHorizontal) {
double childRatioSum = 0;
int images = 0;
for (int i = 0; i < getChildCount(); i++) {
ImageView iv = (ImageView) getChildAt(i);
double width = iv.getDrawable().getIntrinsicWidth();
double height = iv.getDrawable().getIntrinsicHeight();
if (height > 0) {
childRatioSum += width / height;
images++;
}
}
if (childRatioSum > 0 && images == getChildCount()) {
//all images are downloaded, calculate the container height
//(add a few pixels to makes sure we fill the whole width)
double containerHeight = (int) (getMeasuredWidth() / childRatioSum) + images * 0.5;
for (int i = 0; i < getChildCount(); i++) {
ImageView iv = (ImageView) getChildAt(i);
double width = iv.getDrawable().getIntrinsicWidth();
double height = iv.getDrawable().getIntrinsicHeight();
iv.setLayoutParams(new LinearLayout.LayoutParams((int) (width * (containerHeight / height)), (int) containerHeight));
iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
}
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) this.getLayoutParams();
params.width = LayoutParams.MATCH_PARENT;
params.height = (int) containerHeight;
this.setLayoutParams(params);
requestLayout();
}
}
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
scaleImages();
}
}

ScrollView and complex subviews that contains scrolling feauture

I've got a problem with one of my views on android, which has quite complex structure.
Basically, what I wan't to achieve is something like this:
Having scrolled view, that has inside some views with wrap content, but last of those views, has gridview. The problem is that without setting exact height of the grid view (and with wrap content)... scroll doesn't allow to scroll to the end of this grid. I don't want to use scroll inside this GridView, I used this widget to simplify data population and use build-in controls and adapters.
My XML:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="#+id/scrollview_root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
// some relevant subviews
</LinearLayout>
// here problem starts
<GridView
android:id="#+id/grid_products"
android:layout_width="match_parent"
android:minHeight="50dp"
android:layout_height="wrap_content" />
// here problem ends
</LinearLayout>
</ScrollView>
I tried to provide custom scrollView with overwritten onMesure, also tried in onResume invalidateScroll and use computeScroll method.
Here is some code for overwritten onMesure:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int expandSpec = MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK,MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) getLayoutParams();
params.height = getMeasuredHeight()+10000;
setLayoutParams(params);
invalidate();
requestLayout();
}
But with no luck ... still I have to provide exact value for layout_height, otherwise its not visible and not reachable via scrolling.
I experimented a lot and for now have no idea, what I can check next and to do to make it works.
Thanks in advance for any help,
Regards David
UPDATE WITH WORKING SOLUTION
This works for me https://stackoverflow.com/a/8483078/566817 with custom GridView class, that has this method inside:
public int measureRealHeight(Context context)
{
final int columnsCount = 3;
final int screenWidth = ((Activity)context).getWindowManager().getDefaultDisplay().getWidth();
final double screenDensity = getResources().getDisplayMetrics().density;
final int columnWidth = (int) (screenWidth / columnsCount + screenDensity + 0.5f);
final int verticalSpacing = (int) context.getResources().getDimension(R.dimen.grid_spacing_vertical);
final int rowsCount = getAdapter().getCount() / columnsCount + (getAdapter().getCount() % columnsCount == 0 ? 0 : 1);
return columnWidth * rowsCount + verticalSpacing * (rowsCount - 1);
}
Also need to update LayoutParams in my Fragment in onResume method:
#Override
public void onResume()
{
super.onResume();
int realHeight = gridViewProducts.measureRealHeight(getActivity());
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) gridViewProducts.getLayoutParams();
lp.height = realHeight;
gridViewProducts.setLayoutParams(lp);
gridViewProducts.requestLayout();
}
My grid in XML
<xxx.CustomGridView
android:id="#+id/grid_products"
android:isScrollContainer="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:numColumns="3" />
I spend much time on this so far, and have no better solution so far unfortunately (I know its not perfect solution).
I hope it will help someone :)

Programmatic Margin for LinearLayout is ignored

I have an Android layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/dice_container"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="top" >
<FrameLayout
android:id="#+id/die_frame_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal|top" >
<ImageView
android:id="#+id/die1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:contentDescription="#string/dice"
android:src="#drawable/d6" />
<ImageView
android:id="#+id/die_overlay_1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:alpha="0.4"
android:visibility="invisible"
android:src="#drawable/gray_shape"
android:contentDescription="#string/gray_overlay" />
</FrameLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<FrameLayout
android:id="#+id/die_frame_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<ImageView
android:id="#+id/die2"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:contentDescription="#string/dice"
android:src="#drawable/d2" />
<ImageView
android:id="#+id/die_overlay_2"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:alpha="0.4"
android:visibility="invisible"
android:src="#drawable/gray_shape"
android:contentDescription="#string/gray_overlay" />
</FrameLayout>
<FrameLayout
android:id="#+id/die_frame_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<ImageView
android:id="#+id/die3"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:contentDescription="#string/dice"
android:src="#drawable/d1" />
<ImageView
android:id="#+id/die_overlay_3"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:alpha="0.4"
android:visibility="invisible"
android:src="#drawable/gray_shape"
android:contentDescription="#string/gray_overlay" />
</FrameLayout>
</LinearLayout>
</LinearLayout>
And I am trying to programmatically take care of the positions of the images. The problem is that the result is not what I want it to be, which would be three images centered in the form of a pyramid. Die number 1 is the top, and 2 and 3 bottom left and right respectively.
FrameLayout[] frames = new FrameLayout[] {(FrameLayout) findViewById(R.id.die_frame_1),
(FrameLayout) findViewById(R.id.die_frame_2),
(FrameLayout) findViewById(R.id.die_frame_3)};
Point size = getSize();
int width = size.x;
int height = size.y;
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) frames[0].getLayoutParams();
// Width will be restricted either by height or width. Dice should take max 50% of screen
int frameWidth = (int) Math.min(width * 0.44, height * 0.5 * 0.44); // Each dice 40%
params.width = frameWidth;
params.height = frameWidth; // Square, so width equals height
// Ensure that the space between each dice is equal
int frameHeightMargin = (int) (height * 0.5 - frameWidth * 2) / 3; // Divide the remainder evenly
int frameWidthMarginOuter = (int) (width - 2 * frameWidth - frameHeightMargin) / 2; // Calculate outer
int frameWidthMarginCenter = (int) frameHeightMargin / 2; // Same spacing between dice
int frameWidthMarginUpper = (int) (width - frameWidth) / 2; // Upper only
for (int i = 0; i < frames.length; i++) {
if (i == 0) {
params.setMargins(frameWidthMarginUpper, frameHeightMargin, frameWidthMarginUpper, frameWidthMarginCenter); // Gravity is centered
} else if (i == 1) { // Left dice
params.setMargins(frameWidthMarginOuter, frameWidthMarginCenter, frameWidthMarginCenter, frameHeightMargin);
} else if (i == 2) {// Right dice
params.setMargins(frameWidthMarginCenter, frameWidthMarginCenter, frameWidthMarginOuter, frameHeightMargin);
}
frames[i].setLayoutParams(params);
}
Current result:
The code is in the onCreate method by the way. I have both tried with and without requestLayout() for the FrameLayouts and the parents.
Unless you know exactly what you are doing do not use the same LayoutParams for more than one ViewGroup. The solution is very simple. Create a new LayoutParams for each die.
FrameLayout[] frames = new FrameLayout[] {(FrameLayout) findViewById(R.id.die_frame_1),
(FrameLayout) findViewById(R.id.die_frame_2),
(FrameLayout) findViewById(R.id.die_frame_3)};
Point size = getSize();
int width = size.x;
int height = size.y;
// Width will be restricted either by height or width. Dice should take max 50% of screen
int frameWidth = (int) Math.min(width * 0.44, height * 0.5 * 0.44); // Each dice 40%
// Ensure that the space between each dice is equal
int frameHeightMargin = (int) (height * 0.5 - frameWidth * 2) / 3; // Divide the remainder evenly
int frameWidthMarginOuter = (int) (width - 2 * frameWidth - frameHeightMargin) / 2; // Calculate outer
int frameWidthMarginCenter = (int) frameHeightMargin / 2; // Same spacing between dice
int frameWidthMarginUpper = (int) (width - frameWidth) / 2; // Upper only
for (int i = 0; i < frames.length; i++) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
params.width = frameWidth;
params.height = frameWidth; // Square, so width equals height
if (i == 0) {
params.setMargins(frameWidthMarginUpper, frameHeightMargin, frameWidthMarginUpper, frameWidthMarginCenter); // Gravity is centered
} else if (i == 1) { // Left dice
params.setMargins(frameWidthMarginOuter, frameWidthMarginCenter, frameWidthMarginCenter, frameHeightMargin);
} else if (i == 2) {// Right dice
params.setMargins(frameWidthMarginCenter, frameWidthMarginCenter, frameWidthMarginOuter, frameHeightMargin);
}
frames[i].setLayoutParams(params);
}

Android: Rotate whole layout

I need to be able to rotate whole layouts on the fly (on the click of a button).
I am able to rotate the layouts using, eg. layout.setRotation(270.0f). The problem is, after the rotation, the layout height and width are not matching its parent's.
I have tried inverting height and width like so,
RelativeLayout layout = (RelativeLayout)findViewById(R.id.rootLayout);
LayoutParams layoutParams = layout.getLayoutParams();
int height = layout.getHeight();
int width = layout.getWidth();
layoutParams.height = width;
layoutParams.width = height;
Which does nothing at all.
I am working with sdk 14.
The first image below is the app as it starts. The second one, after a rotation. I wish to fill the black "space". Any help would be appreciated.
The images below show only a button in the layout. However, in reality, the layout are a lot more complex. What I am trying to achieve is "faking" a landscape view.
Edit: Changed images and added descriptions.
Not sure why this is useful, but it's a nice puzzle. Here is something that works for me:
On rotate click, do this:
RelativeLayout mainLayout = (RelativeLayout) findViewById(R.id.main);
int w = mainLayout.getWidth();
int h = mainLayout.getHeight();
mainLayout.setRotation(270.0f);
mainLayout.setTranslationX((w - h) / 2);
mainLayout.setTranslationY((h - w) / 2);
ViewGroup.LayoutParams lp = (ViewGroup.LayoutParams) mainLayout.getLayoutParams();
lp.height = w;
lp.width = h;
mainLayout.requestLayout();
And the layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="#+id/main"
android:layout_height="match_parent"
android:background="#ffcc88"
tools:context=".TestRotateActivity" >
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Test"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
/>
<Button
android:id="#+id/rotate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Rotate"
android:layout_centerInParent="true"
/>
</RelativeLayout>
Try this code:
btnRotate.setOnClickListener(new OnClickListener()
{
#SuppressLint("NewApi")
#Override
public void onClick(View v)
{
int orientation = getResources().getConfiguration().orientation;
switch(orientation)
{
case Configuration.ORIENTATION_LANDSCAPE:
llparent.setRotation(270.0f);
RelativeLayout.LayoutParams layoutParams_LandsScape =
new RelativeLayout.LayoutParams(
rlRoot.getHeight(), rlRoot.getWidth());
layoutParams_LandsScape.setMargins(
rlRoot.getTop(), rlRoot.getRight(),
rlRoot.getBottom(), rlRoot.getLeft());
layoutParams_LandsScape.addRule(RelativeLayout.CENTER_IN_PARENT);
llparent.setLayoutParams(layoutParams_LandsScape);
break;
case Configuration.ORIENTATION_PORTRAIT:
llparent.setRotation(270.0f);
RelativeLayout.LayoutParams layoutParams_Portrait =
new RelativeLayout.LayoutParams(
rlRoot.getHeight(), rlRoot.getWidth());
layoutParams_Portrait.setMargins(
0, 0, rlRoot.getBottom(), rlRoot.getLeft());
layoutParams_Portrait.addRule(RelativeLayout.CENTER_IN_PARENT);
llparent.setLayoutParams(layoutParams_Portrait);
break;
}
}
});
}
And XML:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".RotateAnim">
<RelativeLayout
android:id="#+id/rlroot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#090">
<LinearLayout
android:id="#+id/llParent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#900"
android:gravity="center"
android:orientation="vertical">
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="Button"/>
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
Simple and tricky way to make screen orientation along the button click.. with an example..
Here,I'm using the sharedPreference(Im setting an boolean value based on orientation )
Method for button onClick.
public void rotate(View v) {
edt = prefs.edit();
if (!prefs.getBoolean("screen_protrait", true)) {
edt.putBoolean("screen_protrait", true);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else {
edt.putBoolean("screen_protrait", false);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
edt.commit();
}
In xml, set an onClick method for rotate button
<Button
android:id="#+id/bt_rotate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:onClick="rotate"
android:text="Rotate" />
Last one, is in onCreate of Activity you want to set the Prefernce from Application..as
prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
Keep coding.. You can achieve your goal...Let me know,if it's working with your scenario.
If you want to literally rotate the screen, you can force a screen orientation.
Otherwise there's no easy way to do what you are trying to do as View.setRotation(float) will always render the View in its "real" bounds and then rotate it! What I suggest is careful consideration of what elements of the layout should be rotated and then to rotate those specifically.
The only true "automatic" way of achieving it would be to create a custom layout that essentially measures, layouts and draws children rotated... Honestly I would only go there if I really, really needed to and it's probably more trouble than it's worth!
// get root layout from activity's XML
LinearLayout mParentLayout = (LinearLayout) findViewById(R.id.activity_main);
// get screen size from DisplayMetrics if you need to rotate before the screen is shown
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
int width = displayMetrics.widthPixels;
// if you are rotating after the view was shown
// acquire width and height from the layout's parent's LayoutParams
// calculate offset to move the view into correct position
int offset = (width - height) / 2;
// rotate the layout
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(height, width);
mParentLayout.setLayoutParams(lp);
// 90° clockwise
mParentLayout.setRotation(90.0f);
mParentLayout.setTranslationX(offset);
mParentLayout.setTranslationY(-offset);
It may look like the suggested answer, but this displays how to get dimensions from DisplayMetrics and the calculating the offset for the translation is a little different because that's the only way it worked properly.
i'll suggest you rotate only button rather than rotating the whole layout like
btn_rotate.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v)
{
rotation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.rotate);
rotation.setFillAfter(true);
btn_rotate.startAnimation(rotation);
}
});
rotate.xml
<set>
<rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="0"
android:fromDegrees="270"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="0"
android:toDegrees="270" />
</set>
try set your layout params to match_parent after rotation:
layout.setRotation(270.0f)
and then
RelativeLayout layout = (RelativeLayout) findViewById(R.id.rootLayout);
layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
EDIT: get the parentView View parent = layout.getParent(); and set the width and height of the parent view to your layout as you need - in width to height and vice versa.
Android: alternate layout xml for landscape mode
As I can remember, you should define a new layout for the horizontal view. I think this link can help you
Try this code:
(RelativeLayoutOuterFrame) it is the name of your layout.which you want to rotate.
Actually, we are not rotate the layout.we just change height an width value.
int w = RelativeLayoutOuterFrame.getWidth();
int h = RelativeLayoutOuterFrame.getHeight();
ViewGroup.LayoutParams lp = (ViewGroup.LayoutParams) RelativeLayoutOuterFrame.getLayoutParams();
lp.height = w;
lp.width = h;
RelativeLayoutOuterFrame.setGravity(RelativeLayout.CENTER_IN_PARENT);
RelativeLayoutOuterFrame.setGravity(RelativeLayout.CENTER_HORIZONTAL);
RelativeLayoutOuterFrame.requestLayout();

Android: onTouchListener not working properly

I'm currently working on an app that has a RelativeLayout that has 4 child FrameLayouts, each FrameLayout has a set of ImageViews inside it and the inside View has it's own behavior. I'm trying to implement a drag and drop to theFrameLayouts while implementing the onTouchListener I found in this tutorial. but unfortunately the drag 'n drop doesn't work properly and nothing is happening.
any thoughts on how to implement the drag and drop correctly? what am I missing?
here is the xml code for a single child of the FrameLayouts:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="80dp"
android:layout_height="108dp" >
<ImageView
android:id="#+id/secondImage"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="#drawable/back" />
<ImageView
android:id="#+id/firstImage"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="#drawable/c2" />
</FrameLayout>
here is the xml code for the main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#drawable/pinecropped"
android:orientation="vertical" >
<FrameLayout
android:id="#+id/cardNumber1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="50dp"
android:layout_marginTop="60dp" >
<include layout="#layout/card" />
</FrameLayout>
<FrameLayout
android:id="#+id/cardNumber2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/cardNumber1"
android:layout_marginLeft="50dp"
android:layout_marginTop="100dp" >
<include layout="#layout/card" />
</FrameLayout>
<FrameLayout
android:id="#+id/cardNumber3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="50dp"
android:layout_marginTop="60dp" >
<include layout="#layout/card" />
</FrameLayout>
<FrameLayout
android:id="#+id/cardNumber4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="#+id/cardNumber3"
android:layout_marginRight="50dp"
android:layout_marginTop="100dp" >
<include layout="#layout/card" />
</FrameLayout>
</RelativeLayout>
here is the Java code for the single child:
public class Card implements OnClickListener {
private int _resId;
private Context _context;
private ImageView firstImage, secondImage;
private boolean isFirst;
public Card(Context context, FrameLayout parent) {
_context = context;
firstImage = (ImageView) parent.findViewById(R.id.firstImage);
secondImage = (ImageView) parent.findViewById(R.id.secondImage);
}
public void setupCards(int resId, boolean hasBackSide) {
_resId = resId;
Bitmap temp = BitmapFactory.decodeResource(_context.getResources(),
_resId);
firstImage.setImageBitmap(temp);
if (hasBackSide) {
temp = BitmapFactory.decodeResource(_context.getResources(),
R.drawable.back);
}
secondImage.setImageBitmap(temp);
isFirst = true;
secondImage.setVisibility(View.GONE);
}
// MORE IMPLEMENTATION
}
this is my main activity Java code:
public class FaceUpActivity extends Activity {
FrameLayout firstCard, secondCard, thirdCard, forthCard;
Card cardNumber1, cardNumber2, cardNumber3, cardNumber4;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initUI();
}
private void initUI() {
firstCard = (FrameLayout) findViewById(R.id.cardNumber1);
secondCard = (FrameLayout) findViewById(R.id.cardNumber2);
thirdCard = (FrameLayout) findViewById(R.id.cardNumber3);
forthCard = (FrameLayout) findViewById(R.id.cardNumber4);
cardNumber1 = new Card(this, firstCard);
cardNumber2 = new Card(this, secondCard);
cardNumber3 = new Card(this, thirdCard);
cardNumber4 = new Card(this, forthCard);
cardNumber1.setupCards(R.drawable.c2, true);
cardNumber2.setupCards(R.drawable.d0, true);
cardNumber3.setupCards(R.drawable.h5, true);
cardNumber4.setupCards(R.drawable.sj, true);
firstCard.setOnTouchListener(dragMe);
secondCard.setOnTouchListener(dragMe);
thirdCard.setOnTouchListener(dragMe);
forthCard.setOnTouchListener(dragMe);
}
OnTouchListener dragMe = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
FrameLayout.LayoutParams params = (LayoutParams) v
.getLayoutParams();
int maxWidth = getWindowManager().getDefaultDisplay().getWidth();
int maxHeight = getWindowManager().getDefaultDisplay().getHeight();
int topMargin, leftMargin;
int cond = v.getId();
if (cond == R.id.cardNumber1 || cond == R.id.cardNumber2
|| cond == R.id.cardNumber3 || cond == R.id.cardNumber4) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
topMargin = (int) event.getRawY() - (v.getHeight());
leftMargin = (int) event.getRawX() - (v.getWidth()) / 2;
if (topMargin < 0) {
params.topMargin = 0;
} else if (topMargin > maxHeight) {
params.topMargin = maxHeight - v.getHeight();
} else {
params.topMargin = topMargin;
}
if (leftMargin < 0) {
params.leftMargin = 0;
} else if (leftMargin > maxWidth) {
params.leftMargin = maxWidth - (v.getWidth() / 2);
} else {
params.leftMargin = leftMargin;
}
v.setLayoutParams(params);
break;
case MotionEvent.ACTION_MOVE:
topMargin = (int) event.getRawY() - (v.getHeight());
leftMargin = (int) event.getRawX() - (v.getWidth()) / 2;
if (topMargin < 0) {
params.topMargin = 0;
} else if (topMargin > maxHeight) {
params.topMargin = maxHeight - v.getHeight();
} else {
params.topMargin = topMargin;
}
if (leftMargin < 0) {
params.leftMargin = 0;
} else if (leftMargin > maxWidth) {
params.leftMargin = maxWidth - (v.getWidth() / 2);
} else {
params.leftMargin = leftMargin;
}
v.setLayoutParams(params);
break;
}
}
return true;
}
};
}
Why your D&D is not working:
D&D is not working because your Card (that useses the internal FrameLayout) is implementing the onClick event that "eat" the d&d event.
Solutions are two:
you can have 2 different areas, one for the click event (the biggest part of the card), one for the d&d event (the bottom-left edge of the card). You can see a live example in the Android Music player that implements this solution.
Otherwise you can handle the two events at the same time:
click == down+up in the same time (under 100ms) in the same x/y location
dd == down+up in different times ( > 50/100ms)
I think there are a couple of potential problems:
Are you sure you're getting the correct IDs in your onTouch()? (since the touch listener is only used with your card views, you propably can remove the if checking for the IDs)
You are using a FrameLayout - so use the x and y position instead of margins edit: my bad, you're using RelativeLayout, so the margins are kind of right. Though, I'd use a FrameLayout instead
the API says you need to return true in onTouch() if you consumed the event, false otherwise (you're returning true when you didn't move the cards and nothing when you did)

Categories

Resources