I want to create Hexagon shape for my project so I want to create that shape in .xml format so how can i create.
The best solution for you would be use VectorDrawable:
Hexagon shape as vector drawable:
<vector android:height="24dp" android:viewportHeight="628.0"
android:viewportWidth="726.0" android:width="27dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#00ffffff"
android:pathData="m723,314c-60,103.9 -120,207.8 -180,311.8 -120,0 -240,0 -360,0C123,521.8 63,417.9 3,314 63,210.1 123,106.2 183,2.2c120,0 240,0 360,0C603,106.2 663,210.1 723,314Z"
android:strokeColor="#000000" android:strokeWidth="4"/>
</vector>
Update(28:07.2016):
To support API below Lollipop use support library http://android-developers.blogspot.com/2016/02/android-support-library-232.html remember to use VectorDrawableCompat instead VectorDrawable
if you want output like this
if you want full hexagon
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="512dp"
android:height="512dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:fillColor="#000000"
android:pathData="m485.291,129.408 l-224,-128c-3.285,-1.877 -7.296,-1.877
-10.581,0l-224,128c-3.328,1.899 -5.376,5.44 -5.376,9.259v234.667c0,3.819 2.048,7.36
5.376,9.259l224,128c1.643,0.939 3.456,1.408 5.291,1.408s3.648,-0.469
5.291,-1.408l224,-128c3.328,-1.899 5.376,-5.44 5.376,-9.259v-234.667c-0.001,-3.819
-2.049,-7.36 -5.377,-9.259z" />
</vector>
While most solutions would involve including the ShapeImageView (which is a great library, btw), you can always write your own logic to create a custom hexagon shaped layout.
All you need to do is define the properties of the Path object and then use that in the onDraw() method of the layout using the Canvas.
This is how you would create the hexagon path.
float midx = getWidth() / 2;
float midy = getHeight() / 2;
Path p = new Path();
p.moveTo(midx, midy);
p.lineTo(midx+150, midy + 220);
p.lineTo(midx, midy + 220);
p.lineTo(midx-150, midy + 220);
p.lineTo(midx-300, midy);
p.lineTo(midx-150, midy-220);
p.lineTo(midx+150, midy-220);
p.lineTo(midx+300, midy);
p.lineTo(midx+150, midy + 220);
return p;
Now, in your custom hexagon layout, use this path in onDraw().
#Override
protected void onDraw(Canvas canvas) {
Path clipPath = new Path();
clipPath.addPath(p); //p is the path you created above
canvas.clipPath(clipPath);
canvas.drawColor(Color.RED); //optional
super.onDraw(canvas)
}
Once you have your custom layout ready, you can set the background of the layout to any drawable you want (just as you would do for any other layouts).
You can use VectorDrawable (VectorDrawableCompat for old version) https://developer.android.com/studio/write/vector-asset-studio.html. You can easialy import shape from .svg files.
Related
I am using this image:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="39dp"
android:viewportWidth="32"
android:viewportHeight="39">
<path
android:pathData="M15.8575,6.5831L15.8575,1.0031C15.8575,0.1031 14.7775,-0.3369 14.1575,0.3031L6.5575,7.8831C6.1575,8.2831 6.1575,8.9031 6.5575,9.3031L14.1375,16.8831C14.7775,17.5031 15.8575,17.0631 15.8575,16.1631L15.8575,10.5831C23.3175,10.5831 29.2175,17.4231 27.5775,25.1631C26.6375,29.7031 22.9575,33.3631 18.4375,34.3031C11.2975,35.8031 4.9375,30.9031 3.9775,24.2831C3.8375,23.3231 2.9975,22.5831 2.0175,22.5831C0.8175,22.5831 -0.1425,23.6431 0.0175,24.8431C1.2575,33.6231 9.6175,40.1231 19.0775,38.2831C25.3175,37.0631 30.3375,32.0431 31.5575,25.8031C33.5375,15.5431 25.7375,6.5831 15.8575,6.5831Z"
android:strokeWidth="1"
android:fillColor="#fff"
android:fillType="evenOdd"
android:strokeColor="#00000000"/>
<path
android:pathData="M-34,-30h100v100h-100z"
android:strokeWidth="1"
android:fillType="evenOdd"
android:strokeColor="#00000000"/>
</vector>
as drawableStart in a button
android:drawableStart="#drawable/ic_restart"
I am using focus navigation, so in onFocusChanged of the button I am enlarging it with this extension:
fun View.enlarge(turnOn: Boolean) {
if (turnOn) {
translationZ = 1f
scaleX = 1.2f
scaleY = 1.2f
} else {
translationZ = 0f
scaleX = 1.0f
scaleY = 1.0f
}
}
It works very well, however the drawable is pixelated after I enlarge it even though it is xml. The image loses its quality. What is causing this and how can I avoid it and keed the image looking good?
EDIT: tried different approach according to answer below, but was not successful
override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
if (gainFocus) {
imageView.layoutParams.height = 60
imageView.layoutParams.width = 60
} else {
imageView.layoutParams.height = 50
imageView.layoutParams.width = 50
}
}
or
override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
if (gainFocus) {
imageView.drawable.setBounds(0,0,60,60)
} else {
imageView.drawable.setBounds(0,0,50,50)
}
}
None of these two methods are working. This time the icon is pixelated when made smaller.
What is still wrong with VectorDrawable?
Using Scale X/Y to enlarge your views make them lose quality, instead increase the width and height, since you're using a vector drawable it would scale properly with it, to do this in code, use the layout param of the view... but note that the drawable of a button does not scale with the button so you should try something else like an image button or a linear layout with the drawable beside the button.
Is there a wat to make a indicator like this?
it has some pointed arrow down like in the selected item?
The only solution I could find is to grab the source code of the original TabLayout and customize it, according your needs.
In fact, all you need to do to get this custom pointing arrow is to override SlidingTabStrip's void draw(Canvas canvas) method. Unfortunately, SlidingTabStrip is private inner class inside TabLayout.
Luckily, all support library code is open, so we can create our own TabLayoutWithArrow class. I replaced the standard void draw(Canvas canvas) by this one to draw the arrow:
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
// i used <dimen name="pointing_arrow_size">3dp</dimen>
int arrowSize = getResources().getDimensionPixelSize(R.dimen.pointing_arrow_size);
if (mIndicatorLeft >= 0 && mIndicatorRight > mIndicatorLeft) {
canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight - arrowSize,
mIndicatorRight, getHeight() - arrowSize, mSelectedIndicatorPaint);
canvas.drawPath(getTrianglePath(arrowSize), mSelectedIndicatorPaint);
}
}
private Path getTrianglePath(int arrowSize) {
mSelectedIndicatorPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mSelectedIndicatorPaint.setAntiAlias(true);
int leftPointX = mIndicatorLeft + (mIndicatorRight - mIndicatorLeft) / 2 - arrowSize*2;
int rightPointX = leftPointX + arrowSize*2;
int bottomPointX = leftPointX + arrowSize;
int leftPointY = getHeight() - arrowSize;
int bottomPointY = getHeight();
Point left = new Point(leftPointX, leftPointY);
Point right = new Point(rightPointX, leftPointY);
Point bottom = new Point(bottomPointX, bottomPointY);
Path path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
path.setLastPoint(left.x, left.y);
path.lineTo(right.x, right.y);
path.lineTo(bottom.x, bottom.y);
path.lineTo(left.x, left.y);
path.close();
return path;
}
Of course, the background, the particular design of the indicator can be improved/adjust according your needs.
To make my custom TabLayoutWithArrow, I had to copy these files into my project:
AnimationUtils
TabLayout
ThemeUtils
ValueAnimatorCompat
ValueAnimatorCompatImplEclairMr1
ValueAnimatorCompatImplHoneycombMr1
ViewUtils
ViewUtilsLollipop
To have transparency behind the arrow, you just need to set this Shape-drawable, as a background for the TabLayoutWithArrow :
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:bottom="#dimen/pointing_arrow_size">
<shape android:shape="rectangle" >
<solid android:color="#FFFF00" />
</shape>
</item>
<item android:height="#dimen/pointing_arrow_size"
android:gravity="bottom">
<shape android:shape="rectangle" >
<solid android:color="#00000000" />
</shape>
</item>
</layer-list>
And the actual usage is:
<klogi.com.viewpagerwithdifferentmenu.CustomTabLayout.TabLayoutWithArrow
android:id="#+id/tabLayout"
android:background="#drawable/tab_layout_background"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
I've uploaded the whole project (the TabLayoutWithArrow + one-page app which is using it) to my dropbox - feel free to check it out.
I hope, it helps
now it doesn't work, tintmanager class is removed from support library 23.2.0 , I managed same functionality by changing background drawable at runtime inside for loop detecting clicked position , PS : check this question and answer I am using same library : https://github.com/astuetz/PagerSlidingTabStrip/issues/141
Here is the code for anyone requiring an upward triangle using #Konstantin Loginov code
private Path getTrianglePath(int arrowSize) {
mSelectedIndicatorPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mSelectedIndicatorPaint.setAntiAlias(true);
mSelectedIndicatorPaint.setColor(Color.WHITE);
int leftPointX = mIndicatorLeft + (mIndicatorRight - mIndicatorLeft) / 2 - arrowSize * 1 / 2;
int mTopX = leftPointX + arrowSize;
int mTopY = getHeight() - arrowSize;
int rightPointX = leftPointX + arrowSize * 2;
int leftPointY = getHeight();
Point left = new Point(leftPointX, leftPointY);
Point right = new Point(rightPointX, leftPointY);
Point bottom = new Point(mTopX, mTopY);
Path path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
path.setLastPoint(left.x, left.y);
path.lineTo(right.x, right.y);
path.lineTo(bottom.x, bottom.y);
path.lineTo(left.x, left.y);
path.close();
return path;
}
Is there a wat to make a indicator like this?
it has some pointed arrow down like in the selected item?
The only solution I could find is to grab the source code of the original TabLayout and customize it, according your needs.
In fact, all you need to do to get this custom pointing arrow is to override SlidingTabStrip's void draw(Canvas canvas) method. Unfortunately, SlidingTabStrip is private inner class inside TabLayout.
Luckily, all support library code is open, so we can create our own TabLayoutWithArrow class. I replaced the standard void draw(Canvas canvas) by this one to draw the arrow:
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
// i used <dimen name="pointing_arrow_size">3dp</dimen>
int arrowSize = getResources().getDimensionPixelSize(R.dimen.pointing_arrow_size);
if (mIndicatorLeft >= 0 && mIndicatorRight > mIndicatorLeft) {
canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight - arrowSize,
mIndicatorRight, getHeight() - arrowSize, mSelectedIndicatorPaint);
canvas.drawPath(getTrianglePath(arrowSize), mSelectedIndicatorPaint);
}
}
private Path getTrianglePath(int arrowSize) {
mSelectedIndicatorPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mSelectedIndicatorPaint.setAntiAlias(true);
int leftPointX = mIndicatorLeft + (mIndicatorRight - mIndicatorLeft) / 2 - arrowSize*2;
int rightPointX = leftPointX + arrowSize*2;
int bottomPointX = leftPointX + arrowSize;
int leftPointY = getHeight() - arrowSize;
int bottomPointY = getHeight();
Point left = new Point(leftPointX, leftPointY);
Point right = new Point(rightPointX, leftPointY);
Point bottom = new Point(bottomPointX, bottomPointY);
Path path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
path.setLastPoint(left.x, left.y);
path.lineTo(right.x, right.y);
path.lineTo(bottom.x, bottom.y);
path.lineTo(left.x, left.y);
path.close();
return path;
}
Of course, the background, the particular design of the indicator can be improved/adjust according your needs.
To make my custom TabLayoutWithArrow, I had to copy these files into my project:
AnimationUtils
TabLayout
ThemeUtils
ValueAnimatorCompat
ValueAnimatorCompatImplEclairMr1
ValueAnimatorCompatImplHoneycombMr1
ViewUtils
ViewUtilsLollipop
To have transparency behind the arrow, you just need to set this Shape-drawable, as a background for the TabLayoutWithArrow :
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:bottom="#dimen/pointing_arrow_size">
<shape android:shape="rectangle" >
<solid android:color="#FFFF00" />
</shape>
</item>
<item android:height="#dimen/pointing_arrow_size"
android:gravity="bottom">
<shape android:shape="rectangle" >
<solid android:color="#00000000" />
</shape>
</item>
</layer-list>
And the actual usage is:
<klogi.com.viewpagerwithdifferentmenu.CustomTabLayout.TabLayoutWithArrow
android:id="#+id/tabLayout"
android:background="#drawable/tab_layout_background"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
I've uploaded the whole project (the TabLayoutWithArrow + one-page app which is using it) to my dropbox - feel free to check it out.
I hope, it helps
now it doesn't work, tintmanager class is removed from support library 23.2.0 , I managed same functionality by changing background drawable at runtime inside for loop detecting clicked position , PS : check this question and answer I am using same library : https://github.com/astuetz/PagerSlidingTabStrip/issues/141
Here is the code for anyone requiring an upward triangle using #Konstantin Loginov code
private Path getTrianglePath(int arrowSize) {
mSelectedIndicatorPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mSelectedIndicatorPaint.setAntiAlias(true);
mSelectedIndicatorPaint.setColor(Color.WHITE);
int leftPointX = mIndicatorLeft + (mIndicatorRight - mIndicatorLeft) / 2 - arrowSize * 1 / 2;
int mTopX = leftPointX + arrowSize;
int mTopY = getHeight() - arrowSize;
int rightPointX = leftPointX + arrowSize * 2;
int leftPointY = getHeight();
Point left = new Point(leftPointX, leftPointY);
Point right = new Point(rightPointX, leftPointY);
Point bottom = new Point(mTopX, mTopY);
Path path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
path.setLastPoint(left.x, left.y);
path.lineTo(right.x, right.y);
path.lineTo(bottom.x, bottom.y);
path.lineTo(left.x, left.y);
path.close();
return path;
}
Have a look at my code below.
ShapeDrawable shapeDrawable = new ShapeDrawable(new RectShape());
shapeDrawable.getPaint().setColor(Color.parseColor("#5a2705"));
shapeDrawable.getPaint().setStyle(Style.STROKE);
shapeDrawable.getPaint().setAntiAlias(true);
shapeDrawable.getPaint().setStrokeWidth(2);
shapeDrawable.getPaint().setPathEffect(new CornerPathEffect(10));
I am applying this as background to my LinearLayout, but the edges are not smooth. How can I fix this?
Here is the screenshot of how it looks.
Using a programmatically-created shape drawable as a View background results in the outer half of your stroke width getting cropped off (for reasons I don't know). Look closely at your image and you'll see that your stroke is only 1 pixel wide, even though you requested 2. That is why the corners look ugly. This effect will be much more apparent if you try a bigger stroke and radius such as 10 and 40, respectively.
Either use an XML drawable, which doesn't seem to have this problem, like in Harshit Jain's answer, or do the following if you must (or prefer to) use a programmatic solution.
Solution: Use a layer list to inset the rectangle by the amount that is being clipped (half the stroke width), like this:
float strokeWidth = 2;
ShapeDrawable shapeDrawable = new ShapeDrawable(new RectShape());
shapeDrawable.getPaint().setColor(Color.parseColor("#5a2705"));
shapeDrawable.getPaint().setStyle(Style.STROKE);
shapeDrawable.getPaint().setAntiAlias(true);
shapeDrawable.getPaint().setStrokeWidth(strokeWidth);
shapeDrawable.getPaint().setPathEffect(new CornerPathEffect(10));
Drawable[] layers = {shapeDrawable};
LayerDrawable layerDrawable = new LayerDrawable(layers);
int halfStrokeWidth = (int)(strokeWidth/2);
layerDrawable.setLayerInset(0, halfStrokeWidth, halfStrokeWidth, halfStrokeWidth, halfStrokeWidth);
Then use the layerDrawable as your background. Here is a screen shot of the result of the above code:
You can try creating a separate xml file with a layout of the rounded rectangle. Such as:
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="color_here"/>
<stroke android:width="5dp" android:color="color_here"/>
<corners android:radius="2dp"/>
</shape>
You can tune this to your liking and use this XML file as a background in your main XML.
You can also try using 9Patch which should already come with your SDK
To cater such background requirement I prefer using 9-patch drawable.
Below are the pointer to get going with creating and using 9-patch drawable resources:
Developer Doc: http://developer.android.com/tools/help/draw9patch.html
Another explanation: http://radleymarx.com/blog/simple-guide-to-9-patch/
Online tool to create 9-patch images. (Note: Modify the file extension to *.9.png while saving the image)
You might want to consider checking this out.
Rounded Image Views by Vince
I have solved this issue in a very simple way,
I was looking for a code which can be done programmatically and not via xml,
Here is the code:
GradientDrawable shape = new GradientDrawable();
shape.setCornerRadius(10);
shape.setColor(Color.WHITE);
shape.setStroke(2, Color.parseColor("#996633"));
You can set this like this : view.setBackgroundDrawable(shape);
The answer posted by #Tenfour04 might also solve the issue but i havent tried as such.
Hope it helps someone.
You may try using selector like this . This solution is the best you just need to set this selector to the background of the layout.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#color/white" />
<stroke android:width="1dp" android:color="#color/grey" />
<padding android:left="10dp" android:top="10dp" android:right="10dp" android:bottom="10dp" />
<corners android:radius="15dp" />
<gradient
android:angle="90"
android:startColor="#color/white"
android:centerColor="#color/grey"
android:endColor="#color/white"
android:type="linear" />
</shape>
Choose color of your own.
Cheers!
I've encountered this problem recently and came up with a different solution. IMHO the best solution is to create your own Shape implementation and use it to create a ShapeDrawable.
Below is a simple implementation of rounded rectangle, that will allow you to inset it's border.
class InsetRoundRectShape(
private var radiusArray: FloatArray = floatArrayOf(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f),
private var inset: RectF = RectF()
): RectShape() {
private var innerRect: RectF = RectF()
private var path: Path = Path()
constructor(radius: Float, inset: RectF): this(floatArrayOf(radius, radius, radius, radius, radius, radius, radius, radius), inset)
constructor(radius: Float, inset: Float): this(radius, RectF(inset, inset, inset, inset))
init {
if (radiusArray.size < 8) {
throw ArrayIndexOutOfBoundsException("radius array must have >= 8 values")
}
}
override fun draw(canvas: Canvas, paint: Paint) {
canvas.drawPath(path, paint)
}
override fun getOutline(outline: Outline) {
super.getOutline(outline)
val radius = radiusArray[0]
if(radiusArray.any { it != radius }) {
outline.setConvexPath(path)
return
}
val r = rect()
outline.setRoundRect(ceil(r.left).toInt(), ceil(r.top).toInt(), floor(r.right).toInt(), floor(r.bottom).toInt(), radius)
}
override fun onResize(w: Float, h: Float) {
super.onResize(w, h)
val r = rect()
path.reset()
innerRect.set(r.left + inset.left, r.top + inset.top, r.right - inset.right, r.bottom - inset.bottom)
if(innerRect.width() <= w && innerRect.height() <= h) {
path.addRoundRect(innerRect, radiusArray, Path.Direction.CCW)
}
}
override fun clone(): InsetRoundRectShape {
val shape = super.clone() as InsetRoundRectShape
shape.radiusArray = radiusArray.clone()
shape.inset = RectF(inset)
shape.path = Path(path)
return shape
}
}
Create ShapeDrawable like this
//Inset has to be half of strokeWidth
ShapeDrawable(InsetRoundRectShape(10f, 4f)).apply {
this.paint.color = Color.BLUE
this.paint.style = Paint.Style.STROKE
this.paint.strokeWidth = 8.dp
this.invalidateSelf()
}
I'm trying to create an AndEngine HUD that will sit on top of a TMXTiledMap (bear with me, I'm very new to AndEngine). To keep things simple at first, I have a simple rectangle created via Android drawables. The idea is that this will sit at the bottom center of the screen and never move, even as the map underneath it is moved in various directions. For now, all I want to do is get that rectangle to show up. Later on I'll add other functionality to the rectangle.
My drawable is created like this:
?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<corners
android:radius="7dp" />
<gradient
android:startColor="#343434"
android:endColor="#17171717"
android:angle="270"
android:useLevel="false"
android:type="linear" />
<padding
android:left="10dp"
android:top="10dp"
android:right="10dp"
android:bottom="10dp" />
</shape>
And I have it pulled into a layout like this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container"
android:layout_width="300dip"
android:layout_height="100dip"
android:orientation="horizontal"
android:layout_marginLeft="20dp"
android:background="#drawable/menu_bkgrnd" >
</LinearLayout>
And then finally, here is where I try to pull it in as a HUD:
rect = new HUD();
ITouchArea container = (ITouchArea) findViewById(R.id.container);
this.rect.registerTouchArea(container);
rect.attachChild((IEntity) container);
As you can see, I'm doing a lot of casting to satisfy AndEngine, but when I run this, the map is totally screwed up. Am I going about this correctly? Is my casting incorrect? (or maybe both!).
Thanks for any help.
EDIT:
Based on the code that Jong and 正宗白布鞋 suggested below, I've adjusted my Java code as follows:
this.atlas = new BitmapTextureAtlas(null, 256, 256, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
this.atlas.load();
ITextureRegion drawable = BitmapTextureAtlasTextureRegionFactory.createFromResource(atlas, getApplicationContext(), R.drawable.myDrawable, 0, 0);
rect.attachChild(new Sprite(0, 0, drawable, this.getVertexBufferObjectManager()));
At this point, I'm still just trying to get this to appear on the screen. I'll adjust size and location later.
Everything compiles and runs without error, however my screen is just a total mess.
As you can see, I had to make a couple of small adjustments to the constructor arguments to get AndEngine to accept my instantiations. Not sure if I'm doing this correctly.
The other issue that I see in this code is that it appears to me that this code is just going to place an inactive shape on my screen. I know that in my original post, I said that my immediate goal is to make this rectangle show up, but I think that it has to show up as a registered touch area since it will ultimately be something with controls on it that need to respond to user commands. Sorry if I overly minimized what I am trying to do.
I'm still not getting this. Anybody have any suggestions? Thanks again!
You can't cast LinearLayout to ITouchArea, ITouchArea is an interface implemented by AndEngine classes only.
Like 正宗白布鞋 suggested, you should use the createFromResource method of BitmapTextureAtlasTextureRegionFactory.
You can use this code:
BitmapTextureAtlas atlas = new BitmapTextureAtlas(sizeX, sizeY, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
TextureRegion drawable = BitmapTextureAtlasTextureRegionFactory.createFromResource(atlas, getApplicationContext(), R.drawable.drawableId, 0, 0);
rect.attachChild(new Sprite(x, y, drawable));
EDIT:
If you want your sprite to respond to touch events, you can register it as a touch area in your rect HUD.
Sprite sprite = new Sprite(x, y, drawable, getVertexBufferObjectManager()) {
#Override
public boolean onAreaTouched(final TouchEvent pTouchEvent, final float pX, final float pY) {
//Do what you want here
}
}
rect.registerTouchArea(sprite);
rect.attachChild(sprite);
I had the same problem several days ago, and I couldn't find a proper answer. So now that I've gathered information from different sources, here is how I dealt with this.
AFAIK, it appears that the method createFromResource(atlas, getApplicationContext(), R.drawable.drawableId, 0, 0) from BitmapTextureAtlasTextureRegionFactory cannot deal with XML drawable (such as <shape>...</shape>).
Indeed, when I was only displaying a colored rectangle, everything was fine - and I think with a PNG file too -, but when I tried to create a Sprite from my XML drawable, I always got a NullBitmapException when the drawing happened.
Steps I took:
Convert drawable resource to Drawable
Convert Drawable to Bitmap (Source here)
Convert Bitmap to TextureRegion (This is where I used this)
Create a Sprite from the TextureRegion (or two for selected/unselected, like here)
Attach the Sprite to the game's HUD
So, with code:
Convert drawable resource TextureRegion
/**
* Takes a drawable, and converts it to a TextureRegion.
* #param context
* #param resId the drawable ID
* #param textureSizeX width for our TextureRegion
* #param textureSizeY height for our TextureRegion
* #return a TextureRegion from the drawable
*/
public static TextureRegion textureRegionFromDrawable(BaseGameActivity context, int resId, int textureSizeX, int textureSizeY) {
Drawable drawable = context.getResources().getDrawable(resId);
Bitmap bitmap = drawableToBitmap(drawable);
BitmapTextureAtlasTextureSource source = new BitmapTextureAtlasTextureSource(bitmap);
BitmapTextureAtlas textureAtlas = new BitmapTextureAtlas(context.getTextureManager(), textureSizeX, textureSizeY);
textureAtlas.addTextureAtlasSource(source, 0, 0);
textureAtlas.load();
TextureRegion textureRegion = (TextureRegion) TextureRegionFactory.createFromSource(textureAtlas, source, 0, 0);
return textureRegion;
}
// Drawable to Bitmap
public static Bitmap drawableToBitmap(Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
The utility intermediary class
public class BitmapTextureAtlasTextureSource extends BaseTextureAtlasSource implements IBitmapTextureAtlasSource {
private final int[] mColors;
public BitmapTextureAtlasTextureSource(Bitmap pBitmap) {
super(0, 0, pBitmap.getWidth(), pBitmap.getHeight());
mColors = new int[mTextureWidth * mTextureHeight];
for (int y = 0; y < mTextureHeight; ++y) {
for (int x = 0; x < mTextureWidth; ++x) {
mColors[x + y * mTextureWidth] = pBitmap.getPixel(x, y);
}
}
}
#Override
public Bitmap onLoadBitmap(Config pBitmapConfig) {
return Bitmap.createBitmap(mColors, mTextureWidth, mTextureHeight, Bitmap.Config.ARGB_8888);
}
#Override
public IBitmapTextureAtlasSource deepCopy() {
return new BitmapTextureAtlasTextureSource(Bitmap.createBitmap(mColors, mTextureWidth, mTextureHeight, Bitmap.Config.ARGB_8888));
}
#Override
public Bitmap onLoadBitmap(Config pBitmapConfig, boolean pMutable) {
// TODO Auto-generated method stub
return null;
}
}
Create two Sprite from the TextureRegions (I wanted 2 states for my button: selected/unselected), and attach them to the HUD
int textureSizeX = 512, textureSizeY = 512;
TextureRegion textureDefault = AndEngineUtils.textureRegionFromDrawable(activity, R.drawable.custom_menu_button, textureSizeX, textureSizeY);
TextureRegion textureSelected = AndEngineUtils.textureRegionFromDrawable(activity, R.drawable.custom_menu_button_selected, textureSizeX, textureSizeY);
mGameHUD = new HUD();
// 2 sprites for selected and unselected
final Sprite buttonUnselected, buttonSelected ;
buttonSelected = new Sprite(0, 0, textureSelected, activity.getVertexBufferObjectManager());
buttonSelected.setVisible(false);
buttonUnselected = new Sprite(0, 0, textureDefault, activity.getVertexBufferObjectManager()) {
#Override
public boolean onAreaTouched(TouchEvent pSceneTouchEvent, float pTouchAreaLocalX, float pTouchAreaLocalY) {
switch (pSceneTouchEvent.getAction()) {
case TouchEvent.ACTION_DOWN:
// Change button
buttonSelected.setVisible(true);
this.setVisible(false);
break;
case TouchEvent.ACTION_UP:
// reset button
this.setVisible(true);
buttonSelected.setVisible(false);
break;
default:
break;
}
return super.onAreaTouched(pSceneTouchEvent, pTouchAreaLocalX, pTouchAreaLocalY);
}
};
mGameHUD.attachChild(buttonSelected);
mGameHUD.attachChild(buttonUnselected);
mGameHUD.registerTouchArea(buttonUnselected);
camera.setHUD(mGameHUD);
We can also make use of scene.setOnAreaTouchListener(touchListener) in order for an "unwanted" sliding gesture - until outside of the button - to take effet to reset the button color (see this thread).
I have just begun using AndEngine and I am not yet familiar with best practices, so feel free to correct me.