My activity has a LinearLayout with a single child view. I want both to fill the screen, minus a 12 dp margin.
Unfortunately, the child view is drawn 12 dp too big and gets cut off. Apparently match_parent ignores the layout_margin attribute when calculating the size of the child view. What is the simplest way to fix this?
myActivity.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_margin="12dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.myapp.myView
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
myActivity.java
package com.myapp;
import android.app.Activity;
import android.os.Bundle;
public class myActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myActivity);
}
}
myView.java
package com.myapp;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class myView extends View {
private Paint paint = new Paint();
public myView(Context context, AttributeSet attrs) {
super(context, attrs);
paint.setColor(0xFFFF0000); //red
paint.setStyle(Paint.Style.STROKE); // for unfilled rectangles
paint.setStrokeWidth(4);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int size = canvas.getWidth(); // width = height (see onMeasure())
canvas.drawRect(0, 0, size, size, paint);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
// This gives us a square canvas!
}
}
Child views can have a margin around them, parent views (or view groups like layouts) can have a padding between their boundaries and their child views. In other words, margin is outside a view, padding is inside.
Also, see this excellent explanation: Difference between a View's Padding and Margin
Example with standard View and padding instead of margin:
I created a little example with a standard view instead of your custom one, and using padding for the LinearLayout as suggested above and it works perfectly (see screenshot):
<?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:padding="12dp"
android:orientation="vertical" >
<View
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#ff0000"/>
</LinearLayout>
Solution
Turns out, the problem was your using canvas.getWidth() in your custom view's onDraw method. Using the view's getWidth() instead, solved the problem. Finally :-)
Related
I am developing some kind of a scrollable horizontal chart. I have a RecyclerView.Adapter with view holder of two views:
1. View - will just represent a bar with defined height.
2. TextView - on the top of (1)View with angle -45 it should represent some data to which this column belongs.
Width of bar should be equal for each column, but text has different length.
But boundaries of parent (no metter is it Frame or Relative layouts are cutting off the rest of the text.
P.S. clipChildren and clipToPadding didn't help.
My .xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="#dimen/chart_column_width"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false">
<View
android:id="#+id/cc_progress"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:layout_alignParentBottom="true"
android:background="#color/colorPrimaryDark"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextView
android:id="#+id/cc_tagname"
android:layout_above="#id/cc_progress"
android:elevation="2dp"
android:textSize="20sp"
android:padding="2dp"
android:textAlignment="textStart"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
This is happening because you are just rotating the TextView. So, the TextView retaining its original height and width.
Try using this VerticalTextView :
`public class VerticalTextView extends TextView{
final boolean topDown;
public VerticalTextView(Context context, AttributeSet attrs){
super(context, attrs);
final int gravity = getGravity();
if(Gravity.isVertical(gravity) && (gravity&Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) {
setGravity((gravity&Gravity.HORIZONTAL_GRAVITY_MASK) | Gravity.TOP);
topDown = false;
}else
topDown = true;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
#Override
protected void onDraw(Canvas canvas){
TextPaint textPaint = getPaint();
textPaint.setColor(getCurrentTextColor());
textPaint.drawableState = getDrawableState();
canvas.save();
if(topDown){
canvas.translate(getWidth(), 0);
canvas.rotate(90);
}else {
canvas.translate(0, getHeight());
canvas.rotate(-90);
}
canvas.translate(getCompoundPaddingLeft(), getExtendedPaddingTop());
getLayout().draw(canvas);
canvas.restore();
}
}`
All you have to set the gravity of the text. By default, text is from top to bottom. If you set android:gravity="bottom", then it's drawn from bottom to top.
Hope it helps you.
Its little tough task but it is possible. I'm explaining idea how you can make it possible: –
You have make recyclerview wrap_content and keep it inside the relativelayout and the scrollview.
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:fitsSystemWindows="true">
<RelativeLayout
android:id="#+id/rlRoot"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</RelativeLayout>
</ScrollView>
Now calculate the width of an item of the recylerview and add the textview in rlRoot at position (width * itemPosition). It places the textview at exact X position. Now to calculate Y coordinate, you can get height of the items as you are setting the bar height and Y position of the textView should be(rlRoot.getHeight()- itemHeight). And then give the whatever angle you want, it will never ellipsis. Repeat the loop for all the items.
I never tried it, but i guess, it will work with your efforts.
Override OnMeasure of Textview and increase the height on super.OnMeasure method
I want a Button like this:
+-----------------------+
| |
| +-----+ |
| |Image| |
| +-----+ |
| Text |
| |
| |
+-----------------------+
EDIT: Explanation to the picture: I want the COMBINATION of Image and text centered (text ALWAYS below the image)
I want the Button to stretch to a parent object (to make the whole area the button click area) and still align imgage AND text at center.
I achieve only top center alignment with folowing code, but I don't get the desired behaviour...
<Button
android:id="#+id/btInfo"
android:paddingTop="20dp"
android:layout_width="0dp"
android:layout_height="match_parent"
android:gravity="top|center_horizontal"
android:layout_weight="1"
android:background="#drawable/border_button_main_menu"
android:drawableTop="#drawable/bt_info"
android:onClick="onClick"
android:text="#string/info"
android:textColor="#drawable/bt_white_red_text"
android:textSize="15dp" />
changing android:gravity="top|center_horizontal" to android:gravity="center_vertical|center_horizontal" only leads to image centered at top and text centered at bottom...
---- EDIT2 -----
Wanted behaviour:
1) look as described (Image and text is a optical group and the group is centered in the button)
2) text should be part of the button (I want the onclick behaviour to work with selectors)
---- EDIT3 -----
added my own solution... but thanks to all that tried to help
Use the Following Code, your problem will be solve.
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignBottom="#+id/foreground"
android:layout_alignLeft="#+id/foreground"
android:layout_alignRight="#+id/foreground"
android:layout_alignTop="#+id/foreground"
android:background="#android:drawable/dialog_frame"
android:onClick="clickedMe" />
<RelativeLayout
android:id="#+id/foreground"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true" >
<TextView
android:id="#+id/button_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="112dp"
android:text="#string/hello_world" />
<ImageView
android:id="#+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="#+id/button_text"
android:layout_centerHorizontal="true"
android:paddingBottom="10dip"
android:paddingTop="10dip"
android:src="#drawable/ic_launcher" />
</RelativeLayout>
</RelativeLayout>
------------- EDIT --------------------
oNclick Method:
final TextView text = (TextView) findViewById(R.id.button_text);
RelativeLayout foreground = (RelativeLayout) findViewById(R.id.foreground);
foreground.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "clicked");
Toast.makeText(getApplicationContext(), "Clicked...!!!",
Toast.LENGTH_LONG).show();
text.setTextColor(Color.RED);
}
});
Try this:
<LinearLayout
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="#+id/btInfo"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#drawable/border_button_main_menu"
android:drawableTop="#drawable/bt_info"
android:onClick="onClick"
android:text="#string/info"
android:textColor="#drawable/bt_white_red_text"
android:textSize="15dp" />
</LinearLayout>
Call the setBackgroundDrawable() the text you will then add to the button will appear below the drawable!
I think the best way to achieve this kind of UI is using ImageButton instead of Button. But you can still achieve this by some hackish ways. One of them is here:
How to have Image and Text Center within a Button
You'll just have to mention inner RelativeLayout's orientation as "vertical" in the solution given in above link.
I hope it helps.
May be this can help you too:
How to center icon and text in a android button with width set to "fill parent"
The output you are trying to achieve can not be done in that way using drawableTop.
reason why? - View can set background or drawable and while setting drawable Android gives only 4 options to set bounds of the drawable either top,left,right or bottom and nothing for center.
Now in your XML you are having view's height and width as MATCH_PARENT so every time the drawable set using drawableTOP or LEFT etc. it will go to that edge of the view. which is happening right now with you.
From Comments :
Android OS is ultimately a Software.. and a software has always been developed within it's scope. The thing you are asking is out of scope so it's directly not supported by android using any default Form widget..
I demonstrated that how the android have written the Drawable class and how you can use it so please try to understand the answer and not only the Solution to your problem.. by the way to get click of whole area you can write click on that LinearLayout instead of the button.
Solution will be :
<LinearLayout height = MATCH_PARENT and width = 0dp>
<Button height and width = WRAP_CONTENT with drawableTop=image and gravity = center>
</LinearLayout>
My solution is now deriving from a button... that fullfills all my requirements... I don't know, if the measuring of the text is really exact that way, but it looks to be so... if not, everyone get's the idea behind and can adjust that...
Thanks for the help, though
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.util.AttributeSet;
import android.widget.Button;
public class CenterImageTextButton extends Button {
private Paint mPaint = new Paint();
private String mText = null;
private float mTextSize = 0;
public CenterImageTextButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CenterImageTextButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CenterImageTextButton(Context context) {
super(context);
}
#Override
public void onDraw(Canvas canvas) {
mText = getText().toString();
mTextSize = getTextSize();
mPaint.setStyle(Style.FILL);
mPaint.setColor(getCurrentTextColor());
// get image top
Drawable drawable = getCompoundDrawables()[1];
Drawable curDrawable = null;
if (drawable instanceof StateListDrawable)
curDrawable = ((StateListDrawable)drawable).getCurrent();
else
curDrawable = ((BitmapDrawable)drawable).getCurrent();
Bitmap image = ((BitmapDrawable)curDrawable).getBitmap();
// call default drawing method without image/text
setText("");
setCompoundDrawables(null, null, null, null);
super.onDraw(canvas);
setText(mText);
setCompoundDrawables(null, drawable, null, null);
// get measurements of button and Image
int width = getMeasuredWidth();
int height = getMeasuredHeight();
int imgWidth = image.getWidth();
int imgHeight = image.getHeight();
// get measurements of text
//float densityMultiplier = getContext().getResources().getDisplayMetrics().density;
//float scaledPx = textSize * densityMultiplier;
//paint.setTextSize(scaledPx);
mPaint.setTextSize(mTextSize);
float textWidth = mPaint.measureText(mText);
// draw Image and text
float groupHeight = imgHeight + mTextSize;
canvas.drawBitmap(image, (width - imgWidth) / 2, (height - groupHeight) / 2, null);
canvas.drawText(mText, (width - textWidth) / 2, mTextSize + (height - groupHeight) / 2 + imgHeight, mPaint);
}
}
I'm trying to create a 2D game engine for an Android app. I've followed this tutorial, which works fine for creating a full screen display, but I don't want that. I want to make my view take the top 2/3 (or whatever) of the screen, and fill the bottom third with standard Android widgets (buttons, text entry, etc.). I cannot get this to work. The best I can get is a blank white screen. I've tried many permutations, including using an outer LinerLayout, then embedding the custom SurfaceView inside a nested RelativeLayout, and putting the Android widgets in a nested LinearLayout, and it doesn't work.
For instance, this produces a white screen, when I feel like it should be 50% SurfaceView, 50% for a TextView:
activity_main.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"
android:baselineAligned="false" >
<RelativeLayout
android:layout_width="0dip"
android:layout_height="0dp"
android:layout_weight="1"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mapContainer"
>
</RelativeLayout>
<LinearLayout
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="0dp"
xmlns:android="http://schemas.android.com/apk/res/android" >
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#color/white"
android:text="#string/test_str" />
</LinearLayout>
</LinearLayout>
MainActivity.java:
package com.removed.for.privacy;
import com.removed.for.privacy;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout mapContainer = (RelativeLayout) findViewById(R.id.mapContainer);
JSMapView mapView = new JSMapView(this);
mapContainer.addView(mapView);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
Any ideas?
Figured it out through lots of googling and semi-related questions. Here's an example of how I got it to fill 2/3 the height in portrait mode (100% width), and 2/3 width (100% height) in landscape.
In the custom surface view, override the onLayout method:
#Override
public void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (changed) {
(this).layout(0, 0, viewWidth, viewHeight);
}
}
In the class constructor, add this code:
public YourCustomSurfaceView(Context context) {
super(context);
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int rotation = display.getRotation();
// If vertical, we fill 2/3 the height and all the width. If horizontal,
// fill the entire height and 2/3 the width
if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
screenWidth = display.getWidth();
screenHeight = display.getHeight();
viewHeight = 2 * (screenHeight / 3);
viewWidth = screenWidth;
} else {
screenWidth = display.getWidth();
screenHeight = display.getHeight();
viewWidth = 2 * (screenWidth / 3);
viewHeight = screenHeight;
}
// Enter rest of code here
}
To have Custom SurfaceView size
surfaceView!!.layoutParams = ConstraintLayout.LayoutParams(width, height)
ConstraintLayout Depend upon your Parent Layout
Now, that the constraintLayout is out. One can do this by setting "layout_constraintHeight_percent" to "0.5". The surfaceView would look like below:
<SurfaceView
android:id="#+id/surface_camera"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHeight_percent="0.5"
/>
To change SurfaceView's custom height and width use "android.view.ViewGroup.LayoutParams"
There are subclasses of LayoutParams for different subclasses of ViewGroup. For example, AbsoluteLayout has its own subclass of LayoutParams which adds an X and Y value. By this you can change X and Y of SurfaceView
Code of change surfaceview height width is here:
http://agalaxycode.blogspot.in/2014/12/change-surfaceview-height-width.html
I have one image view (assigned one bitmap to it) and another bitmap (Transparent) for painting. How it is possible to scroll this bitmap and background bitmap at the same time to be painted at different places.
From what I understood you are trying to draw on top of a image. If thats the case, create a custom view and get the image using BitmapFactory. After you get a bitmap object, use its copy method and use that copy for the canvas of the view.
Now you can override the onDraw method of a custom view and draw anything on top of it.
This view can be added to the layout, and scrolling will happen to the view.
Edit: Sample Code
Ok this is some code that might help you. I dont have the time to go through all your code.
But I tried to understand your requirement.
I am showing the code for an activity, its xml and custom view
Activity.java
public class DrawDemo extends Activity {
private FrameLayout container;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.draw_demo_layout);
container = (FrameLayout)findViewById(R.id.sc);
container.addView(new DrawView(this));
}
public void drawHandler(View target){
container.addView(new DrawView(this));
}
public void clearHandler(View target){
if(container.getChildCount() != 1){
container.removeViewAt(container.getChildCount()-1);
}
}
}
draw_demo_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical">
<FrameLayout android:orientation="vertical"
android:layout_width="fill_parent" android:layout_height="0dip"
android:id="#+id/sc" android:scrollbars="horizontal"
android:layout_weight="1.0">
<ImageView android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="#drawable/chips"/>
</FrameLayout>
<Button android:layout_width="fill_parent"
android:layout_height="wrap_content" android:text="Draw"
android:onClick="drawHandler"/>
<Button android:layout_width="fill_parent"
android:layout_height="wrap_content" android:text="Clear"
android:onClick="clearHandler"/>
</LinearLayout>
DrawView.java
public class DrawView extends View {
public DrawView(Context context) {
super(context);
setLayoutParams(new LayoutParams(LinearLayout.LayoutParams.FILL_PARENT
,LinearLayout.LayoutParams.FILL_PARENT));
}
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint p = new Paint();
p.setColor(Color.RED);
p.setStrokeWidth(5);
canvas.drawLine(50, 50, 100, 150, p);
canvas.drawLine(100, 150, 20, 50, p);
}
}
This activity has a framelayout which has a imageview as the base which holds the bitmap.
We add a custom draw view on top of it and do our drawings on it.When we want to clear the drawing we remove the drawview and add another one if we need to draw again.
This might not be the most efficient way. But should get you started.
I have a custom view
.........
package com.yasir.canvasTest;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.util.Log;
import android.view.View;
public class GraphView extends View {
private static final String TAG = "GraphView";
public static boolean BAR = true;
public static boolean LINE = false;
private Paint paint;
private float[] values;
private String[] horlabels;
private String[] verlabels;
private String title;
private boolean type;
public Canvas tmpCanvas = null;
public GraphView(Context context, float[] values, String title, String[] horlabels, String[] verlabels) {
super(context);
//setScrollContainer(true);
Log.v(TAG,"On GraphView==>");
paint = new Paint();
setWillNotDraw(false);
requestLayout();
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
Log.v(TAG,"<==On Draw");
/// drawing code here
invalidate();
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.v(TAG,"On onMeasure==>");
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(
parentWidth * 2, parentHeight);
}
}
...............
Activity: onCreate
...............
setContentView(R.layout.horscroll);
HorizontalScrollView hsView = (HorizontalScrollView) findViewById(R.id.hzsv);
LinearLayout ll = (LinearLayout) findViewById(R.id.linearParent);
ll.removeAllViews();
// create custom view and add to laywout
GraphView graphView = new GraphView(this, values, "GraphViewDemo",horlabels, verlabels, GraphView.LINE);
hsView.requestLayout();
hsView.setWillNotDraw(false);
ll.requestLayout();
ll.setWillNotDraw(false);
ll.addView(graphView);
.....................
Xml
....................
<?xml version="1.0" encoding="utf-8"?>
<HorizontalScrollView
android:id="#+id/hzsv"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout android:id="#+id/linearParent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"></LinearLayout>
</HorizontalScrollView>
..................
Regards,
Yasir Perwez
Try checking with Hierarchy Viewer what's going on. I suspect your view is not getting drawn because it has a 0 width, and this could be for two reasons (either or both):
you're not setting any LinearLayout.LayoutParams on your custom view before adding it to the layout, and I'm not sure if the default ones are of the "fill parent" kind;
your onMeasure() is incomplete, it should take into account also the mode (UNSPECIFIED, EXACTLY, AT_MOST) wich will depend from the layout params too. There's no guarantee that getSize() returns parent's size, and as the framework is meant, you shouldn't even know if that happens.
(#slund's suggestion will probably be useful too)
Your GraphView is getting measured with a width of 0 (zero). You are then requesting that it be set to 2*width, which is still zero. The draw logic will optimize not asking a zero width view to draw.
Probably what you want is to ask the HorizontalScrollView to fill the full width of the view with android:fillViewport="true", ie:
<?xml version="1.0" encoding="utf-8"?>
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/hzsv"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true">
<LinearLayout
android:id="#+id/linearParent"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</LinearLayout>
</HorizontalScrollView>
ALso see #bigstones answer. You really need to handle all MeasureSpec types (UNSPECIFIED, AT_MOST, EXACTLY). Setting fillViewport="true" gets your onMeasure called with type EXACTLY and the width of the viewport.