I have an image which I'm using as a background for a RelativeLayout. The image needs to be tiled horizontally to make a pattern.
I'm able to get the image to tile horizontally by using this code:
BitmapDrawable b3 = (BitmapDrawable)getResources().getDrawable(R.drawable.background);
b3.setTileModeX(Shader.TileMode.REPEAT);
v.findViewById(R.id.layout).setBackgroundDrawable(b3);
The problem is that the image also tiles vertically. It seems to tile in the "clamp" mode in the vertical, but "repeat" mode in the horizontal. Here is a screenshot:
As you can see, the image is just a little bit smaller than the space it occupies, and the bottom edge is "clamped".
How can I set the image to stretch vertically but tile horizontally?
This method invokes creation of new bitmap but it looks like it's acrhiving your goal
View view = findViewById(R.id.layout);
BitmapDrawable bd = (BitmapDrawable) getResources().getDrawable(R.drawable.tile);
int width = view.getWidth();
int intrinsicHeight = bd.getIntrinsicHeight();
Rect bounds = new Rect(0,0,width,intrinsicHeight);
bd.setTileModeX(TileMode.REPEAT);
bd.setBounds(bounds);
Bitmap bitmap = Bitmap.createBitmap(bounds.width(), bounds.height(), bd.getBitmap().getConfig());
Canvas canvas = new Canvas(bitmap);
bd.draw(canvas);
BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), bitmap);
view.setBackground(bitmapDrawable);
Please note that it only works if view was already layouted, so a method lile onWindowFocusChanged is a good place for this code.
I feel it is straight forward:(This code will tile in Y and repeat in x)
In your onWindowFoucsChanged you call:
public void onWindowFocusChanged(boolean hasFocus) {
// TODO Auto-generated method stub
super.onWindowFocusChanged(hasFocus);
Drawable d = getRepeatingBG(this, R.drawable.image_that_you_want_to_repeat);
body_view.setBackgroundDrawable(d);
}
private Drawable getRepeatingBG(Activity activity, int center)
{
DisplayMetrics dm = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled=true;
Bitmap center_bmp = BitmapFactory.decodeResource(activity.getResources(), center, options);
center_bmp.setDensity(Bitmap.DENSITY_NONE);
center_bmp=Bitmap.createScaledBitmap(center_bmp, dm.widthPixels , center_bmp.getHeight(), true);
BitmapDrawable center_drawable = new BitmapDrawable(activity.getResources(),center_bmp);
//change here setTileModeY to setTileModeX if you want to repear in X
center_drawable.setTileModeY(Shader.TileMode.REPEAT);
return center_drawable;
}
Far too late for you, but may be useful for others.
You can create a custom View in order to do this. Simply scale your source bitmap to be as high as your view and then draw it repeatedly on the canvas:
public class RepeatingXImageView extends View {
Bitmap bitmap;
Paint paint;
public RepeatingXImageView(Context context) {
super(context);
}
public RepeatingXImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RepeatingXImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if(changed) {
paint = new Paint();
bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.seekbar_overlay);
bitmap = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth(), bottom - top, false);
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(bitmap == null) return;
int left = 0;
while(left < getWidth()) {
canvas.drawBitmap(bitmap, left, 0, paint);
left += bitmap.getWidth();
}
}
}
Having the same problem myself. Tried using 9patch image but it seems you cannot use 9patch images along with tiling, the 9patch stretches the image no matter what you define in the tiling property.
At the end what i will do is ask the creator of the image to stretch it vertically or do it myself. I would love a better solution if anyone finds one.
Related
I am doing a work on SVG Paths and Image. I have loaded SVG file and get an image and try to set thi image on canvas . But canvas is not showing image. I check the height and width and null check of this image/picture and it is not null so i am unable to understand that why canvas is not showing image. any help
My code:
public class MainActivity extends Activity{
Context c;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
c=getApplicationContext();
setContentView(new GameView(this));
}
public class GameView extends View{
private int width, height;
private long svgId;
Picture picture;
long startTime;
float scaleFactor;
public GameView(Context context) {
super(context);
SVG svg = SVGParser.getSVGFromResource(getResources(),R.raw.android);
picture = svg.getPicture();
}
#Override
protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
// get visible area
width = right - left;
height = bottom - top;
}
#Override
public void onDraw(Canvas canvas) {
// paint a white background...
canvas.drawColor(Color.BLACK);
if (canvas!=null)
{
Toast.makeText(c, "yahooooooooooooooooo"+picture.getHeight(), Toast.LENGTH_LONG).show();
scaleFactor=Math.min((float)getHeight()/picture.getHeight(),(float)getWidth()/picture.getWidth());
canvas.scale((float)scaleFactor,(float)scaleFactor);
canvas.drawPicture(picture);
}
}
}
}
There is a known issue with svg-android that with certain images, the hardware acceleration can actually cause the image to not show up. I'm not sure of the exact circumstances of what causes this, but I do know it happened to me, as I documented in this question. The key is to disable hardware acceleration for the view. Depending on what your target it, you might not need to do something quite as fancy as I did (Android 3.0+ doesn't need to check the build version), but here's the key part. Add the code I included in the constructor to your constructor, then add the disableHardwareAcceleration bit
public GameView(Context context,AttributeSet attrs) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
{
disableHardwareAcceleration();
}
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void disableHardwareAcceleration()
{
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
This is what I use to return a drawable object that I will use through all my app
It works quite well.
final BitmapDrawable getSVG_Drawable
(int svgResourceID, int width, int height)
{
// Get a Picture from the SVG in res/raw.
final SVG vector =
SVGParser.getSVGFromResource(getResources(), svgResourceID);
final DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
// Redraw the picture to a new size.
final Bitmap bmp =
Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
final Canvas cnv = new Canvas(bmp);
cnv.setDensity((int) (metrics.xdpi));
cnv.drawPicture(vector.getPicture(), new Rect(0, 0, width, height));
final BitmapDrawable drw = new BitmapDrawable(getResources(), bmp);
// Return the drawable.
return drw;
}
I make custom view with complex background, that consists of two elements: at the top - bitmap drawable and bellow it 9-path drawable.
My code is:
public class MyCustomFrame extends FrameLayout
{
Drawable main, top;
public RequestInfoBottomContainer(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
main = res.getDrawable(R.drawable.bg_main);
top = res.getDrawable(R.drawable.bg_top);
}
#Override
protected void onDraw(Canvas canvas)
{
int width = getMeasuredWidth();
int height = canvas.getHeight();
top.setBounds(0, 0, width, top.getIntrinsicHeight());
main.setBounds(0, top.getIntrinsicHeight(), width, height);
top.draw(canvas);
main.draw(canvas);
}
}
For example, if top drawable height is 10 px, width is 500 px, height 200 px and I set bounds 0, 10, 500, 200 to main drawable Android draws it 0, 0, 500, 200 bounds. I.e. main draws over top.
What I do wrong?
I make small work around, but it is no good.
Bitmap bmp = Bitmap.createBitmap(width, height - top.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
main.setBounds(0, 0, width, bmp.getHeight());
main.draw(new Canvas(bmp));
canvas.drawBitmap(bmp, 0, top.getIntrinsicHeight(), null);
bmp.recycle();
I'm a beginner/intermediate java-student and I just started working with android 2D programming. I'm working in surfaceView and want to set a backgroud, but the problem is I dont know how to make the background fill 100% of the screen, this is how I draw my background:
public GFXSurface(Context context) {
super(context);
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.roadtest);
getHolder().addCallback(this);
mThread = new ViewThread(this);
}
public void doDraw(Canvas canvas){
canvas.drawBitmap(mBitmap, 0, 0, null);
}
Something like that should do the trick, place it after obtaining your bitmap
float scale = (float)mBitmap.getHeight()/(float)getHeight();
int newWidth = Math.round(mBitmap.getWidth()/scale);
int newHeight = Math.round(mBitmap.getHeight()/scale);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(mBitmap, newWidth, newHeight, true);
then draw it like you already do :)
I have a class derived from ImageView:
public class TouchView extends ImageView
{
#Override
protected void onDraw(Canvas canvas)
...
The touchview is created only once in the activity's onCreate and populated with a drawable from a SVG file.
ImageView imageView = new TouchView(this);
imageView.setScaleType(ImageView.ScaleType.MATRIX);
FrameLayout f = (FrameLayout)findViewById(R.id.frame2);
FrameLayout.LayoutParams l = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.FILL_PARENT, FrameLayout.LayoutParams.FILL_PARENT);
f.addView(imageView, l);
...
is = openFileInput(svgname);
svg = SVGParser.getSVGFromInputStream(is);
is.close();
Drawable d = svg.createPictureDrawable();
imageView.setImageDrawable(d);
All the environment remains the same all the time. Yet in the onDraw method I'm getting canvas with different sizes between the events. That is the code:
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
Log.v("DRAW", " w= " + canvas.getWidth() + " h=" + canvas.getHeight());
...
}
produces logs with lines, where width and height of the canvas change back and forth from normal 1024*728 (this is the correct view dimension on the tablet) to 200*160 (strange thing introducing bugs in my drawings). I'm embarrassed.
Should the canvas be always of the same size for the same view/drawable? The documentation says that getWidth and getHeight methods return dimensions of the current drawing layer, but it's not clear what is the layer, how many of them is created for the canvas "behind the scene" and how to control this process.
I'd appreciate any explanation on how to get consistent drawing behaviuor, specifically by getting actual size of the view being painted in onDraw.
Currently I'm using a workround with a call to the view's getDrawingRect, but I'm not sure it is a proper way, because it seems that the canvas parameter of onDraw should be all-sufficient for drawing sizing.
I had the same issue, here is how I got this fixed, hope it helps you
protected void onDraw(Canvas c) {
super.onDraw(c);
int w = getWidth(), h = getHeight();
// resize
Matrix resize = new Matrix();
resize.postScale((float)Math.min(w, h) / (float)mMarker.getWidth(), (float)Math.min(w, h) / (float)mMarker.getHeight());
imageScaled = Bitmap.createBitmap(mMarker, 0, 0, mMarker.getWidth(), mMarker.getHeight(), resize, false);
c.drawBitmap(imageScaled, 0,0, paint);
}
Where mMarker is defined in the custom ImageView constructor.
...
private Bitmap mMarker, imageScaled;
Paint paint = new Paint();
//Java constructor
public AvatarImageView(Context context) {
super(context);
init();
}
//XML constructor
public AvatarImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
// load the image only once
mMarker = BitmapFactory.decodeResource(getResources(), R.drawable.silhouette_48);
mMarker.setHasAlpha(true);paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(4);
//invalidate(); // don't know if I need this
}
I think I'm a bit confused about how to use custom views. I'm following along with slides from a talk given by Eric Burke from Square (from this year's anddevcon, slides here: http://www.andevcon.com/AndevCon_II/downloadpresentation.aspx?aid=Taming_Android__User_Experience_Lessons_from_Square_pdf.zip&sid=2).
His code, or at least the part he showed in the slides, went something like this:
public class EditablePhoto extends View {
private Bitmap framedPhoto;
private Bitmap image;
private Drawable placeholder;
public EditablePhoto(Context context) {
super(context);
}
#Override protected void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
int measuredWidth = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
int measuredHeight = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
//ensure view always square
int min = Math.min(measuredHeight, measuredWidth);
setMeasuredDimension(min, min);
}
#Override
protected void onDraw(Canvas canvas) {
if(placeholder == null && image==null) return;
if(framedPhoto == null) {
createFramedPhoto(Math.min(getWidth(), getHeight()));
}
canvas.drawBitmap(framedPhoto, 0, 0, null);
}
private void createFramedPhoto(int size) {
Drawable imageDrawable = (image!=null)
? new BitmapDrawable(image) : placeholder;
Bitmap output = Bitmap.createBitmap(size, size,
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
RectF outerRect = new RectF(0, 0, size, size);
float outerRadius = size / 18f;
//Red rectangle
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.RED);
canvas.drawRoundRect(outerRect, outerRadius, outerRadius, paint);
paint.setXfermode(new PorterDuffXfermode(
PorterDuff.Mode.SRC_IN));
imageDrawable.setBounds(0, 0, size, size);
canvas.saveLayer(outerRect, paint, Canvas.ALL_SAVE_FLAG);
imageDrawable.draw(canvas);
canvas.restore();
}
}
What I don't get is how to actually use this View now.... Where and when do you set the bitmaps, which are private fields in this class...?
Generally confused and would love some enlightenment.
More than one year passed, but I hope this will help anyone who looking for the right answer. In my case, I putted this line of code
framedPhoto = output;
as the last one in createFramedPhoto() method. It works.
In the example, the author created a rounded rectangle as background then he draw the bitmap on it with XOR mode, so all pixel outside the rounded rectangle will be trim off.
OnDraw() is the method where you will Draw your view on canvas. here too you can analyze onDraw() will fisrt call CreateFramePhoto then draw this Bitmap on canvas .
You can add this customView in layout Either from xml or in Java Class
1) Through Xml :
<EditablePhoto android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
..........................
/>
dont forgate to add constructor EditablePhoto(Context context, AttributeSet attributeSet) for this case
2) through Java class :
EditablePhoto editablePhoto = new EditablePhoto(this);
addView(editablePhoto) // or do anthing you want with this