I have created this class which extends View and draws a graph in Canvas. While drawing a graph I am considering actual screen height and width and it draws a graph according to that.
My problem is I am using this View as a widget in another activity with a desired size say 100 by 150..but the View class doesn't scale itself and only shows a clipped part of it not the graph. I know O have to do some calculation to fit this canvas into desired size but I have no clue how to do that..Any help would be great!!
Code where I am drawing graph is
public class GraphPlotting extends View
{
public void onDraw(Canvas canvas)
{
readPoints();
Paint paint1 = new Paint();
paint1.setColor(Color.rgb(0xFF,0xFF, 0xFF));
paint1.setStrokeWidth(1.0f);
Paint paint2 = new Paint();
paint2.setColor(Color.rgb(0xFF,0xFF, 0xFF));
paint2.setStrokeWidth(10.0f);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.RED);
canvas.drawPaint(paint);
canvas.drawLines(p,paint1);
}
public void readPoints()
{
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
height = dm.heightPixels;
width = dm.widthPixels;
// drawing graph based on above dimension which in my case is 480 by 800
}
xml Layout where I am using this View
<RelativeLayout
android:layout_width="200dp"
android:layout_height="200dp"
android:orientation="vertical"
android:layout_below="#+id/btn_analyze"
android:layout_centerHorizontal="true"
android:id="#+id/rlgraph"
>
<com.aventusoft.mylynel.GraphPlotting
android:id="#+id/view1"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
I want to fit my canvas with my Relative layout height and width and graph should visible in that area..currently its showing the View but only part of it..Graph is not visible at all..Please help me in calculation I need to do..
Copy/paste direct from one of my own classes which extends ImageView to draw a graph. The idea is that you can put this into any layout. The class takes care of it's own measurements.
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
widgetHeight = h;
widgetWidth = w;
if (sizeChangedObservers != null && sizeChangedObservers.size() > 0) {
for (SizeChangedObserver observer : sizeChangedObservers) {
observer.callback(w, h);
}
}
updateIncrements();
}
The sizeChangedObservers just let's me notify other classes if the graph size changes, e.g. I have scale indicators on the X and Y axis. updateIncrements() is where I figure out how many pixels each X and Y represents using the device screen resolution, graph size and DPI count.
Using this approach, the canvas will be whatever size it is and you don't care. When you plot, you just use x * xPixels and y * yPixels which are the calculated pixels per value.
[EDIT] Try reading this first http://developer.android.com/training/custom-views/custom-drawing.html
Related
I have a frame layout with a background image.I am using canvas to draw lines on this framelayout.
The issue is , on different screen sizes , the lines are not appearing at the same place.
I tried multiplying the coordinates by the screen density dpi , but its still not working.
Snippet of my canvas code
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
bitmap = ((BitmapDrawable) zoom_lay.getBackground()).getBitmap();
mutableBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
canvas = new Canvas(mutableBitmap);
canvas.drawColor(Color.TRANSPARENT);
zoom_lay.setBackground(new BitmapDrawable(getResources(), mutableBitmap));
final Path pathPolygon_2 = new Path();
pathPolygon_2.reset(); // only needed when reusing this path for a new build
canvas.drawLine(500,500,600,615,paint);
Thank you
Create your layout with the following params:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/mainView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#mipmap/background"
tools:context=".MainActivity">
Can be any type, FrameLayout is just an example. The important part is to set it's width and height to match parent.
From there you can use your code to get the bitmap and draw on it only thing, let's say you want to draw the line horizontally in the center:
canvas.drawLine(0,muteableBitmap.getHeight()/2,muteableBitmap.getWidth(),muteableBitmap.getHeight()/2,paint);
And say you want a line vertically from the center:
canvas.drawLine(muteable.getWidth()/2,0,muteableBitmap.getWidth()/2,muteableBitmap.getHeight,paint);
I've written this manually so it might not compile due to capital letters and such, but it should work
first you need to get the size of the Bitmap, and then calculate the end points of the line according to your needs. For example, if you want to draw a line in the middle of the Bitmap, the Y coordinate is this Bitmap.getHeight() * 0.5f. I hope I can help you.
Bitmap mutableBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
int height = mutableBitmap.getHeight();
int width = mutableBitmap.getWidth();
float startX = width * 0f;
float startY = height * 0.5f;
float stopX = width * 1f;
float stopY = height * 0.5f;
canvas.drawLine(startX, startY, stopX, stopY, paint);
I am trying to have user's profile picture in one of my FABs. I tried a cheeky way by giving the fab a negative padding but it doesn't work. There is still padding. How do i remove that padding and have a filled FAB?
this is my fab :
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|start"
android:src="#drawable/testpropic"
android:padding="-50dp"
android:elevation="0dp"
android:id="#+id/profileFAB"
android:stateListAnimator="#null"
app:pressedTranslationZ="12dp"
android:visibility="gone"
/>
This is what I am getting :
There are two ways to achieve your desired effect:
Simply just override the original dimensions of the Android support
library by adding:
<dimen name="design_fab_image_size" tools:override="true">48dp</dimen>
to your dimens.xml, this will automatically override the design_fab_image_size
set by the library (may not work with third party libraries) as noted in How to remove auto padding of FAB button in android?
Note: 48dp in this case is the width of the floating action button.
Use an ImageView with a Circle Transformation using the Picasso Library instead of a FloatingActionButton. You'll lose the simple Fab functionality and animations but you'll have more flexibility with setting the image correctly. As you're animating the FAB on button press (i.e. displaying all four floating action buttons, replacing it with an ImageView of the same dimensions and attributes, would be easy to change to in your XML)
An example of the class would be:
public class CircleTransform implements Transformation {
#Override
public Bitmap transform(Bitmap source) {
int size = Math.min(source.getWidth(), source.getHeight());
int x = (source.getWidth() - size) / 2;
int y = (source.getHeight() - size) / 2;
Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);
if (squaredBitmap != source) {
source.recycle();
}
Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig());
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
BitmapShader shader = new BitmapShader(squaredBitmap,
BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
paint.setShader(shader);
paint.setAntiAlias(true);
float r = size / 2f;
canvas.drawCircle(r, r, r, paint);
squaredBitmap.recycle();
return bitmap;
}
#Override
public String key() {
return "Circle";
}
}
and an example of using such technique with Picasso would be:
Picasso.with(context).load(imageUri).transform(new CircleTransform()).into(imageView);
Note: .load() can take a String URL or Resource ID
Update:
If you want a circle image inside a circle FloatingActionButton, use both techniques but use your FloatingActionButton instead. Transform the image and then set it to your FloatingActionButton using Picasso.
I am designing a drawing app where user can import image from gallery and then scale up or down to fit the screen width or height. so that user can draw onto the imported picture, using the below code.
I am extending View, named DrawView. The DrawView is same as screenwidth, but its height is less than screenheight because there are some buttons above it, placing the DrawView to the bottom of the Screen under the functioning buttons, and so I declared DrawViewHeight.
See below for examples for dimension and results of variables.
Question:
The bitmap can be properly loaded and scaled to fit to the DrawView.
However, it is located at the top of the DrawView. I would like to place it in the middle of the screen, so i added the following code but still FAILS.
bitmapCanvas.drawBitmap(bitmap, x_adjustment, y_adjustment, null);
How could it be further modified such that the imported image (decoded and copied as bitmap while importing) is placed center of DrawView, with blank space (eg. white) above and below and left and right of the loaded scaled bitmap image?
Note: Those surrounding space around the image are not to be drawn onto by user.
Codes:
Declarations:
private Bitmap bitmap; // drawing area for display or saving
private Canvas bitmapCanvas; // used to draw on bitmap
OnSizeChanged:
public void onSizeChanged(int w, int h, int oldW, int oldH)
{
super.onSizeChanged(w, h, oldW, oldH);
DrawViewWidth = w;
DrawViewHeight = h;
bitmap = Bitmap.createBitmap(getWidth(), DrawViewHeight, Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas(bitmap);
bitmap.eraseColor(Color.WHITE); // erase the BitMap with white
}
OnDraw:
#Override
protected void onDraw(Canvas canvas) // called each time this View is drawn
{
canvas.drawBitmap(bitmap, 0, 0, paintScreen);
}
Load Pictures:
public void load_pic(final String picturePath)
{
// get screen dimension first
WindowManager wm = (WindowManager) context_new.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
final int screenWidth = display.getWidth();
final int screenHeight = display.getHeight();
//get importing bitmap dimension
Options op = new Options();
op.inJustDecodeBounds = true;
Bitmap pic_to_be_imported = BitmapFactory.decodeFile(picturePath, op);
final int x_pic = op.outWidth;
final int y_pic = op.outHeight;
// scaling
final int IMAGE_MAX_SIZE= (int) Math.max(DrawViewWidth, DrawViewHeight);
int scale = 1;
if (op.outHeight > IMAGE_MAX_SIZE || op.outWidth > IMAGE_MAX_SIZE) {
scale = (int)Math.pow(2, (int) Math.round(Math.log(IMAGE_MAX_SIZE / (double) Math.max(op.outHeight, op.outWidth)) / Math.log(0.5))); }
final BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
// Start loading image to the DrawView
if ((x_pic > DrawViewWidth) || (y_pic > DrawViewHeight))
{
AlertDialog.Builder onBackBuilder = new AlertDialog.Builder(context_new);
onBackBuilder.setPositiveButton(R.string.buttontext_create_load_pic_stretch, new DialogInterface.OnClickListener()
{
//skipped
}
onBackBuilder.setNegativeButton(R.string.buttontext_create_load_pic_keep_scale, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
float xratio = (float) x_pic / (float) screenWidth;
float yratio = (float) y_pic / (float) DrawViewHeight;
int adjusted_x = 0;
int adjusted_y = 0;
if (xratio >= yratio) {adjusted_x = screenWidth; adjusted_y = (int) (y_pic / xratio);}
if (xratio < yratio) {adjusted_y = DrawViewHeight; adjusted_x = (int) (x_pic / yratio);}
bitmap = (BitmapFactory.decodeFile(picturePath, o2));
bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
bitmap = Bitmap.createScaledBitmap(bitmap, adjusted_x, adjusted_y, true);
int x_adjustment = (screenWidth - adjusted_x) /2;
int y_adjustment = (DrawViewHeight -adjusted_y) /2;
bitmapCanvas.drawBitmap(bitmap, x_adjustment, y_adjustment, null);
// How to modify to put to center of DrawView????
invalidate();
}
});
AlertDialog alert = onBackBuilder.create();
alert.show();
}
Examples of dimension and results of variables:
Screen : 480W * 800H
DrawView : 480W * 590H
Original image : 3264W * 2448H
Scaled image : adjusted_x=480 (meet screenwidth), adjusted_y=360
x_adjustment : 0
y-adjustment : 115
Layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.pearmak.drawing.DrawView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center_vertical|center_horizontal|center"
android:background="#drawable/drawview_border" />
</RelativeLayout>
// You can try this :
int width = containerBitmap.getWidth();
int height = containerBitmap.getHeight();
float centerX = (width - centeredBitmap.getWidth()) * 0.5f;
float centerY = (height- centeredBitmap.getHeight()) * 0.5f;
mCanvas.drawBitmap(centeredBitmap, centerX, centerY, paint);
You can use it to draw a bitmap at the center of another bitmap.
In your onDraw() you specify (0,0) as the bitmap (x,y) which will draw it at the top left corner of your DrawView so your x_adjustment, y_adjustment effect is lost once onDraw is called.
You should do your bitmap location adjustment code inside onDraw not inside onClick
#Override
protected void onDraw(Canvas canvas) // called each time this View is drawn
{
canvas.drawBitmap(bitmap, x_adjustment, y_adjustment, null);
}
The LayoutParameters of your imageview or the imageview's parent view need to specify centering within the screen
also, post your XML layout if there is one, as well as where you make the bitmap part of an imageview in your code
As iTech has noted, to get your Bitmap drawn in the center of the DrawView, you need to draw it in the correct location inside your onDraw method.
Create two additional fields:
int x_adjust, y_adjust;
In your onClick (when loading the bitmap), calculate the correct center, and set x_adjust and y_adjust to the relevant values. Then make sure you use these values in the onDraw method (rather than 0,0).
#Override
protected void onDraw(Canvas canvas)
{
canvas.drawBitmap(bitmap, x_adjust, y_adjust, null);
}
Note: Make sure to recalculate x_adjust and y_adjust in the onSizeChanged method also. Otherwise the bitmap will no longer be centered after you rotate the device.
I have asked questions related to this before, but I think I was framing my objective incorrectly.
What I have: a custom ImageView that displays a graphic and defines multiple touchable areas as rectangles within the image. My problem is scaling. I want to define the touchable area of the image based on it's actual resolution in the bitmap file, but translate those coordinates so the the rectangle covers the same area on the scaled image.
This is what I've got so far:
When the view is created, calculate the ratio of the actual to scaled sizes
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
Drawable pic=this.getDrawable();
int realHeight= pic.getIntrinsicHeight();
int realWidth=pic.getIntrinsicWidth();
int scaledHeight=this.getHeight();
int scaleWidth=this.getWidth();
heightRatio=(float)realHeight/scaledHeight;
widthRatio=(float)realWidth/scaleWidth;
}
Now I want to take the coordinates that define rectangle(s) on the original (un-scaled) image
and draw that rectangle(s) to the same area of the image -- but accounting for scale:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint p=new Paint();
p.setStrokeWidth(1);
p.setColor(Color.BLUE);
for (HotSpot h: spots)
{
//Each hotspot has a rectangle defined in reference to the actual size of the image
Rect old=h.getRect();
float offsetLeft=old.left+(old.left*widthRatio);
float offsetTop=old.top+(old.top*heightRatio);
float offsetRight=old.right+(old.right*heightRatio);
float offsetBottom=old.bottom+(old.bottom*widthRatio);
RectF nRect=new RectF(offsetLeft,offsetTop,offsetRight,offsetBottom);
canvas.drawRect(nRect,p);
}
The results are "in the ball park" but not quite accurate. Any help is appreciated.
You can try this solution:
Get the screen density
Get the image height (or width)
Divide the height (or width) by the density, so you get the length in inches
Divide the coordinate by the length in inches, so you get a relationship between the coordinate and the image which is independent by the image effective size
When you have to draw the same point on a differently scaled image, multiply the last result for the length in inches of the second image (which you obtain using the same operations listed above)
Example:
On the first image:
float density = getResources().getDisplayMetrics().density;
int width = getWidth();
float inchesLength = width/density;
float scaledXCenter = xCenter / inchesLength;
On the same image with a different scale:
float density = getResources().getDisplayMetrics().density;
int width = getWidth();
float inchesLength = width/density;
float restoredXCenter = scaledXCenter * inchesLength;
I am trying to create a tab-like interface where I will switch views by sliding left/right. I display to the user what page they are on by adding a blue glow to the back of the tab icon shown here:
I did this by creating a custom view that will draw the background glow (if the tab is selected) and then the icon. My issue is that the glow is too large. All I need to do is to make it smaller so the sides don't get clipped like they are now (the glow overlaps the page on the bottom by design).
The glow was created using a shape drawable in XML (just a simple radial gradient).
I have tried changing the size of the shape in XML but it always comes out the same. I tried adjusting attributes under and tried adjusting the height and width under too.
Constructor:
public TabIconView(Context context, Bitmap tab)
{
super(context);
back = BitmapFactory.decodeResource(getResources(), R.drawable.background_blue_glow);
this.tab = tab;
}
onMeasure and onDraw:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
setMeasuredDimension(tab.getWidth() * 2, tab.getHeight() * 2);
}
#Override
protected void onDraw(Canvas canvas)
{
width = getMeasuredWidth();
height = getMeasuredHeight();
if (selected)
canvas.drawBitmap(back, (width - back.getWidth()) / 2, (height - back.getHeight()) / 2, null);
canvas.drawBitmap(tab, (width - tab.getWidth()) / 2, (height - tab.getHeight()) / 2, null);
super.onDraw(canvas);
}
Any ideas will be greatly appreciated.
One of the versions of Canvas.drawBitmap() lets you specified the width and height that you want the bitmap to have when drawn on screen. You could also simply rescale the bitmap when you load it (the Bitmap class has methods to create scaled versions of bitmaps.)
Figured it out. In case anybody wants to know...
public TabIconView(Context context, Bitmap tab)
{
super(context);
int backSize = (int) (42 * context.getResources().getDisplayMetrics().density + .5f);
b = BitmapFactory.decodeResource(getResources(), R.drawable.background_blue_glow);
back = Bitmap.createScaledBitmap(b, backSize, backSize, false);
this.tab = tab;
}
I had to create a scaled bitmap from the bitmap that was generated from the shapedrawable.