I have a custom TextView with overridden onDraw() in which I mirror the text like this:
#Override
protected void onDraw(Canvas canvas) {
int cx = this.getMeasuredWidth() / 2;
int cy = this.getMeasuredHeight() / 2;
canvas.save();
if(mirrored)
{
canvas.scale(1f, -1f, cx, cy);
}
super.onDraw(canvas);
canvas.restore();
}
Now I want to scroll this mirrored canvas vertically with repeatable .scrollBy(0, -1) method in my activity.
However, when I scroll the text it is getting trimmed from top:
scroll1 scroll2
Probably something happens in the TextView method bringPointIntoView(int offset) or bringTextIntoView().
Is there a quick fix to this problem?
Thanks in advance!
So, for anybody interested I fixed this by setRotation(180) for text view and then mirroring the canvas by the X axis. Then my onDraw method became like this:
#Override
protected void onDraw(Canvas canvas) {
int cx = this.getMeasuredWidth() / 2;
int cy = this.getMeasuredHeight() /2;
canvas.save();
if(mirrored)
{
canvas.scale(-1f, 1f, cx, cy);
setRotation(180);
}
else
{
setRotation(0);
}
super.onDraw(canvas);
canvas.restore();
}
This way the scrollBy method works fine for either direction.
Related
I have overridden image draw method to draw border on AppCompatImageView.
It works fine for some images but for some images it has some blank space above and below image as shown in picture below. I want to draw border exactly over image dimensions.
Below is code
private static final int PADDING = 8;
private static final float STROKE_WIDTH = 8.0f;
private Paint mBorderPaint;
private void initBorderPaint() {
mBorderPaint = new Paint();
mBorderPaint.setAntiAlias(true);
mBorderPaint.setStyle(Paint.Style.STROKE);
mBorderPaint.setColor(Color.WHITE);
mBorderPaint.setStrokeWidth(STROKE_WIDTH);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(PADDING, PADDING, getWidth() - PADDING, getHeight() - PADDING, mBorderPaint);
}
How to do this in android ? Is it possible using some selector designs? I have one option that I can use a icon for this and set it as drawableLeft but without using the icon how can I achieve this please guide me in good direction
You could achieve this with a custom Drawable. Something like:
public class BackgroundColorDrawable extends Drawable {
private Paint paint;
private RectF rectF;
private float cornerRadius = 20f;
private float borderThickness = 3.5f;
private int insetColour = Color.GREEN;
public BackgroundColorDrawable() {
paint = new Paint();
paint.setAntiAlias(true);
rectF = new RectF();
}
public void setInsetColour(int insetColour) {
this.insetColour = insetColour;
invalidateSelf();
}
public void setBorderThickness(float borderThickness) {
this.borderThickness = borderThickness;
invalidateSelf();
}
public void setCornerRadius(float cornerRadius) {
this.cornerRadius = cornerRadius;
invalidateSelf();
}
#Override
public void draw(Canvas canvas) {
paint.setColor(Color.GRAY);
rectF.set(0f, 0f, canvas.getWidth(), canvas.getHeight());
canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint);
paint.setColor(Color.WHITE);
rectF.set(borderThickness, borderThickness, canvas.getWidth() - borderThickness, canvas.getHeight() - borderThickness);
canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint);
paint.setColor(insetColour);
rectF.set(borderThickness, borderThickness, 60f, canvas.getHeight() - borderThickness);
canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint);
paint.setColor(Color.WHITE);
rectF.set(30f, borderThickness, 60f, canvas.getHeight() - borderThickness);
canvas.drawRect(rectF, paint);
}
#Override
public void setAlpha(int alpha) { /* to implement */ }
#Override
public void setColorFilter(ColorFilter colorFilter) { /* to implement */}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
Then wherever you are using your EditText you just have to set the background drawable in code:
BackgroundColorDrawable drawable = new BackgroundColorDrawable();
editText.setBackground(drawable);
Note there's quite a bit of overdraw in this example (the same pixel gets drawn several times) which you could optimise for.
You'll also have to set some left padding on your EditText for the cursor to line up properly.
You can use a background-image on the EditText
editText.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.pic_name, 0);
I have the following code:
Bitmap right1;
public TicTacToe(Context context) {
super(context);
right1 = BitmapFactory.decodeResource(getResources(), R.drawable.plus);
int width = right1.getWidth();
int height = right1.getHeight();
}
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
right1 = Bitmap.createScaledBitmap(right1, canvas.getWidth()/3, canvas.getWidth()/3, false);
canvas.drawBitmap(right1, (canvas.getWidth() - (canvas.getWidth()/3)), 0, null);
invalidate();
}
I want to rescale the bitmap that is called "right1" based off of what the width of the canvas is. I succeeded in doing this, but the line of code that does this is the third line under the onDraw method. The problem with that line of code is that it will constantly loop when I would like to only run it once. Idealy I would like to put that line of code within the TicTacToe method, but I am unsure how to initialize the canvas within the TicTacToe method so that I can get the canvas width.
You don't need to do this stuff inside constructor just add condition in onDraw() method.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
if(right1==null){
right1 = Bitmap.createScaledBitmap(right1, canvas.getWidth() / 3, canvas.getWidth() / 3, false);
canvas.drawBitmap(right1, (canvas.getWidth() - (canvas.getWidth() / 3)), 0, null);
invalidate();
}
}
I need some help understanding the fundamentals of scrolling over items that are drawn to a canvas in Android. Suppose I want to create a timeline where time at 0 is the top of a visualization and as time increased the timeline continues to be rendered below the previous point. If I wish to render this on Android I know I could simply create a bunch of items on a canvas by overriding onDraw(). However, let's suppose the visualization is bigger than the screen allows.
For example in the first picture below the large black box contains the entire canvas as I render it. I create a blue line that runs vertically up and down as well as several yellow, green and blue rectangles. The red box represents the screen of the Android that is rendering the visualization. As it initially opens all items are drawn but only the items contained within the red box show up on the screen.
Now if the user is to scroll down, items that initially appeared below the red box are in view while items that have gone out of the confines of the red box are no longer visable, as represented in the second picture.
I believe I need to use scrollables but I'm quite lost how to do so. I've read over this page http://developer.android.com/training/custom-views/custom-drawing.html explaining how to create your own customer images and this page http://developer.android.com/training/custom-views/making-interactive.html explaining how to make the UI interactive, but I think I'm missing something.
A sample code that illustrates this problem (this is basic, assume there is logic dictating WHERE the boxes/lines go, etc.) is as follows:
package com.example.scrolltest;
import com.example.scrolltest.Draw;
import android.os.Bundle;
import android.app.Activity;
import android.graphics.Color;
public class MainActivity extends Activity {
Draw draw;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
draw = new Draw(this);
draw.setBackgroundColor(Color.WHITE);
setContentView(draw);
}
}
and
package com.example.scrolltest;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;
public class Draw extends View {
Paint paint = new Paint();
public Draw(Context context) {
super(context);
}
#Override
public void onDraw(Canvas canvas) {
paint.setColor(Color.GREEN);
canvas.drawRect(30, 30, 90, 200, paint);
paint.setColor(Color.BLUE);
canvas.drawLine(100, 20, 100, 1900, paint);
paint.setColor(Color.GREEN);
canvas.drawRect(200, 2000, 400, 3000, paint);
}
}
What I cannot figure out though, is how I use a scrollable to scroll down to the rectangles that are off of the screen. I'm also unsure if I started this correctly or should use drawables instead...
Simple Method (If the height required is not very large).
Use a ScrollView and add your Draw view in it. Compute the required height for that view in onMeasure.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
draw = new Draw(this);
draw.setBackgroundColor(Color.WHITE);
ScrollView scrollView = new ScrollView(this);
scrollView.addView(draw);
setContentView(scrollView);
}
public class Draw extends View {
Paint paint = new Paint();
public Draw(Context context) {
super(context);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Compute the height required to render the view
// Assume Width will always be MATCH_PARENT.
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = 3000 + 50; // Since 3000 is bottom of last Rect to be drawn added and 50 for padding.
setMeasuredDimension(width, height);
}
#Override
public void onDraw(Canvas canvas) {
paint.setColor(Color.GREEN);
canvas.drawRect(30, 30, 90, 200, paint);
paint.setColor(Color.BLUE);
canvas.drawLine(100, 20, 100, 1900, paint);
paint.setColor(Color.GREEN);
canvas.drawRect(200, 2000, 400, 3000, paint);
}
}
An alternative can be to use offset X/Y values.
How you handle the offset values is up to you, all though I prefer using a class I call Camera. The access should be static.
public void render(Canvas c){
c.drawRect(100 - offsetX, 100 - offsetY, 120 - offsetX, 120 - offsetY, Paints.BLUE);
}
And to pan the canvas:
float x, y;
#Override
public boolean onTouchEvent(MotionEvent ev) {
if(ev.getAction() == MotionEvent.ACTION_DOWN){
x = ev.getX();
y = ev.getY();
}else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
float currX = ev.getX();
float currY = ev.getY();
float newOffsetX = (x - currX),
newOffsetY = (y - currY);
//The next two if-statements are to add sensitivity to the scrolling.
//You can drop these if you don't plan on having touch events
//with pressing. Pressing works without this as well, but the canvas may move slightly
//I use DP to handle different screen sizes but it can be replaced with pixels. c is a context-instance
if (newOffsetY < Maths.convertDpToPixel(2, c) && newOffsetY > -Maths.convertDpToPixel(2, c))
newOffsetY = 0;
if (newOffsetX < Maths.convertDpToPixel(2, c) && newOffsetX > -Maths.convertDpToPixel(2, c))
newOffsetX = 0;
offsetX += newOffsetX;
offsetY += newOffsetY;
x = ev.getX();
y = ev.getY();
}
return true;
}
And a sample Camera-class:
public class Camera{
public static float offsetX, offsetY;
//The constructor. ix and iy is the initial offset. Useful if you are creating a game and need to change the initial offset to center around a starting position.
//Most of the time it will be enough to set the values to 0
public Camera(float ix, float iy){
this.offsetX = ix;
this.offsetY = iy;
}
}
I'm making an App that needs to be able to draw new graphics on top of the last set.
This is my current onDraw() method -
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
if(points.size() > 0) {
//do some stuff here - this is all working ok
canvas.drawLine(p1.x, p1.y, p2.x, p2.y, linePaint);
}
}
Basically, I need to draw the new graphics as a layer on top of the last, so what I'm looking for is a way to carry the image of the last canvas to the current.
I have tried to figure it out myself using the canvas.setBitmap() method but it acts very funny.
Any help appreciated :)
P.S if it's needed, the the class extends SurfaceView and implements SurfaceHolder.Callback
Edit: This is what I have tried in the onDraw() method but it just force closes
if(bitmap != null) {
canvas.drawBitmap(bitmap, 0, 0, paint);
canvas.setBitmap(bitmap);
}
Found the answer myself :)
#Override
protected void onDraw(Canvas c) {
if(bitmap != null && canvas != null) {
canvas.drawLine(p1.x, p1.y, p2.x, p2.y, linePaint);
c.drawBitmap(bitmap, 0, 0, linePaint);
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
bitmap = Bitmap.createBitmap(width, height, Config.RGB_565);
canvas = new Canvas(bitmap);
canvas.drawColor(Color.WHITE);
}
Works exactly as intended, it creates the effect of drawing on top of the old canvas continuously
You will have to store the previous image persistently onto a ArrayList, and during ondraw, loop through the ArrayList to redraw all items.
something like this:
for (Graphic graphic : _graphics) {
bitmap = graphic.getBitmap();
coords = graphic.getCoordinates();
canvas.drawBitmap(bitmap, coords.getX(), coords.getY(), null);
}