I'm trying to center text in a canvas in a TextView subclass. It looks fine in Eclipse, but not in the device:
Eclipse:
Device:
There's a reason I want to center the text in this way. Which I'm not explaining here (and is also not in the code), since it doesn't belong to the problem.
I'm already using dips and sps.
The code:
package com.example.test2;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.TextView;
public class CenterTest extends TextView {
private Paint paint;
private Paint paint2;
private Rect bounds;
public CenterTest(Context context) {
super(context);
}
public CenterTest(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
paint2 = new Paint();
bounds = new Rect();
}
public void draw(Canvas canvas) {
canvas.save();
int width = getWidth();
int height = getHeight();
//draw background
paint2.setColor(Color.rgb(0, 0, 0));
paint.setStyle(Paint.Style.FILL);
canvas.drawRect(0, 0, width, height, paint2);
//measure text
paint.setTextSize(getTextSize());
paint.getTextBounds(getText().toString(), 0, getText().toString().length(), bounds);
int textWidth = bounds.width();
int textHeight = bounds.height();
//center before drawing text
canvas.translate((width / 2) - (textWidth / 2), 0);
canvas.translate(0, (height / 2) - (textHeight / 2));
//let TextView.onDraw() do the rest
super.onDraw(canvas);
canvas.restore();
}
}
XML:
<RelativeLayout 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"
android:background="#ffffff"
>
<com.example.test2.CenterTest
android:layout_width="100dip"
android:layout_height="30dip"
android:text="label"
android:textColor="#ffffff"
android:singleLine="true"
android:textSize="12sp"
/>
</RelativeLayout>
Maybe the reason is somewhere in TextView.onDraw() but that's more than 200 lines of code and I don't have time now... and not even sure if it's there.
Thanks in advance!
try to use android:gravity="center"
<com.example.test2.CenterTest
android:layout_width="100dip"
android:layout_height="30dip"
android:text="label"
android:textColor="#ffffff"
android:singleLine="true"
android:textSize="12sp"
android:gravity="center"
/>
Related
I'm not quite sure how to ask this but I need a scrollview that will scroll with an amplitude visualizer as it records audio. EDIT: To be clear, the ScrollView is not scrolling even with touches and gestures.
Here's a screenshot of the app with the custom view. The red bars show the average amplitude of a second.
The bars fill from left to right, but when it reaches the edge it does not scroll.
Here's the XML for the HorizontalScrollView and the AudioVisualizer view:
<HorizontalScrollView
android:id="#+id/hsv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<com.blueteam.audiotes.AudioVisualizer
android:id="#+id/visualizer"
android:layout_width="match_parent"
android:layout_height="200dp" />
</HorizontalScrollView>
Here's the code for the AudioVisualizer element:
package com.blueteam.audiotes;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import java.util.ArrayList;
/**
* Created by tanner on 3/21/17.
*/
public class AudioVisualizer extends View {
private final int BAR_WIDTH = 10;
private final int BAR_STARTING_POINT = 0;
private final int BAR_SPACE = 2;
private final int BAR_BOTTOM = 200;
private final int BAR_MINIMUM_HEIGHT = 20;
private ArrayList<Rect> bars;
private Paint paint;
private ArrayList<Tag> tags;
public AudioVisualizer(Context context, AttributeSet attrs) {
super(context, attrs);
bars = new ArrayList<Rect>();
paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
}
#Override
protected void onDraw(Canvas canvas) {
for(Rect r : bars)
canvas.drawRect(r.left, r.top, r.right, r.bottom, paint);
}
public void addBar(int height) {
if (height < BAR_MINIMUM_HEIGHT)
height = BAR_MINIMUM_HEIGHT;
else if (height > BAR_BOTTOM)
height = BAR_BOTTOM;
bars.add(new Rect(BAR_STARTING_POINT + bars.size() * (BAR_SPACE + BAR_WIDTH),
BAR_BOTTOM - height,
(BAR_STARTING_POINT + bars.size() * (BAR_SPACE + BAR_WIDTH)) + BAR_WIDTH,
BAR_BOTTOM));
}
}
I tried setting the AudioVisualizer width to something like 2000 just to see if it would scroll when the child was clearly larger than the ScrollView, but it still didn't work.
I'm guessing I'm missing something simple.
Change your xml like this:
<HorizontalScrollView
android:id="#+id/hsv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<com.blueteam.audiotes.AudioVisualizer
android:id="#+id/visualizer"
android:layout_width="wrap_content"
android:layout_height="200dp" />
</HorizontalScrollView>
And whenever you addBar do this:
audioVisualizer.addBar(10);
parentHsv.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
I solved it. I didn't override onMeasure. Here's my code:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = (BAR_WIDTH + BAR_SPACE) * bars.size();
setMeasuredDimension(width, MeasureSpec.getSize(heightMeasureSpec));
}
I also needed to call v.requestLayout() before v.invalidate() to update the size.
In my Android project, I encountered some difficulties in the drawing code, so I made up a MCVE below to demonstrate the problem I have.
By running this example, and perform a touch in the screen, you can see two circles coming up.
You can see that the red one is positioned accurately and consistently around the point of touch.
For the green one, you can see that its position is somehow random on repeat touching of the screen.
The red circle is created using an ImageView with the source as ball.xml. For the green one, it is done by Canvas.drawCircle().
So:
How do I correct the code to make the red circle and green circle
appear at exactly the same spot on a touch, regardless of the resolution and dpi of the screen?
Also, btw, the 500X1000 dimension (Bitmap.createBitmap(500, 1000))is just set arbitrarily, it should be according to the screen size of the device.
MainActivity
package com.prime.testdraw;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
public class MainActivity extends AppCompatActivity {
ImageView ivPhoto;
FrameLayout fl;
ImageView ivBall;
private Canvas myCV;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ivPhoto = ((ImageView) findViewById(R.id.ivPhoto));
fl = ((FrameLayout) findViewById(R.id.fl));
ivBall = ((ImageView) findViewById(R.id.ivBall));
myCV = initDraw(ivPhoto);
ivPhoto.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
myCV = initDraw(ivPhoto);
ivBall.setX(motionEvent.getX() - ivBall.getWidth()/2);
ivBall.setY(motionEvent.getY() - ivBall.getHeight()/2);
Paint p = new Paint();
p.setStrokeWidth(3);
p.setColor(Color.GREEN);
p.setTextSize(100);
int radius = 40;
p.setStyle(Paint.Style.STROKE);
myCV.drawCircle(motionEvent.getX() - radius, motionEvent.getY() - radius, radius, p);
ivPhoto.invalidate();
return false;
}
});
}
private Canvas initDraw(ImageView imageView) {
Bitmap bmp = Bitmap.createBitmap(500, 1000, Bitmap.Config.RGB_565);
Canvas cv = new Canvas(bmp);
imageView.setImageDrawable(new BitmapDrawable(getResources(), bmp));
imageView.invalidate();
return cv;
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:id="#+id/fl">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/ivPhoto"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<ImageView
android:layout_width="52dp"
android:layout_height="52dp"
android:id="#+id/ivBall"
android:layout_gravity="left|top"
android:src="#drawable/ball" />
</FrameLayout>
ball.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" >
<stroke
android:width="4dp"
android:color="#FF0000" />
<gradient android:startColor="#FFFF0000" android:endColor="#80FF0000"
android:angle="270"/>
</shape>
Canvas.drawCircle works with these parameters :
cx float: The x-coordinate of the center of the cirle to be drawn
cy float: The y-coordinate of the center of the cirle to be drawn
radius float: The radius of the cirle to be drawn
paint Paint: The paint used to draw the circle
Therefore instead of substracting the radius when calling it, simply call drawCircle directly :
myCV.drawCircle(motionEvent.getX(), motionEvent.getY(), radius, p);
Source : Android Canvas doc
i am trying to add 10 lines in an edittext by default when my app loads. I have defined a fixed height for my edittext.
It only shows one line by default no matter what i do, and more lines are added when i press Enter key. Below is my code. Properties minHeight and minLines does not work
<com.example.EditTextt
android:id="#+id/editText1"
android:layout_width="match_parent"
android:layout_height="600dp"
android:minLines="10"
android:background="#android:color/transparent"
android:fadingEdge="vertical"
android:gravity="top"
android:padding="10dp"
android:scrollbars="vertical"
android:textSize="22sp" >
<requestFocus />
</com.example.EditTextt>
See the image below. The lines appear when i press Enter key again and again. I want this much lines to appear by default like a notepad.
Here is my custom class for my EditText :-
EditTextt.Java
package com.example;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.EditText;
public class LineEditText extends EditText {
private Rect mRect;
private Paint mPaint;
public LineEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(getResources().getColor(R.color.Exzeoblue)); //SET YOUR OWN COLOR HERE
}
#Override
protected void onDraw(Canvas canvas) {
// Gets the number of lines of text in the View.
int count = getLineCount();
// Gets the global Rectangle and Paint objects
Rect r = mRect;
Paint paint = mPaint;
// Draws one line in the rectangle for every line of text in the EditText
for (int i = 0; i < count; i++) {
// Gets the baseline coordinates for the current line of text
int baseline = getLineBounds(i, r);
/*
* Draws a line in the background from the left of the rectangle to the
* right, at a vertical position one dip below the baseline, using the
* "paint" object for details.
*/
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
baseline += getLineHeight();//next line
}
// Finishes up by calling the parent method
super.onDraw(canvas);
}
}
Now this code draws only one line at a time. I am unable to draw 10 lines by default
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.EditText;
public class LineEditText extends EditText {
private Rect mRect;
private Paint mPaint;
int initialCount = 0;
public LineEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.BLUE);
initialCount = getMinLines();
setLines(initialCount);
}
#Override
protected void onDraw(Canvas canvas) {
// Gets the number of lines of text in the View.
int count = getBaseline();
// Gets the global Rectangle and Paint objects
Rect r = mRect;
Paint paint = mPaint;
// Gets the baseline coordinates for the current line of text
int baseline = getLineBounds(0, r);
// Draws one line in the rectangle for every line of text in the EditText
for (int i = 0; i < count; i++) {
/*
* Draws a line in the background from the left of the rectangle to the
* right, at a vertical position one dip below the baseline, using the
* "paint" object for details.
*/
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
baseline += getLineHeight();//next line
}
// Finishes up by calling the parent method
super.onDraw(canvas);
}
}
I used this code
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:orientation="vertical"
android:padding="10dp"
android:layout_height="match_parent"
>
<EditText
android:id="#+id/edit_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="1\n2\n3\n4\n5\n\6\n\7\n8\n9\n10"
android:minLines="10" >
</EditText>
</LinearLayout>
See the image below, The cursor is at the red arrow
package com.example.customedittext;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.EditText;
public class LineEditText extends EditText {
private Rect mRect;
private Paint mPaint;
int initialCount = 0;
public LineEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.BLUE);
initialCount = getMinLines();
setLines(initialCount);
}
#Override
protected void onDraw(Canvas canvas) {
// Gets the number of lines of text in the View.
int count = getBaseline();
// Gets the global Rectangle and Paint objects
Rect r = mRect;
Paint paint = mPaint;
// Gets the baseline coordinates for the current line of text
int baseline = getLineBounds(0, r);
// Draws one line in the rectangle for every line of text in the EditText
for (int i = 0; i < count; i++) {
/*
* Draws a line in the background from the left of the rectangle to the
* right, at a vertical position one dip below the baseline, using the
* "paint" object for details.
*/
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
baseline += getLineHeight();//next line
}
// Finishes up by calling the parent method
super.onDraw(canvas);
}
}
<ScrollView
android:layout_width="fill_parent"
android:layout_height="500dp"
android:background="#android:color/holo_blue_light">
<com.example.customedittext.LineEditText
android:id="#+id/editText1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:scrollbars="vertical"
android:background="#android:color/transparent"
android:ems="10"
android:gravity="top"
android:minLines="7" >
<requestFocus />
</com.example.customedittext.LineEditText>
</ScrollView>
I have to use a canvas class to add frame and multiple images on it. but my problem is that I have to use canvas view in my activity class. I want to use this canvas view as a widget so I can use it in my XML.
I tried this by using this code :
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.view.View;
public class TwoDee extends View {
private int mWidth;
private int mHeight;
public TwoDee(Context context) {
super(context);
}
public TwoDee(Context context, AttributeSet attribs) {
super(context, attribs);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.GRAY);
paint.setStyle(Style.FILL);
canvas.drawPaint(paint);
paint.setColor(Color.BLUE);
canvas.drawLine(0, 0, mWidth, mHeight, paint);
canvas.drawLine(mWidth, 0, 0, mHeight, paint);
paint.setColor(Color.RED);
canvas.drawText("0 kal", 50, 85, paint);
canvas.save();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mWidth = View.MeasureSpec.getSize(widthMeasureSpec);
mHeight = View.MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(mWidth, mHeight);
}
public void setImage(Drawable myImg) {
newImg = myImg;
invalidate();
}
}
and used it in my xml class like this :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello"
/>
<com.yourapphere.TwoDee
android:id="#+id/twoDee1"
android:layout_width="150dp"
android:layout_height="100dp"
/>
</LinearLayout>
and I am accessing it in my activity class like this :
TwoDee myCanvas;
myCanvas = (TwoDee)findViewById(R.id.twoDee1);
myCanvas.setImage(drawable);
my problem is that when I try to call setImage() function from my activity class it shows null pointer exception. What should I do to add image in canvas view using my Activity class.
I dont want to use setContentView because this will make only Canvas View to show in my activity.
LogCat Output :
FATAL EXCEPTION: main
java.lang.NullPointerException
at iTagz.android.Dialog_ImagePreview$1.onClick(Dialog_ImagePreview.java:84)
at android.view.View.performClick(View.java:2485)
at android.view.View$PerformClick.run(View.java:9081)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:130)
at android.app.ActivityThread.main(ActivityThread.java:3770)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:880)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:638)
at dalvik.system.NativeStart.main(Native Method)
Dialog_ImagePreview is my activity from where I'm calling SetImage method.
I was calling my view from other activity and that's why I was getting NullPointerException. But now i'm calling it from same activity and its working fine.
Carefully see your code
<com.yourapphere.TwoDee
android:layout_width="150dp"
android:layout_height="100dp"
/>
You have not written id attribute.It should be
<com.yourapphere.TwoDee
android:id=#+id/twoDee1"
android:layout_width="150dp"
android:layout_height="100dp"
/>
This is wrong.
I dont want to use setContentView because this will make only Canvas View to show in my activity.
Until and unless you don't specify setContentView how android will know from which layout file control should be fetch.
E.g
TwoDee myCanvas = (TwoDee) findViewById(R.id.twoDee1);
findViewById will only work where you specify layout file inside setContentView.
You are missing the R.id.twoDee1 and/or the setContentView().
Add an id to your layout of com.yourapphere.TwoDee
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello"
/>
<com.yourapphere.TwoDee
android:id="#+id/twoDee1"
android:layout_width="150dp"
android:layout_height="100dp"
/>
</LinearLayout>
Then do in the onCreate:
setContentView(R.layout.yourxmlfile);
TwoDee myCanvas = (TwoDee) findViewById(R.id.twoDee1);
myCanvas.setImage(drawable);
Update:
You must call setContentView! I have no idea why you shouldn't want to "cover complete canvas" (what ever that means...)
I have a radiogroup with custom radiobuttons. Icons are set using
rbFirst.setButtonDrawable(R.drawable.first);
But icon is not in the center, how do I fix it?
I tried different attributes in xml file and inside class but it had no effect.
Try using setGravity(Gravity.CENTER); of setting it as a background drawable.
Or if it does not help you will have to derive a new class from RadioButton and override onDraw().
Here's layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<org.test.TestProj.RadioButtonCenter
android:id="#+id/myview"
android:layout_width="fill_parent"
android:layout_height="100dp"
android:layout_centerInParent="true"
android:text="Button test"
/>
</RelativeLayout>
The code:
package org.test.TestProj;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.Gravity;
import android.widget.RadioButton;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
public class RadioButtonCenter extends RadioButton {
public RadioButtonCenter(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CompoundButton, 0, 0);
buttonDrawable = a.getDrawable(1);
setButtonDrawable(android.R.color.transparent);
}
Drawable buttonDrawable;
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (buttonDrawable != null) {
buttonDrawable.setState(getDrawableState());
final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK;
final int height = buttonDrawable.getIntrinsicHeight();
int y = 0;
switch (verticalGravity) {
case Gravity.BOTTOM:
y = getHeight() - height;
break;
case Gravity.CENTER_VERTICAL:
y = (getHeight() - height) / 2;
break;
}
int buttonWidth = buttonDrawable.getIntrinsicWidth();
int buttonLeft = (getWidth() - buttonWidth) / 2;
buttonDrawable.setBounds(buttonLeft, y, buttonLeft+buttonWidth, y + height);
buttonDrawable.draw(canvas);
}
}
}
Finally, here's an attrs.xml file you need to put in res/values so the code can get at platform-defined attributes.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CompoundButton">
<attr name="android:button" />
</declare-styleable>
</resources>