This might not be the best practice for this but its seems like it would be the easiest. I need to pass a bitmap to a SurfaceView so its ready with onDraw is called. But I cant seem to figure out the syntax for when its assigned. Note the extra bitmap in the constructor.
public class SampleView extends SurfaceView implements SurfaceHolder.Callback {
SurfaceHolder holder;
Bitmap mBitmap = null;
public SampleView (Context context, AttributeSet attrs, Bitmap bitmap) {
super(context, attrs);
if(bitmap != null)
mBitmap = bitmap
SurfaceHolder holder = getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setFilterBitmap(true);
double aspectRatio = ((double) mBitmap.getWidth()) / mBitmap.getHeight();
Rect dest = new Rect(0, 0, this.getWidth(),(int) (this.getHeight() / aspectRatio));
canvas.drawBitmap(mBitmap, null, dest, paint);
}
Then i'm trying to assign it, but I don't have a clue how to set the view and pass the bitmap
setContentView(R.layout.sample); //has my custom view
mSampleView = (SampleView) findViewById(R.id.sampleView);
This maybe me not understanding the ways you can init a view. I have seen examples using "addview()". The base goal of this is to display a bitmap in a surfaceview. Based on my googling you cant access the canvas other than the onDraw so I would need to pass it in when its init (i think). I'm also not sure that the constructor is called when I set the var "mSampleView" or when I use setContentView() to call the layout. If its the latter all this mute and makes me wonder if there is a way to access the canvas or target the main activity. If I could target the main activity i could just access it there when needed but I dont have a very good grasp on scope in android/java.
Is it a bad practice to pass data this way? Would be there a more standard way to passdata other than a global datastore.
To clarify my comment a bit more...You can't pass another object directly to the constructor of a class when its inherited class doesn't support that object in the constructor call.
You would have to pass the bitmap afterwards in another method call, either via the value, or by passing a reference to the object.
Your getting extremely confused with the way the onDraw method works with the objects that it uses. I know because I had trouble understanding it for the first time also.
Here is an example of something you could do.
First construct the view and give the view a bitmap
private Bitmap myReferencedBitmap;
public SampleView (Context context, AttributeSet attrs) {
super(context, attrs);
}
Now pass in the bitmap object you want to use.
public fooBitmap(Bitmap b) {
myReferencedBitmap = b;
}
Now use onDraw with the referenced bitmap
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(myReferencedBitmap, null, null, paint);
}
Now whenever you modify the object b that you passed into the SampleView class, and then invalidate the view, the onDraw method will use the new object, because myReferencedBitmap holds a reference to the (object) value.
Another thing to look at is byref vs byval in Java.
Good luck.
Related
I generate an ellipse and I try to reshape with respect to sweepAngle_speed that u can see below. This sweepAngle_speed come from MainActivity.java. In MainActivity.java, I create a seekbar and I use an algorithm between value of seekbar and sweepAngle_speed, therefore I expected a change in filled area in my ellipse. onDraw function is not called directly, so I use invalidate function in my getLog function which is created by me. However I cannot call onDraw function anyway. When I run the code, onDraw function is called directly by system 3 times, however when I change seekbar value, I do not call onDraw function anyway. My first question is that How onDraw function is called directly by system ? The second one is how I can call onDraw function during system is working. Thanks.
CustomView.java
public CustomView(Context context, #Nullable AttributeSet attrs) {
super(context);
m_Context = context;
getLog();
// create the Paint and set its color
}``
#Override
protected void onDraw(Canvas canvas) {
//c=canvas;
//super.onDraw(c);
Paint p1 = new Paint();
RectF rectF = new RectF(-750, 0, 750, 720);
//p1.setColor(Color.parseColor("#34ebe2"));
p1.setShader(new LinearGradient(0, -360, 0, getHeight(), Color.CYAN, Color.BLUE, Shader.TileMode.MIRROR));
Log.d(TAG, "CANVAS: onDraw içine girdi ve Speed angle: " + sweepAngle_speed);
canvas.drawArc(rectF, 90, -sweepAngle_speed, true, p1);
}
public void getLog () {
paint = new Paint();
paint.setColor(Color.BLUE);
Log.d(TAG, "Speed geldi buraya ve invalidate yaptı");
setWillNotDraw(false);
//this.invalidate();
this.invalidate();
}
}
You can't call onDraw directly. You can use the invalidate method which will redraw it
You can make a function inside CustomView class e.g:
public void setSpeed(int sweepAngle_speed){
this. sweepAngle_speed = sweepAngle_speed;
invalidate(); // This invalidate will call onDraw and draw your view again
}
If you extend ViewGroup so you need to call setWillNotDraw(false) in the constructor of your ViewGroup
I'm trying to implement an easy "show/hide" function of a custom SurfaceView.
Many of suggested methods on this forum doesn't work for me...
for example:
1) surfaceView.setVisibility(SurfaceView.GONE);// or INVISIBLE or VISIBLE
2) relativeLayout.removeView(surfaceView); it works but, destroy the view.
3) surfaceView.setZOrderOnTop(true) or false, dinamically changed doesn't works
4) surfaceView.getDrawingCache(): this method return the SurfaceView as a bitmap so , the sequence is
Hide
get the bitmap from surfaceView and save it (Bitmap btm = surfaceView.getDrawingCache())
create new dummy canvas with saved bitmap, dummyCanvas = new Canvas(btm);
clear the canvas ( Canvas instance is in the MainActivity and initialized in the run() mehtod of the SurfaceView thread.)
Show
surfaceView.draw(dummyCanvas);
Also the last one method don't work, it return a black(jpeg) or empty(png) image.
This is a part of the code:
class CustomSurfaceView extends SurfaceView implements Runnable{
Thread thread = null;
SurfaceHolder surfaceHolder;
volatile boolean running = false;
public CustomSurfaceView(Context context) {
super(context);
this.setWillNotDraw(false);
this.setBackgroundColor(Color.TRANSPARENT);
this.setZOrderOnTop(true);
this.setDrawingCacheEnabled(true);
this.buildDrawingCache();
// get the holder to create the canvas into the Thread.run() method:
// "canvas = surfaceHolder.lockCanvas(null)"
surfaceHolder = getHolder();
surfaceHolder.setFormat(PixelFormat.TRANSPARENT);
}
//getDrawingCache() seems to be the only way to get a bitmap from the
//SurfaceView, because my canvas is not "Canvas c = new Canvas(Bitmap)
//BUT this method only return EMPTY BUT NOT NULL image.
public Bitmap getBitmap(){
return this.getDrawingCache();
}
I'm trying to apply a visual effect to a viewgroup. My idea is to grab a bitmap of the viewgroup, shrink it down, expand it back up, and draw it over the viewgroup to give it a blocky, low quality effect.
I've got most of the way there using this code:
public class Blocker {
private static final float RESAMPLE_QUALITY = 0.66f; // less than 1, lower = worse quality
public static void block(Canvas canvas, Bitmap bitmap_old) {
block(canvas, bitmap_old, RESAMPLE_QUALITY);
}
public static void block(Canvas canvas, Bitmap bitmap_old, float quality) {
Bitmap bitmap_new = Bitmap.createScaledBitmap(bitmap_old, Math.round(bitmap_old.getWidth() * RESAMPLE_QUALITY), Math.round(bitmap_old.getHeight() * RESAMPLE_QUALITY), true);
Rect from = new Rect(0, 0, bitmap_new.getWidth(), bitmap_new.getHeight());
RectF to = new RectF(0, 0, bitmap_old.getWidth(), bitmap_old.getHeight());
canvas.drawBitmap(bitmap_new, from, to, null);
}
}
I simply pass in the canvas to draw on and a bitmap of what needs to be scaled down+up and it works well.
public class BlockedLinearLayout extends LinearLayout {
private static final String TAG = BlockedLinearLayout.class.getSimpleName();
public BlockedLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
applyCustomAttributes(context, attrs);
setup();
}
public BlockedLinearLayout(Context context) {
super(context);
setup();
}
private void setup() {
this.setDrawingCacheEnabled(true);
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
// block(canvas); If I call this here, it works but no updates
}
#Override
public void onDraw(Canvas canvas) {
// block(canvas); If I call this here, draws behind children, still no updates
}
private void block(Canvas canvas) {
Blocker.block(canvas, this.getDrawingCache());
}
}
The problem I'm having is in my viewgroup. If I run the block method in the viewgroup's draw, it draws over everything but doesn't ever update when child views change. I've traced function calls with Log, and the draw method seems to be running, but nothing changes.
I've also tried implementing this in onDraw. This draws the bitmap behind all the children views, and again they aren't updating.
Can anyone explain how I would go about fixing this?
Try this:
#Override
protected void dispatchDraw(Canvas canvas) {
// call block() here if you want to draw behind children
super.dispatchDraw(canvas);
// call block() here if you want to draw over children
}
And call destroyDrawingCache() and then, buildDrawingCache() each time you change a child.
Draw() method will work well for you.
I'm now trying to make a count time view in a circle shape, when time is passing, the view will reducing his angle. It's used to cover profile photo(a circle shape photo).
Starting with Android API 23, you can use onDrawForeground(Canvas) to draw on top of child views: https://developer.android.com/reference/android/view/View#onDrawForeground(android.graphics.Canvas)
Unlike onDraw() though, be sure to call through to the super class:
#Override
public void onDrawForeground(final Canvas canvas) {
super.onDrawForeground(canvas);
// Your code here
}
My project is about image processing in android.I have a bitmap that I have loaded from a resource file (an PNG image). I want to draw it. But I couldn't.
Here my code snippet:
mB = BitmapFactory.decodeResource(getResources(), R.drawable.picture);
Canvas c = new Canvas(mB);
Paint p = new Paint();
c.drawBitmap(mB,0,0,p);
it didn't work. Is the code true? .Is there any thing more that I must do?
You should use an ImageView instead and load it by
imageView.setImageResource(R.drawable.picture);
If you want to manually draw it with a Canvas, you have to use a canvas that is passed into a draw() method and implement a Custom View.
Update to add example CustomView:
public class CustomView extends View {
private Paint mPaint;
private Drawable mDrawable;
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mDrawable = context.getResources().getDrawable(R.drawable.some_drawable);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mDrawable.draw(canvas);
}
}
There are a couple of things you are missing.
First, I think you're misunderstanding the Canvas(Bitmap b) constructor. The Bitmap passed in there is one that the Canvas will draw into. This could be just a new Bitmap that you constructed.
Second, it is recommended that you use the Canvas that is passed to you in your View's onDraw method. Presumably that View is one from your Activity, either fetched from your XML layout via findViewById or constructed and passed to setContentView in the Activity's onCreate() method.
So, you are going to have to subclass View and override the onDraw method to get your drawing done. Something like:
public class MyView extends View {
#Override
public void onDraw (Canvas c) {
Bitmap mB = BitmapFactory.decodeResource(this.getContext().getResources(), R.drawable.picture);
c.drawBitmap(mB, 0, 0, null);
}
}
Then, in your Activity, you'll need to create an instance of your new View and pass it to the Activity via setContentView:
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mv = new MyView(this);
setContentView(mv);
}
You can instead call the setContentView(View v, ViewGroup.LayoutParameters lp) overload if you want to set up the LayoutParameters.
I haven't tested any of this, but it should at least get you on the right path.
I'm new to android programming and what I'm trying to figure out is this;
In my layout i have a TextView, ImageView, and Button, all on a vertically oriented LinearLayout.
I want to be able to dynamically draw circles in the ImageView, without disturbing the rest of my layout(textview/button). I'm trying to create a canvas, and use the drawcircle function within canvas to set the location of the circle. And then draw that canvas to my imageview in some way. I cannot get this to work, is there a trick to this? Or is my method fundamentally wrong? How would i go about drawing circles to the ImageView without recreating my entire layout?
Thanks!
I had the same challenge and came to the conclusion that overwriting onDraw will at least in the general case not work. My blog explains the reasons. What worked very well for me is the following:
Create a new image bitmap and attach a brand new canvas to it.
Draw the image bitmap into the canvas.
Draw everything else you want into the canvas.
Attach the canvas to the ImageView.
Here is a code snippet for this:
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
ImageView myImageView = ...
Bitmap myBitmap = ...
Paint myRectPaint = ...
int x1 = ...
int y1 = ...
int x2 = ...
int y2 = ...
//Create a new image bitmap and attach a brand new canvas to it
Bitmap tempBitmap = Bitmap.createBitmap(myBitmap.getWidth(), myBitmap.getHeight(), Bitmap.Config.RGB_565);
Canvas tempCanvas = new Canvas(tempBitmap);
//Draw the image bitmap into the cavas
tempCanvas.drawBitmap(myBitmap, 0, 0, null);
//Draw everything else you want into the canvas, in this example a rectangle with rounded edges
tempCanvas.drawRoundRect(new RectF(x1,y1,x2,y2), 2, 2, myPaint);
//Attach the canvas to the ImageView
myImageView.setImageDrawable(new BitmapDrawable(getResources(), tempBitmap));
ImageView imageView=(ImageView) findViewById(R.id.image);
Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
canvas.drawCircle(50, 50, 10, paint);
imageView.setImageBitmap(bitmap);
I think that a better approach would be to create a custom ImageView and Override the onDraw method. Something like:
public class CustomView extends ImageView {
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrst) {
super(context, attrst);
}
public CustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
MyBitmapFactory bitMapFac = null;
public void setBitmapFactory(MyBitmapFactory bitMapFac)
{
this.bitMapFac = bitMapFac;
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
/*instantiate a bitmap and draw stuff here, it could well be another
class which you systematically update via a different thread so that you can get a fresh updated
bitmap from, that you desire to be updated onto the custom ImageView.
That will happen everytime onDraw has received a call i.e. something like:*/
Bitmap myBitmap = bitMapFac.update(); //where update returns the most up to date Bitmap
//here you set the rectangles in which you want to draw the bitmap and pass the bitmap
canvas.drawBitmap(myBitMap, new Rect(0,0,400,400), new Rect(0,0,240,135) , null);
super.onDraw(canvas);
//you need to call postInvalidate so that the system knows that it should redraw your custom ImageView
this.postInvalidate();
}
}
It would be a good idea to implement some logic that checks if there is a fresh bitmap to acquire via the update() method, so that the code inside onDraw won't execute every time and put overhead to the system.
And then use your custom view wherever you need it. The easiest way would be to declare it directly inside the activity_layout.xml as such:
<com.mycustomviews.CustomView
android:id="#+id/customView"
android:layout_centerInParent="true"
android:layout_height="135dp"
android:layout_width="240dp"
android:background="#android:color/transparent"/>
And then access is in your code like any other view by using:
customView = (CustomView) findViewById(R.id.customView);
If you have a xml with layout with all your elements arranged in vertical orienttion. You can achieve what you want by making a class in your package which extends Class View and override its onDraw method. draw circle in it as you want . then instead of adding imageView in the layout add this your own view in the xml layout.like the following
< LinearLayout >
< TextView> < /TextView>
< Button> < /Button>
<com.prac.MyView> </ com.prac.MyView>
< /LinearLayout>
Check the following link for 2D graphics. Its a great tutorial to read.
http://organicandroid.blogspot.com/2010/08/starting-to-play-with-graphics.html
Hope this help : )
There are a couple of ways to do what you want, but using an ImageView the way you describe isn't one of them. One possibility is to have the ImageView display an animated Drawable. You can then focus on having your Drawable implementation draw the circles. Another is to create a new Bitmap every time you want the image to change and set the ImageView to display the new Bitmap. The usual way of doing this, though, is to create a custom View subclass. The API Demos sample project has an example of a custom view, and you can find lots tutorials with Google.